【问题标题】:regex - python extracting email header names正则表达式 - python 提取电子邮件标题名称
【发布时间】:2016-06-13 12:09:30
【问题描述】:

您好,我正在寻找一种方法来从该文本块(原始来自 mbox 文件)中提取标题名称(以粗体显示的内容) 我尝试了这个适用于崇高文本正则表达式搜索但不适用于 python 的正则表达式 ^\w+-?(\w+)?-?(\w+)?:

rgex = re.findall('^\w+-?(\w+)?-?(\w+)?:', mail);

这就是邮件中的内容

X-Apparently-To:test@yahoo.com; 2016 年 6 月 9 日星期四 13:41:21 +0000
返回路径
Received-SPF:通过(yahoo.com 的域将 72.30.235.45 指定为允许的发件人)
收到:来自 127.0.0.1 (EHLO n3-vm9.bullet.mail.bf1.yahoo.com) (72.30.235.45) 由 mta1287.mail.ne1.yahoo.com 使用 SMTPS;周四,6 月 9 日
2016 13:41:21 +0000
DKIM 签名:v=1;一个=rsa-sha256; c=放松/放松; d=yahoo-inc.com; s=yibm; t=1465479679;
收件人:test@yahoo.com
来自:“雅虎”
回复:“雅虎”
X-YMailISG:PCypxycWLDvGv4Bg8ShrtzVYi3vpFMAjYaqWyWybcVJ_ZQff eyquyqb..Qu6UKhX_Tyz5b3da2iDtRStJpVnNulZHOb8GznJQTCKk9sjvboS KsbzY4E1uScWz0Ieo0jjG0YHrB1dTCzOSeMiPNumCCFS1sR3_SkyMBGG_D2D wWtdRducxLa2YgEMMubVpMtNJMBv.bwk0.E.jQNEy8I3LnJEqcDpmIUM7bZL XgkEFz7yl1Zo6Sj4r0z6pGlVIFOql7uG9Bwq2VJoK1Q1upKJUOBfQqzf64y2 9fXLnQsWENpZloxwncGzLhdzEYGgE3xNuFV8QFxZGXyvtKZFoykH49M03URN jtx8Yg6ypjyRbBIRVJGVFbjAvW6io3yeyIFh042jlgYQtLxbneFA60hn9ifT Mit3bQ5l7Tginw0OgRM2cbqLo0tEZFt9vlN597Z3vPGwsVdBcTp9wnk6orj2 TqjEpAmODy3Yru2HzDP7Dbwq9CGaIozUm91VNWqw5Dy7AMQEsuvnBop7Fflk G21m1WKMBgrS.2bOLQ4797E09LjlyyoWI9FouUNNhDljnPPf2AeKUKzauctw ULOQPveWAm4lDsNLMp5yvXDYNIe5HMor84SVd8_xF3Icna1PAftXGzJUHrXK NZSEN_VO0GprGfaNQg4uSW_0wXFXwC6TYQ4CMjz53o0qNGpILogVfRLwFCFL DtW8nimkLLsNzmDajzJsR_juA86Orw2NE5ED4qdpPxmyxyrXYOQPu3O6zeYf 7mBzU0aX7VHJUxJ4L3HDB9qTjbTaCdnySrnjGtd7u9Cn9yRJirDNeg3UA82P PeA1ZDfc0vKdrn5QI6e6YKa2TTt7Dspy3jObgSapH5epc3LyQVyN7yjpxrq_ MXAbpqedjUfcwq3c7lpt8xxUxy.MXWg0fJO059xijvb_sYTaQTGUWAMeVU.6 IW.hSksejwpn._CgE9Kqabbk5qgYIdYRW1pmz5OBYh0skCX1TrFRuxbGvDit R_wr.wbTpJGiSST.b0ZetmgN72bVvlRtmNPw1Dk.zxaacXxhGSMWupPUDLJZ OMrap2ax8oiQrxT3jIhk8seIkaNJ.tGUhlPx6G4lJJaz0g89LmjBaEjGUG8P W3Phh9db3hjxUIX5UC0jg5ai2XZ7u_wXn2Muk61N1eRCZ0oA2S25YDPK1dh。 3VQ6pH8SSBxVkQHUJXbZUNqLAzi5V5wRS7oeitXERGgA2DiZB268.rJxS7di OMT5eGoITG4LnAo1M3nsVQ6xceHDd4v6KD9KfBgTHX_iLUv_skCv4dVUgVvj edKOFiOMHBTpJ9J9BECjTTzEUpc.fCNUcRwSsiSkqbRhUsAdCbxQZir3Nb1Z 6FzI6J2eNqpj4azjmDeI15R8MyN7VFc6bl6pCZySk2Tx5SQESDm.sVkADSVR pI2nuscEjU3xo_qGUxbh5mbAA17K2zYpcFXaOce8_9Eszos5pURCcdtBYUqI I_DOtvNe.zWY1ShRcr9ZzTj3ibmc7NBmvumhVMjqirb12mfJ6oxHv8d86gze HtAJmJghczUg5otSzdxSgEJJxjMZrzSidJ9FP.gPiPWtuukz82YpZ32MnCVs 6.V2DRxpUmZa31KH93QSEzwMlCn3FFTLBv9izcjoFP81yeAn.3QloF8XIC3K WmtXtloyeGjuygAhlkd_prXmmGGC5JmPlY8xu4k1NavkdDh6pG6zIkt83Wsd p.D.0BgM
X-Originating-IP:[75.30.245.45]
身份验证结果:mta1287.mail.ne1.yahoo.com from=yahoo-inc.com; domainkeys=neutral(无签名);来自=yahoo-inc.com; dkim=pass (ok)

