如何在不包含周围文本的情况下解析确切数据?

时间:2016-11-08 20:17:24

标签: python regex extract

我的代码非常接近成功,但我只需要一些帮助。

我有100页的数据页面,但在将其应用于其他页面之前,我正在完美地解析1页。在这一页,这是一封电子邮件,我需要提取几件事:日期,部门,鱼类,英镑和金钱。到目前为止,我已成功使用RegularExpressions识别某些单词并从该行提取数据:例如查找"已发送"因为我知道日期信息将始终遵循,并寻找" Pounds"或" lbs"因为Pounds信息总是在那之前。

我遇到的问题是我的代码正在抓取数据所在的整行,而不仅仅是数字数据。例如,我想抓住Pounds的数字值,但我意识到这将非常困难,因为100个电子邮件中的每一个都措辞不同。我不确定是否有可能使这个代码变得简单,因为我需要RegEx识别数据周围的文本,但不包括在我的导出命令中。那么我是否只是盲目地抓住某些被认可的词语后的角色?

以下是我用于提取Pounds数据的代码:

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

理想情况下,我只想要输出5,000磅的数值,但我不确定如何抓住这个数字。

以下是我需要解析的原始电子邮件文本:

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

赞赏任何和所有的帮助,以及提出批评的问题。谢谢。

2 个答案:

答案 0 :(得分: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\))

基本上(?:)是一个未被捕获的匹配组,因此正则表达式只捕获中间括号内的组。

如果您提供所需的确切周围文本,我可以更具体。

修改

关掉你的新例子我可以看到所有例子之间唯一的相似之处就是你有一个数字(成千上万,所以它有一个,),后跟一些空格,然后是{{ 1}}。所以你的正则表达式看起来像:

lbs

这将返回数字本身的匹配。您可以看到它正在运行的示例here。由于忽略了不是数千的值(即不包含(?:(\d+,\d+)\s+lbs) ),此正则表达式将排除较小的值。

编辑2:

此外,我还要指出,这可以在没有使用str.split()的正则表达式的情况下完全完成。您可以只使用您想要的数字是,之前的单词,即lbs位于lbs的位置,而不是尝试查找特定的单词模式。号码位于i位置。

您必须面对的唯一其他考虑因素是如何处理多个值,两个明显的值是:

  1. 最大的价值。
  2. 第一个价值。
  3. 以下两种情况如何与原始代码一起使用:

    i-1

    有一点需要注意的是,代码并没有处理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之前的值不是数字,则正则表达式或拆分都不起作用。如果您遇到这个问题,我建议您搜索数据以查找有问题的电子邮件 - 如果数量足够小,可以手动编辑。

答案 1 :(得分: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)

如果需要,您可以更加彻底地使用正则表达式。

希望这有帮助!

<强>更新

这里的主要部分是正则表达式(\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,.]+)是被捕获的东西,所以基本上是数字部分。

带有?:的括号定义了非捕获组。

此正则表达式将匹配:

  • 2,890磅(捕获&#39; 2,890&#39;)
  • 3.6磅(捕获&#39; 3.6&#39;)
  • 5678829磅
  • 23磅
  • 9,894Pounds

以及不需要的东西:

  • 2 ..... lbs
  • 3,4,6,7,8磅

不匹配:

  • 7423
  • 23m lbs
  • 45 ppounds
  • 2.8磅

根据您拥有的内容的复杂程度,您可以制作更复杂的正则表达式。我认为这个正则表达式足以满足您的目的。

希望这有助于澄清