Parsing “From” addresses from email text

前端 未结 8 2082
春和景丽
春和景丽 2021-02-19 03:56

I\'m trying to extract email addresses from plain text transcripts of emails. I\'ve cobbled together a bit of code to find the addresses themselves, but I don\'t know how to mak

相关标签:
8条回答
  • 2021-02-19 04:29

    If your goal is actually to extract email addresses from text, you should use a library built for that purpose. Regular expressions are not well suited to match arbitrary email addresses.

    But if you're doing this as an exercise to understand regular expressions better, I'd take the approach of expanding the expression you're using to include the extra text you want to match. So first, let me explain what that regex does:

    [\w\-][\w\-\.]+@[\w\-][\w\-\.]+[a-zA-Z]{1,4}
    
    • [\w\-] matches any "word" character (letter, number, or underscore), or a hyphen
    • [\w\-\.]+ matches (any word character or hyphen or period) one or more times
    • @ matches a literal '@'
    • [\w\-] matches any word character or hyphen
    • [\w\-\.]+ matches (any word character or hyphen or period) one or more times
    • [a-zA-Z]{1,4} matches 1, 2, 3, or 4 lowercase or uppercase letters

    So this matches a sequence of a "word" that may contain hyphens or periods but doesn't start with a period, followed by an @ sign, followed by another "word" (same sense as before) that ends with a letter.

    Now, to modify this for your purposes, let's add regex parts to match "From", the name, and the angle brackets:

    From: [\w\s]+?<([\w\-][\w\-\.]+@[\w\-][\w\-\.]+[a-zA-Z]{1,4})>
    
    • From: matches the literal text "From: "
    • [\w\s]+? matches one or more consecutive word characters or space characters. The question mark makes the match non-greedy, so it will match as few characters as possible while still allowing the whole regular expression to match (in this case, it's probably not necessary, but it does make the match more efficient since the thing that comes immediately afterwards is not a word character or space character).
    • < matches a literal less-than sign (opening angle bracket)
    • The same regular expression you had before is now surrounded by parentheses. This makes it a capturing group, so you can call m.group(1) to get the text matched by that part of the regex.
    • > matches a literal greater-than sign

    Since the regex now uses capturing groups, your code will need to change a little as well:

    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
    

    The code [m.group(1) for m in mailsrch.finditer(line)] produces a list out of the first capturing group (remember, that was the part in parentheses) from each match found by the regular expression.

    0 讨论(0)
  • 2021-02-19 04:29

    Roughly speaking, you can:

    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
    

    This utilizes the built-in python parseaddr function to parse the address out of the from line (as demonstrated by other answers), without the overhead necessarily of parsing the entire message (e.g. by using the more full featured email and mailbox packages). The script here simply skips any lines that do not begin with "From:". Whether the overhead matters to you depends on how big your input is and how often you will be doing this operation.

    0 讨论(0)
  • 2021-02-19 04:35

    Use the email and mailbox packages to parse the plain text version of the email. This will convert it to an object that will enable to extract all the addresses in the 'From' field.

    You can also do a lot of other analysis on the message, if you need to process other header fields, or the message body.

    As a quick example, the following (untested) code should read all the message in a unix style mailbox, and print all the 'from' headers.

    import mailbox
    import email
    
    mbox = mailbox.PortableUnixMailbox(open(filename, 'rU'), email.message_from_file)
    
    for msg in mbox:
       from = msg['From']
       print from
    
    0 讨论(0)
  • 2021-02-19 04:36

    Try this out:

    >>> 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')
    

    Unfortunately it only finds the first email in each line because it's expecting header lines, but maybe that's ok?

    0 讨论(0)
  • 2021-02-19 04:41
    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'])
    
    0 讨论(0)
  • 2021-02-19 04:46

    if you can be reasonably sure that lines containing these email addresses start with whitespace followed by "From:" you can simply do this:

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

    then later - or on adding them to the list - you can refine the addresslines items to give out exactly what you want

    0 讨论(0)
提交回复
热议问题