【问题讨论】:

  • 是否有理由不只使用email 包来解析标头(从而获得标头名称)?
  • donkopotamus- 是的,就我而言,它必须来自某个文件
  • Wiktor Stribiżew- 谢谢你这样做:)
  • @user1731805 你可以只读取文件并将文本传递给email.Parser(它将读取 rfc822 样式消息)

标签: python regex


【解决方案1】:

比设计适当的正则表达式更简单的方法可能是使用 python 附带的更合适的工具...email.parser 模块,该模块旨在解析诸如此类的 rcf822 消息。

>>> from email import parser
>>> txt = """X-Apparently-To: test@yahoo.com; Thu, 09 Jun 2016 13:41:21 +0000 
... Return-Path: 
... Received-SPF: pass (domain of yahoo.com designates 72.30.235.45 as permitted sender) 
... Received: from 127.0.0.1 (EHLO n3-vm9.bullet.mail.bf1.yahoo.com) (72.30.235.45) by mta1287.mail.ne1.yahoo.com with SMTPS; Thu, 09 Jun 2016 13:41:21 +0000
... DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yahoo-inc.com; s=yibm; t=1465479679; 
... To: test@yahoo.com 
... From: "Yahoo" 
... Reply-To: "Yahoo"
... X-YMailISG: PCypxy...
... X-Originating-IP: [75.30.245.45] 
... Authentication-Results: mta1287.mail.ne1.yahoo.com from=yahoo-inc.com; domainkeys=neutral (no sig); from=yahoo-inc.com; dkim=pass (ok)
... """
>>> msg = parser.Parser().parsestr(txt, headersonly=True)
>>> print(msg.keys())
['X-Apparently-To', 'Return-Path', 'Received-SPF', 'Received', 'DKIM-Signature', 'To', 'From', 'Reply-To' 'X-YMailISG', 'X-Originating-IP', 'Authentication-Results']

【讨论】:

  • @WiktorStribiżew 因为我不想用接下来的三个非常大的标题来污染答案......
  • :) 你至少可以截断它们,这让我有点困惑。
  • @WiktorStribiżew 这是一个更好的主意 :-)
  • @user1731805 那是因为此 pastebin 中的前三行 不是电子邮件消息的一部分 ...如果您去掉它们,它就可以很好地解析。 (前两行是 mbox 格式的一部分,另一行不确定)
【解决方案2】:

如果您在整个 mbox 文件上运行正则表达式,那么正则表达式将不起作用 - 您将不得不编写一个程序。原因是消息的正文可能包含与标头标记完全匹配的标记。

假设您只在 mbox 文件的标题部分运行正则表达式,然后查看 email RFC(第 2.2 节),那么以下正则表达式应该可以工作:

'^([^:]+):'

