2011-10-08 271 views
2

我是python/django的新手,我試圖從我的刮板中獲得更有效的信息。目前,該刮板會將漫畫書名稱列表並將其正確劃分爲三個部分(發佈日期,原始日期和標題)的CSV列表。然後我將當前日期和標題傳遞到我的數據庫的不同部分,我在我的Loader腳本中執行(將mm/dd/yy轉換爲yyyy-mm-dd,保存到「pub_date」列,標題轉到「標題」柱)。Python上的不規則字符串解析

相同的字符串可以是這樣的:

10/12/11|10/12/11|Stan Lee's Traveler #12 (10 Copy Incentive Cover) 

我成功地抓住了日期,但標題是棘手。在這種情況下,我希望在第二個「|」之後填寫三個不同的列。標題應該轉到「標題」,一個charfield。數字12(在'#'之後)應該進入DecimalField「issue_num」,並且'()'之間的所有內容都應該進入「特殊」字符域。我不知道如何做這種嚴格的解析。有時,有多個#(一個漫畫尤其被描述爲一個包,「Containing issues#90-#95」),並且有幾個具有多個「()」組(例如,「背叛的猿人行星#1(4)(25副本激勵封面) )

什麼是一條好的道路來開始解決這個問題?我對If/else語句的瞭解迅速分裂爲更復雜的行。我怎樣纔能有效地(如果可能)pythonic-ly解析通過這些線和細分它們,以便我可以將它們插入我的數據庫中的正確位置?

+1

你所描述的是*不*嚴格的解析。你需要定義你的輸出是什麼。例如,給定「包含問題#90-#95」,應該在'issue_num'中做些什麼?字符串「包含問題」是否顯示在「標題」中?那麼多個'()'組呢? ......無論如何,這種特殊的「智能解析」往往會陷入瘋狂。尋找其他方式獲取信息,或準備一次令人沮喪的體驗。 –

+0

對於「#90-#95」的一個實例,我打算給它一個空值的問題編號,而是給它一個卷號。對於多個組,()的第二組總是包含我想要的部分,所以(理論上)我會考慮任何有兩組括號的問題,並要求它只查看第二個,刪除(或至少不參考)其餘部分。 – Alxjrvs

回答

1

使用regular expression模塊re。舉例來說,如果你有一個變量s您的樣品記錄的第三| -delimited場,那麼你可以做

match = re.match(r"^(?P<title>[^#]*) #(?P<num>[0-9]+) \((?P<special>.*)\)$", s) 
title = match.groups('title') 
issue = match.groups('num') 
special = match.groups('special') 

你會得到在最後三行的IndexError的缺失場。調整RE直到它解析你想要的一切。

1

解析標題是最困難的部分,這聽起來像你可以處理的日期等自己。問題是,有沒有那麼可以分析每一個冠軍,但也有很多規則,你只能猜測哪一個工作在一個特定的標題一個規則。

我通常通過創建規則列表處理這個問題,從最特殊到一般,並通過一個直到一個匹配嘗試出來的。

寫這樣的規則可以使用re模塊甚至pyparsing

總的想法是這樣的:

class CantParse(Exception): 
    pass 

# one rule to parse one kind of title 
import re 
def title_with_special(title): 
    """ accepts only a title of the form 
    <text> #<issue> (<special>) """ 
    m = re.match(r"[^#]*#(\d+) \(([^)]+)\)", title) 
    if m: 
     return m.group(1), m.group(2) 
    else: 
     raise CantParse(title) 


def parse_extra(title, rules): 
    """ tries to parse extra information from a title using the rules """ 
    for rule in rules: 
     try: 
      return rule(title) 
     except CantParse: 
      pass 

    # nothing matched 
    raise CantParse(title) 


# lets try this out 
rules = [title_with_special] # list of rules to apply, add more functions here 
titles = ["Stan Lee's Traveler #12 (10 Copy Incentive Cover)", 
      "Betrayal Of The Planet Of The Apes #1 (Of 4)(25 Copy Incentive Cover))"] 

for title in titles: 
    try: 
     issue, special = parse_extra(title, rules) 
     print "Parsed", title, "to issue=%s special='%s'" % (issue, special) 
    except CantParse: 
     print "No matching rule for", title 

正如你所看到的第一個標題是正確分析,而不是第二。您必須編寫一系列規則來說明數據中所有可能的標題格式。

+0

謝謝!這讓我能夠確定問題編號/特殊問題,但是如何繼續將它們記錄在循環中? book.issue_num = ?? – Alxjrvs

+0

@Alxjrvs:解析所有標題格式還有很多工作要做。規則函數只是返回'(issue_num,special)'對,所以你使用它。 –

+0

謝謝。我有點失落 - 我怎麼會在腳本中調用它?我不確定持有問題編號和特殊價值的是什麼。 – Alxjrvs

1

正則表達式是要走的路。但是,如果你不舒服地填寫它們,你可以嘗試我寫的一個小解析器(https://github.com/hgrecco/stringparser)。它將字符串格式(PEP 3101)轉換爲正則表達式。在你的情況下,你會做以下事情:

>>> from stringparser import Parser 
>>> p = Parser(r"{date:s}\|{date2:s}\|{title:s}#{issue:d} \({special:s}\)") 
>>> x = p("10/12/11|10/12/11|Stan Lee's Traveler #12 (10 Copy Incentive Cover)") 
OrderedDict([('date', '10/12/11'), ('date2', '10/12/11'), ('title', "Stan Lee's Traveler "), ('issue', 12), ('special', '10 Copy Incentive Cover')]) 
>>> x.issue 
12 

這種情況下的輸出是一個(有序)字典。這將適用於任何簡單的情況下,你可能會調整它捕捉多個問題或多個()

還有一件事:注意,在當前版本中,你需要手動轉義正則表達式字符(即如果你想查找|,你需要輸入\ |)。我打算很快改變這一點。