2016-11-08 57 views
0

我的代碼非常接近成功,但我只需要一點幫助。如何在不包含周圍文本的情況下解析精確數據?

我有100頁的數據,但我正在解析只有1頁完美,然後才應用到其他人。在這一頁,這是一封電子郵件,我需要提取幾件事情:日期,部門,魚種,磅和金錢。到目前爲止,我已經成功地使用RegularExpressions來識別某些單詞並從該行中提取數據:例如查找「已發送」,因爲我知道日期信息將始終跟着該單詞,並且查找「磅」或「磅」因爲磅信息將永遠在此之前。

我遇到的問題是我的代碼抓住了數據所在的整行,而不僅僅是數字數據。例如,我想抓住磅的數字值,但我意識到這將是非常困難的,因爲100個電子郵件中的每一個都有不同的措辭。我不確定是否甚至有可能使此代碼萬無一失,因爲我需要RegEx識別數據周圍的文本,但不會將其包含在我的導出命令中。那麼,我是否會盲目地抓住某些被認可的單詞後的角色?

這是用於提取英鎊數據一塊我的代碼:

for filename in os.listdir(path): 
    file_path = os.path.join(path, filename) 
    if os.path.isfile(file_path): 
     with open(file_path, 'r') as f: 
      sector_result = [] 
      pattern = re.compile("Pounds | lbs", re.IGNORECASE) 
      for linenum, line in enumerate(f): 
      if pattern.search(line) != None: 
       sector_result.append((linenum, line.rstrip('\n'))) 
       for linenum, line in sector_result: 
        print ("Pounds:", line) 

而且這裏是它打印出:

Pounds: -GOM Cod up to 5,000 lbs (live wt) @ 1.40 lbs 
Pounds: -GOM Cod up to 5,000 lbs (live wt) @ 1.40 lbs 
Pounds: -American Plaice 2,000 lbs  .60 lbs or best offer 

理想我只是想在5000磅數值被出口,但我不知道我會如何去抓住這個數字。

這裏是我需要解析原始電子郵件文本:

From: 
Sent: Friday, November 15, 2013 2:43pm 
To: 

Subject: NEFS 11 fish for lease 

Greetings, 

NEFS 11 has the following fish for lease: 

-GOM Cod up to 5,000 lbs (live wt) @ 1.40 lbs 
-American Plaice 2,000 lbs  .60 lbs or best offer 

這裏是另一個單獨的電子郵件儘管這將需要進行解析;這就是爲什麼寫這個代碼是困難的,因爲它也必須處理各種不同的措詞電子郵件,因爲它們都通過不同的人寫的:

From: 
Sent: Monday, December 09, 2013 1:13pm 
To: 

Subject: NEFS 6 Stocks for lease October 28 2013 

Hi All, 

The following is available from NEFS VI: 

4,000 lbs. GBE COD (live wt) 

10,000 lbs. SNE Winter Flounder 

10,000 lbs. SNE Yellowtail 

10,000 lbs GB Winter Flounder 

Will lease for cash or trade for GOM YT, GOM Cod, Dabs, Grey sole stocks on equitable basis. 

Please forward all offers. 

Thank you, 

And here is another image of data that can be found in the emails...I can handle parsing the written txt in the body of emails, I can handle parsing the attached PDFs, but I am completely lost with how to handle these. So any ideas anyone has I'm all ears

任何和所有幫助表示讚賞,因爲以及提出批評的問題。謝謝。

+1

請提供您嘗試解析的源數據的示例或代碼片段。 – Keozon

+0

我已添加原始電子郵件。使整個項目變得困難的原因是,每封電子郵件都不會是這樣,因爲它們都是由不同的人寫的。 – Stephen

回答

1

正則表達式可以識別和不是圍繞一個值導出文本,這被稱爲非捕獲組。例如:

Pounds: -GOM Cod up to 5,000 lbs (live wt) @ 1.40 lbs

要認識到,up to,你想要的值,並(live wt)你可以寫這樣的正則表達式:

(?: up to).(\d+,\d+.lbs).(?:\(live wt\)) 

本質(?:)是沒有拍攝的匹配組,所以正則表達式只能捕獲中間的括號內的組。

如果您提供所需的確切周邊文字,我可以更具體。

編輯:

去了你的新的例子,我可以看到,所有的例子之間的唯一相似的是,你有一個數字(在成千上萬所以它有一個,),其次是空白的某些量,然後是lbs。所以你的正則表達式如下:

(?:(\d+,\d+)\s+lbs) 

這將返回數字本身的匹配。你可以看到它的一個例子here。這個正則表達式將排除較小的值,這是因爲忽略了不是數千的值(即不包含,)。

編輯2:

而且我想,我想指出的是,這可以完全不使用正則表達式str.split()來完成。您可以使用這樣一個事實,即您想要的數字將是lbs之前的單詞,即如果lbs位於位置i,那麼您的號碼位於位置i-1,而不是嘗試查找特定的單詞模式。

你要面對的唯一其他要考慮的是如何處理多個值,這兩個明顯的是:

  1. 最大的價值。
  2. 第一值。