【讨论】:

    【解决方案3】:

    Python 提供了可以为您完成这些低级任务的电子邮件包,但如果您想通过艰苦的方式学习电子邮件标头,请参考 RFC5322(以前的 RFC822)

    在其他有意义的信息中,您可以找到标头字段的定义:

    标题字段是以字段名称开头的行,后跟 冒号 (":"),后跟字段正文,并以 CRLF 结尾。一个 字段名称必须由可打印的 US-ASCII 字符组成(即, 值介于 33 和 126 之间的字符),除了 冒号。字段主体可能由可打印的 US-ASCII 字符组成 以及空格(SP,ASCII 值 32)和水平制表符(HTAB, ASCII 值 9) 字符(统称为空白 字符,WSP)。字段主体不得包括 CR 和 LF,除非 用于“折叠”和“展开”时

    折叠稍后定义为:

    标题字段的字段主体部分可以拆分为 多线表示;这称为“折叠”。一般 规则是,只要本规范允许折叠白色 空格(不仅仅是 WSP 字符),可以在任何 WSP。

    这意味着:

    • 如果一行不以 WSP 开头(正则表达式中为\s),则行首到一列是标题名称。
    • 当一行以 WSP 开头时,它是一个续行。

    所以这个正则表达式应该足够了:'([\x21-\x7e]+?):'

    【讨论】:

      【解决方案4】:

      您的 '^\w+-?(\w+)?-?(\w+)?:' 正则表达式匹配字符串的开头 (^),然后是 1+ 个单词字符,然后是可选的 -,然后 1+ 个单词字符被捕获到可选的第 1 组中(这些是由re.findall 返回,作为每个元组中的第一项作为列表返回),后跟一个可选的连字符,再次是一个匹配 1+ 个单词字符的捕获组(可选,但仍作为元组中的第二项返回),以及一个: 最后。因为^\w 和 2 个捕获组之间的可选 -,所以它不起作用。

      如果您收到的输入符合 rfc 8222 消息样式,您应该考虑切换到上面给出的from email import parser 解决方案。

      另外,在我看来,您可以只捕获除空格和冒号之外的所有字符,直到冒号后跟空格在行首

      r"^([^\s:]+):\s"
      

      并将其与re.findallre.M 标志一起使用。

      正则表达式解释

      • ^ - 行首(re.M 使 ^ 匹配换行符后的位置)或字符串开头)
      • ([^\s:]+) - 捕获第 1 组匹配 1+ 个字符而不是空格和冒号
      • : - 冒号
      • \s - 一个空格字符。

      regex demo

      Python demo 其中re.findall 仅返回捕获的 文本:

      import re
      p = re.compile(r'^([^\s:]+):\s', re.MULTILINE)
      test_str = "X-Apparently-To: test@yahoo.com; Thu, 09 Jun 2016 13:41:21 +0000 \nReturn-Path: \nReceived-SPF: pass (domain of yahoo.com designates 72.30.235.45 as permitted sender) \nReceived: from 127.0.0.1 (EHLO n3-vm9.bullet.mail.bf1.yahoo.com) (72.30.235.45) by mta1287.mail.ne1.yahoo.com with SMTPS; Thu, 09 Jun \n2016 13:41:21 +0000 \nDKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yahoo-inc.com; s=yibm; t=1465479679; \nTo: test@yahoo.com \nFrom: \"Yahoo\" \nReply-To: \"Yahoo\" \nX-YMailISG: PCypxycWLDvGv4Bg8ShrtzVYi3vpFMAjYaqWyWybcVJ_ZQff eyquyqb..Qu6UKhX_Tyz5b3da2iDtRStJpVnNulZHOb8GznJQTCKk9sjvboS KsbzY4E1uScWz0Ieo0jjG0YHrB1dTCzOSeMiPNumCCFS1sR3_SkyMBGG_D2D wWtdRducxLa2YgEMMubVpMtNJMBv.bwk0.E.jQNEy8I3LnJEqcDpmIUM7bZL XgkEFz7yl1Zo6Sj4r0z6pGlVIFOql7uG9Bwq2VJoK1Q1upKJUOBfQqzf64y2 9fXLnQsWENpZloxwncGzLhdzEYGgE3xNuFV8QFxZGXyvtKZFoykH49M03URN jtx8Yg6ypjyRbBIRVJGVFbjAvW6io3yeyIFh042jlgYQtLxbneFA60hn9ifT Mit3bQ5l7Tginw0OgRM2cbqLo0tEZFt9vlN597Z3vPGwsVdBcTp9wnk6orj2 TqjEpAmODy3Yru2HzDP7Dbwq9CGaIozUm91VNWqw5Dy7AMQEsuvnBop7Fflk G21m1WKMBgrS.2bOLQ4797E09LjlyyoWI9FouUNNhDljnPPf2AeKUKzauctw ULOQPveWAm4lDsNLMp5yvXDYNIe5HMor84SVd8_xF3Icna1PAftXGzJUHrXK NZSEN_VO0GprGfaNQg4uSW_0wXFXwC6TYQ4CMjz53o0qNGpILogVfRLwFCFL DtW8nimkLLsNzmDajzJsR_juA86Orw2NE5ED4qdpPxmyxyrXYOQPu3O6zeYf 7mBzU0aX7VHJUxJ4L3HdB9qTjbTaCdnySrnjGtd7u9Cn9yRJirDNeg3UA82P PeA1ZDfc0vKdrn5QI6e6YKa2TTt7Dspy3jObgSapH5epc3LyQVyN7yjpxrq_ MXAbpqedjUfcwq3c7lpt8xxUxy.MXWg0fJO059xijvb_sYTaQTGUWAMeVU.6 IW.hSksejwpn._CgE9Kqabbk5qgYIdYRW1pmz5OBYh0skCX1TrFRuxbGvDit R_wr.wbTpJGiSST.b0ZetmgN72bVvlRtmNPw1Dk.zxaacXxhGSMWupPUDLJZ OMrap2ax8oiQrxT3jIhk8seIkaNJ.tGUhlPx6G4lJJaz0g89LmjBaEjGUG8P W3Phh9db3hjxUIX5UC0jg5ai2XZ7u_wXn2Muk61N1eRCZ0oA2S25YDPK1dh. 3VQ6pH8SSBxVkQHUJXbZUNqLAzi5V5wRS7oeitXERGgA2DiZB268.rJxS7di OMT5eGoITG4LnAo1M3nsVQ6xceHDd4v6KD9KfBgTHX_iLUv_skCv4dVUgVvj edKOFiOMHBTpJ9J9BECjTTzEUpc.fCNUcRwSsiSkqbRhUsAdCbxQZir3Nb1Z 6FzI6J2eNqpj4azjmDeI15R8MyN7VFc6bl6pCZySk2Tx5SQESDm.sVkADSVR pI2nuscEjU3xo_qGUxbh5mbAA17K2zYpcFXaOce8_9Eszos5pURCcdtBYUqI I_DOtvNe.zWY1ShRcr9ZzTj3ibmc7NBmvumhVMjqirb12mfJ6oxHv8d86gze HtAJmJghczUg5otSzdxSgEJJxjMZrzSidJ9FP.gPiPWtuukz82YpZ32MnCVs 6.V2DRxpUmZa31KH93QSEzwMlCn3FFTLBv9izcjoFP81yeAn.3QloF8XIC3K WmtXtloyeGjuygAhlkd_prXmMGGC5JmPlY8xu4k1NavkdDh6pG6zIkt83Wsd p.D.0BgM \nX-Originating-IP: [75.30.245.45] \nAuthentication-Results: mta1287.mail.ne1.yahoo.com from=yahoo-inc.com; domainkeys=neutral (no sig); from=yahoo-inc.com; dkim=pass (ok)"
      print(p.findall(test_str))
      

      更新

      现在,由于您要求仅获取值,因此您可以使用相同的方法,但只需在找到键时剥离键并将值添加到结果列表中:

      txt = "YOUR_STRING_HERE"
      values = []                    # Resulting value list
      start_matching = False         # Bool flag to start matching the key-value pairs
      val = ""                       # Temp string to keep multiline values
      for line in txt.split("\n"):   #  Split the input into lines
          if re.match(r"[^\s:]+:\s", line.strip()): # Check if the entry is found
              start_matching = True   # Start matching
              if val:                 # If a val is initialized, 
                  values.append(val)  #    we save it to our list
                  val = ""            # Reset the temp string value
              val += re.sub(r"^[^\s:]+:\s", "", line.strip()) # Append the value string start
          else:
              if start_matching:      # If matching has started,
                  val += "{}\n".format(line.strip()) # add the line to the value found
      print(values)
      

      IDEONE demo

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-11-24
        • 1970-01-01
        • 2011-01-16
        • 2018-12-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-02-26
        相关资源
        最近更新 更多