2016-11-08 56 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

相关问题