下面是這兩種情況下會與你原來的代碼工作:

def max_pounds(line): 
    pound_values = {} 
    words = line.split() 
    for i, word in enumerate(words): 
     if word.lower() == 'lbs': 
      # Convert the number into an float 
      # And save the original string representation. 
      pound_values[(float(words[i-1].replace(',','')))] = words[i-1] 
    # Print the biggest numerical number. 
    print(pound_values[max(pound_values.keys())]) 

def first_pounds(line): 
    words = line.split() 
    for i, word in enumerate(words): 
     if word.lower() == 'lbs': 
      # print the number and exit. 
      print(words[i-1]) 
      return 

for filename in os.listdir(path): 
    file_path = os.path.join(path, filename) 
    if os.path.isfile(file_path): 
     with open(file_path, 'r') as f: 
      sector_result = [] 
      pattern = re.compile("Pounds | lbs", re.IGNORECASE) 
      for linenum, line in enumerate(f): 
      if pattern.search(line) != None: 
       sector_result.append((linenum, line.rstrip('\n'))) 
       for linenum, line in sector_result: 
        print ("Pounds:", line) 
        # Only one function is required. 
        max_pounds(line) 
        first_pounts(line) 

一個需要注意的是,代碼不處理的邊緣情況下lbs是第一個字,但是這是很容易處理與try-catch

如果lbs之前的值不是數字,則正則表達式或分割都不起作用。如果你遇到這個問題,我會建議你搜索你的數據以獲取有問題的電子郵件 - 如果數量足夠小,可以手動編輯它們。

+0

這非常合理。如果每封電子郵件都被相同的單詞包圍,那麼這種技術將很好地工作,但正如您在上面我編輯的兩個示例中可以看到的,每個電子郵件都是不同的。有些(如上)在數字值後面只有「lbs」。如果是這種情況,是否有辦法識別「lbs」,然後在它之前直接捕獲該數字?這會是一個盲目的搶奪,可能會比想要的4000更多還是更少? – Stephen

+0

我已經更新了我的答案,專門捕獲'lbs'和前一個數字,而不管數字和'lbs'之間的空白大小。讓我知道你是否需要更復雜的東西! – Darkstarone

+0

我還添加了一個非正則表達式方法,以防您感覺更舒適。 – Darkstarone

1

這裏有足夠的正則表達式靈活:

for filename in os.listdir(path): 
    file_path = os.path.join(path, filename) 
    if os.path.isfile(file_path): 
     with open(file_path, 'r') as f: 
      pattern = r'(\d[\d,.]+)\s*(?:lbs|[Pp]ounds)' 
      content = f.read() 

      ### if you want only the first match ### 
      match = re.search(pattern, content) 
      if match: 
       print(match.group(1)) 

      ### if you want all the matches ### 
      matches = re.findall(pattern, content) 
      if matches: 
       print(matches) 

如果需要,您可以更透徹與正則表達式。

希望這會有所幫助!

UPDATE

主要部分在這裏是正則表達式(\d[\d,.]+)\s*(?:lbs|[Pp]ounds)。這是一個基本的,解釋如下:

(      
    \d     -> Start with any digit character 
    [\d,.]+   -> Followed by either other digits or commas or dots 
)      
\s*     -> Followed by zero or more spaces 
(?:      
    lbs|[Pp]ounds  -> Followed by either 'lbs' or 'Pounds' or 'pounds' 
)      

的括號定義捕獲組,所以(\d[\d,.]+)是所捕獲的東西,所以基本上數字部分。

帶有?:的括號定義了一個非捕獲組。

此正則表達式匹配:

  • 2890磅(捕獲 '2890')
  • 3.6磅(捕獲 '3.6')
  • 5678829磅
  • 23磅
  • 9,894Pounds
  • etc

除了不需要的東西,如:

  • 2 .....磅
  • 1,3,4,6,7,8-磅

它不會匹配:

  • 23米磅
  • 45 ppounds
  • 2.8英鎊

根據您擁有的內容的複雜程度,您可以製作更爲複雜的正則表達式。我會認爲這個正則表達式對於你的目的來說足夠好。

希望這有助於澄清

+0

所以,你能簡單地向我解釋你的代碼,所以我明白了......正如你已經寫了它,它會識別Pounds這個詞,我可以看到它,但那麼它會捕獲什麼? txt文件中單詞Pounds的第一個(或全部)出現?或者它會在Pounds這個詞之前還是之後捕獲(並且只是)數字值?在這種情況下,5,000或2,000 .... – Stephen

+0

用更多的解釋更新了答案。希望它有幫助 – damores

+0

這是一個了不起的澄清,非常感謝你,RegEx一直對我來說超級複雜,但你已經幫了很大忙。我試着用你的代碼,它幾乎是完美的......當我運行用於查找所有匹配(re.findall代碼)的代碼時,它捕獲了5000和2000磅的值,但它也捕獲還有1.40和0.60美元的金額......是否有排除這些?或者更確切地說,一種只抓取關鍵詞Pounds旁邊的數字字符的方法? – Stephen

相關問題