【问题标题】:Parsing "From" addresses from email text从电子邮件文本中解析“发件人”地址
【发布时间】:2010-10-07 16:37:34
【问题描述】:

我正在尝试从电子邮件的纯文本副本中提取电子邮件地址。 我拼凑了一些代码来自己查找地址,但我不知道如何区分它们;现在它只是吐出文件中的所有电子邮件地址。我想让它只吐出以“发件人:”和几个通配符开头并以“>”结尾的地址(因为电子邮件设置为发件人 [name] )。

现在是代码:

import re #allows program to use regular expressions
foundemail = []
#this is an empty list

mailsrch = re.compile(r'[\w\-][\w\-\.]+@[\w\-][\w\-\.]+[a-zA-Z]{1,4}')
 #do not currently know exact meaning of this expression but assuming
 #it means something like "[stuff]@[stuff][stuff1-4 letters]"

        # "line" is a variable is set to a single line read from the file
# ("text.txt"):
for line in open("text.txt"):

    foundemail.extend(mailsrch.findall(line))

    # this extends the previously named list via the "mailsrch" variable
      #which was named before

print foundemail

【问题讨论】:

    标签: python string email parsing text


    【解决方案1】:

    试试这个:

    >>> from email.utils import parseaddr
    
    >>> parseaddr('From: vg@m.com')
    ('', 'vg@m.com')
    
    >>> parseaddr('From: Van Gale <vg@m.com>')
    ('Van Gale', 'vg@m.com')
    
    >>> parseaddr('    From: Van Gale <vg@m.com>   ')
    ('Van Gale', 'vg@m.com')
    
    >>> parseaddr('blah abdf    From: Van Gale <vg@m.com>   and this')
    ('Van Gale', 'vg@m.com')
    

    不幸的是,它只在每一行中找到第一封电子邮件,因为它需要标题行,但也许没关系?

    【讨论】:

    • parseaddr 实际上只是 AddressList(addr).addresslist[0] 所以你可以通过 AddressList(addr).addresslist 来获取它们
    【解决方案2】:
    import email
    msg = email.message_from_string(str)
    
    # or
    # f = open(file)
    # msg = email.message_from_file(f)
    
    msg['from']
    
    # and optionally
    from email.utils import parseaddr
    addr = parseaddr(msg['from'])
    

    【讨论】:

      【解决方案3】:

      如果您的目标实际上是从文本中提取电子邮件地址,您应该使用为此目的构建的库。正则表达式不太适合匹配任意电子邮件地址。

      但是,如果您这样做是为了更好地理解正则表达式,我会采取扩展您正在使用的表达式以包含您想要匹配的额外文本的方法。所以首先,让我解释一下这个正则表达式的作用:

      [\w\-][\w\-\.]+@[\w\-][\w\-\.]+[a-zA-Z]{1,4}
      
      • [\w\-] 匹配任何“单词”字符(字母、数字或下划线),连字符
      • [\w\-\.]+ 匹配(任何单词字符连字符句点)一次或多次
      • @ 匹配文字“@”
      • [\w\-] 匹配任何单词字符连字符
      • [\w\-\.]+ 匹配(任何单词字符连字符句点)一次或多次
      • [a-zA-Z]{1,4} 匹配 1、2、3 或 4 个小写或大写字母

      所以这匹配一个“单词”序列,该序列可能包含连字符或句点,但不以句点开头,后跟一个@ 符号,然后是另一个结束的“单词”(与之前的含义相同)用一封信。

      现在,为了根据您的目的修改它,让我们添加正则表达式部分以匹配“From”、名称和尖括号:

      From: [\w\s]+?<([\w\-][\w\-\.]+@[\w\-][\w\-\.]+[a-zA-Z]{1,4})>
      
      • From: 匹配文字文本“From:”
      • [\w\s]+? 匹配一个或多个连续的单词字符个空格字符。问号使匹配不贪婪,因此它将匹配尽可能少的字符,同时仍然允许整个正则表达式匹配(在这种情况下,可能没有必要,但它确实使匹配更有效,因为紧随其后的不是单词字符或空格字符)。
      • &lt; 匹配文字小于号(左尖括号)
      • 您之前使用的相同正则表达式现在被括号括起来。这使它成为一个捕获组,因此您可以调用m.group(1) 来获取与该部分正则表达式匹配的文本。
      • &gt; 匹配文字大于号

      由于正则表达式现在使用捕获组,因此您的代码也需要稍作更改:

      import re
      foundemail = []
      
      mailsrch = re.compile(r'From: [\w\s]+?<([\w\-][\w\-\.]+@[\w\-][\w\-\.]+[a-zA-Z]{1,4})>')
      
      for line in open("text.txt"):
          foundemail.extend([m.group(1) for m in mailsrch.finditer(line)])
      
      print foundemail
      

      代码[m.group(1) for m in mailsrch.finditer(line)] 根据正则表达式找到的每个匹配项从第一个捕获组(请记住,括号中的部分)生成一个列表。

      【讨论】:

      • 嘿,看起来像是在我还在打字的时候发布的。我们得出了大致相同的最终结果,但您实际上仍然可以使用 mailsrc.findall() - 如果只有一个组,则它会返回该组的匹配列表,因此无需列表理解。
      • 这是完美的,谢谢!我也会试试杰的,这样我就可以得到几个不同的版本。也感谢您的广泛解释。
      • 有趣,我不知道...函数的执行方式有点奇怪:?
      • 我建议更改“[\w\s]+?”到“[\w\s\.,]+?”在您更改的正则表达式的开头。点和逗号很容易出现在电子邮件地址中
      • Alex,你是对的,但是从我对 Cal 的问题的阅读来看,电子邮件设置为 From: [name] 听起来他只是在解析伪电子邮件地址。完整的 RFC822 电子邮件地址需要一个完整的(非基于正则表达式的)解析器,例如 email.utils.parseaddr。
      【解决方案4】:
      mailsrch = re.compile(r'[\w\-][\w\-\.]+@[\w\-][\w\-\.]+[a-zA-Z]{1,4}')
      

      表达式分解:

      [\w-]:任何单词字符(字母数字,加下划线)或破折号

      [\w-.]+:任何单词字符、破折号或句点/点,一次或多次

      @:文字@符号

      [\w-][\w-.]+:任何单词 char 或 dash,后跟任何单词 char、dash 或句点一次或多次。

      [a-zA-Z]{1,4}:任意字母字符 1-4 次。

      只匹配以From: 开头并用 符号包裹的行:

      import re
      
      foundemail = []
      mailsrch = re.compile(r'^From:\s+.*<([\w\-][\w\-\.]+@[\w\-][\w\-\.]+[a-zA-Z]{1,4})>', re.I | re.M)
      foundemail.extend(mailsrch.findall(open('text.txt').read()))
      
      print foundemail
      

      【讨论】:

        【解决方案5】:

        使用电子邮件和邮箱包来解析电子邮件的纯文本版本。这会将其转换为能够提取“发件人”字段中的所有地址的对象。

        如果你需要处理其他的头字段,或者消息体,你还可以对消息做很多其他的分析。

        作为一个简单的示例,以下(未经测试的)代码应读取 unix 样式邮箱中的所有邮件,并打印所有“发件人”标头。

        import mailbox
        import email
        
        mbox = mailbox.PortableUnixMailbox(open(filename, 'rU'), email.message_from_file)
        
        for msg in mbox:
           from = msg['From']
           print from
        

        【讨论】:

          【解决方案6】:

          粗略地说,您可以:

          from email.utils import parseaddr
          
          foundemail = []
          for line in open("text.txt"):
              if not line.startswith("From:"): continue
              n, e = parseaddr(line)
              foundemail.append(e)
          print foundemail
          

          这利用内置的 python parseaddr 函数从行外解析地址(如其他答案所示),而无需解析整个消息的开销(例如,通过使用功能更全的电子邮件和邮箱包)。此处的脚本只是跳过任何不以“From:”开头的行。开销对您来说是否重要取决于您的输入有多大以及您执行此操作的频率。

          【讨论】:

            【解决方案7】:

            如果您可以合理地确定包含这些电子邮件地址的行以空格开头,后跟“发件人:”,您可以这样做:

            addresslines = []
            for line in open("text.txt"):
                if line.strip().startswith("From:"):
                    addresslines.append(line)
            

            然后稍后 - 或将它们添加到列表中 - 您可以优化地址行项目以准确给出您想要的内容

            【讨论】:

            • 嗯,我一定是错误地实现了这个......它要么保持空白,要么显示我之前得到的整个列表。
            【解决方案8】:

            "[stuff]@[stuff][stuff1-4 letters]" 差不多,但如果你愿意,你可以使用我刚刚发现的技巧here 来解码正则表达式。在交互式 Python 会话中执行 compile(),如下所示:

            mailsrch = re.compile(r'[\w\-][\w\-\.]+@[\w\-][\w\-\.]+[a-zA-Z]{1,4}', 128)
            

            它将打印出以下内容:

            in 
              category category_word
              literal 45
            max_repeat 1 65535 
              in 
                category category_word
                literal 45
                literal 46
            literal 64 
            in 
              category category_word
              literal 45
            max_repeat 1 65535 
              in 
                category category_word
                literal 45
                literal 46
            max_repeat 1 4 
              in 
                range (97, 122)
                range (65, 90)
            

            如果你能习惯的话,它会向你展示 RE 的工作原理。

            【讨论】:

            • 没有完全回答这个问题,但这是一个巧妙的技巧......感谢您指出:-)
            猜你喜欢
            • 2014-04-09
            • 2011-01-07
            • 2015-06-27
            • 1970-01-01
            • 1970-01-01
            • 2016-01-21
            • 2017-07-21
            • 2012-05-25
            • 1970-01-01
            相关资源
            最近更新 更多