Schlagwort: Python

  • QooliImapEmailExtractor – Ein IMAP Email-Adressen-Extrahierer in Python

    QooliImapEmailExtractor – Ein IMAP Email-Adressen-Extrahierer in Python

    Ich wurde kürzlich angefragt, ob ich nicht ein Programm kennen würde, um aus einen Mail-Postfach alle Email-Adressen zu extrahieren. Egal, ob diese im „To“- oder „From“- oder „CC“-Feld stehen.

    Leider kannte ich kein Programm, dass dafür geeignet ist, obwohl es ein solches mit Sicherheit aber gibt. Aber bei vielen Gratis-Tools oder auch Trials kann man sich nicht sicher sein, ob es Malware enthält oder ob am Ende die Email-Adressen die extrahiert wurden auch noch gleich an den Hersteller des Programms gesendet werden.

    Warum diese Aufgabe also nicht einfach selber mit Python lösen? Im Internet wurde ich schnell fündig und fand die Basis für ein Skript, welches schon einmal grundsätzlich alle Adressen aus einem einzelnen IMAP-Inbox-Folder eines Postfachs (mittels imaplib) in eine Variable extrahiert: https://www.quora.com/Is-there-a-way-to-extract-email-addresses-from-all-emails-in-my-Gmail-account/answer/Jerry-Neumann

    Das Skript habe ich dann etwas schöner formatiert und erweitert, dass alle Folder eines Postfachs durchlaufen und die Daten dann in eine CSV-Datei exportiert werden. Das sieht dann wie folgt aus:

    import imaplib
    import email
    import csv
    import re
    
    
    list_response_pattern = re.compile(r'\((?P<flags>.*?)\) "(?P<delimiter>.*)" (?P<name>.*)')
    
    
    def parse_list_response(line):
        flags, delimiter, mailbox_name = list_response_pattern.match(line).groups()
        mailbox_name = mailbox_name.strip('"')
        return flags, delimiter, mailbox_name
    
    
    def split_addresses(s):
        # split an address list into list of tuples of (name,address)
        if not s:
            return []
        out_q = True
        cut = -1
        res = []
        for i in range(len(s)):
            if s[i] == '"':
                out_q = not out_q
            if out_q and s[i] == ',':
                res.append(email.utils.parseaddr(s[cut + 1:i]))
                cut = i
        res.append(email.utils.parseaddr(s[cut + 1:i + 1]))
        return res
    
    
    def main():
        print 'Processing started...'
        addresses = []
        mail = imaplib.IMAP4_SSL('imap.server.com')  # enter hostname eg: imap.gmail.com
        mail.login('user@server.com', 'Password')  # userid, password
        response_code_folders, mail_folder_lines = mail.list()
        for mail_folder_line in mail_folder_lines:
            flags, delimiter, mailbox_name = parse_list_response(mail_folder_line)
            print '  Extracting from folder: ' + mailbox_name
            mail.select(mailbox_name)
            result, data = mail.search(None, "ALL")
            ids = data[0].split()
            mail_count = len(ids)
            print '   Mails found in folder: ' + str(mail_count)
            if mail_count > 0:
                msgs = mail.fetch(','.join(ids), '(BODY.PEEK[HEADER])')[1][0::2]
                mail_ix = 0
                for x, msg in msgs:
                    mail_ix += 1
                    msgobj = email.message_from_string(msg)
                    print '   extracting data from message ' + str(mail_ix) + '/' + str(len(msgs))
                    addresses.extend(split_addresses(msgobj['to']))
                    msgobj = email.message_from_string(msg)
                    addresses.extend(split_addresses(msgobj['from']))
                    addresses.extend(split_addresses(msgobj['cc']))
        # only take unique addresses
        addresses = set(addresses)
        csv_filename = 'email_addresses_export.csv'
        with open(csv_filename, 'wb') as out:
            csv_out = csv.writer(out)
            csv_out.writerow(['Name', 'Address'])
            for row in addresses:
                csv_out.writerow(row)
        print ' CSV with addresses written to: ' + csv_filename
        print 'Processing finished'
    
    
    main()
    

    Wenn ihr das Skript verwenden wollt, müsst ihr Python installiert haben und euren Mailserver und die Postfach-Logindaten eintragen:

    mail = imaplib.IMAP4_SSL(‚imap.server.com‚)  # IMAP-Hostname
    mail.login(‚user@server.com‚, ‚Password‚)  # Postfach-Benutzer-ID, Passwort

  • Feeds parsen und schreiben mit Python

    Ich hatte letzthin das Problem, dass ich auf meiner neuen Fotografie-Seite einen Feed meiner letzten iStockphoto-Uploads einbinden wollte, aber dies nicht ging, da der offizielle Feed scheinbar nicht validiert:
    http://validator.w3.org/feed/check.cgi?url=http%3A%2F%2Fwww.istockphoto.com%2Fistock_myfiles_rss.php%3FID%3D8197370

    Auf WordPress verwendete ich zuerst das „Embed RSS„-Plugin um Feeds in die Seite zu integrieren, jedoch kam dies nicht mit diesem ungültigen Feed klar. Einige RSS-Reader-Programme (z.B. „Vienna RSS„) hatten jedoch keine Probleme mit dem Feed. Vermutlich korrigierten sie intern die Fehler oder ignorierten diese einfach.

    Zum Glück hat mich einmal ein guter Freund in die Welt von Python eingeführt und mir die nötigen Basics dieser tollen Programmiersprache beigebracht.

    Mit Hilfe der Bibliotheken „feedparser“ und „PyRSS2Gen“ konnte ich ein Python-Skript schreiben um dieses Problem umgehen.

    Zuerst hole ich mit feedparser den Feed ab und baue später mit PyRSS2Gen einen neuen Feed im „RSS 2.0“-Format neu auf. Abschliessend schreibe ich diesen neuen Feed als XML-Datei in das Verzeichnis des Webservers.

    Das Skript habe ich so umgebaut, dass eine beliebige iStockphoto-Benutzer-ID als Parameter übergeben werden kann. Auch die Ziel-Datei kann als Paramter übergeben werden.

    Hier das Skript, welches in einem Repository auf Github gehosted ist:

    #!/usr/bin/python
    
    import datetime
    import feedparser
    import getopt
    import os
    import PyRSS2Gen
    import sys
    
    def create_latest_uploads_feed(targetPath, feedTitle, feedUrl, feedDescription, entries):
      rssItems = []
      for entry in entries:
        rssItem = PyRSS2Gen.RSSItem(
            title = entry['title'],
            link = entry['link'],
            description = entry['description'],
            guid = PyRSS2Gen.Guid(entry['link']),
            pubDate = entry['pubDate'],
            author = entry['author']
            )
        rssItems.append(rssItem)
      rss = PyRSS2Gen.RSS2(
          title = feedTitle,
          link = feedUrl,
          description = feedDescription,
          lastBuildDate = datetime.datetime.now(),
          items = rssItems)
      print 'Writing RSS feed: ' + targetPath
      rss.write_xml(open(targetPath, 'w'))
      print 'Done!'
    
    
    def fetch_feed_and_create_rss(userId, targetPath):
      feedUrl = "http://www.istockphoto.com/istock_myfiles_rss.php?ID=" + userId
      print 'Fetching feed: ' + feedUrl
      feed = feedparser.parse(feedUrl)
      feedTitle = feed['feed']['title']
      feedDescription = ""
      entries = feed['entries']
      preparedEntries = []
      print 'Processing fetched entries. Entries found: ' + str(len(entries))
      for entry in entries:
        entryPubDate = entry['published']
        pubDate = datetime.datetime.strptime(entryPubDate[:-6], '%Y-%m-%dT%H:%M:%S')
        #pubDateClean = pubDate.strftime('%d.%m.%Y')
        author = entry['author']
        entryTitle = entry['title']
        print ' * Entry title: ' + entryTitle.encode('utf-8')
        link = entry['link']
        summaryHtml = entry['summary']
        preparedEntries.append({"pubDate":pubDate,"author":author,"title":entryTitle,
          "description":summaryHtml,"link":link})
      create_latest_uploads_feed(targetPath, feedTitle, feedUrl, feedDescription,
          preparedEntries)
    
    def main(argv):
      scriptName = os.path.basename(__file__)
      usageInfo = 'usage: ' + scriptName + ' -i  -t '
      userId = ''
      targetPath = ''
      try:
        opts, args = getopt.getopt(argv,"hi:t:",["id=","targetpath="])
      except getopt.GetoptError:
        print usageInfo
        sys.exit(2)
      for opt, arg in opts:
        if opt == '-h':
          print usageInfo
          sys.exit()
        elif opt in ("-i", "--id"):
          userId = arg
        elif opt in ("-t", "--targetpath"):
          targetPath = arg
      if (userId and targetPath):
        print 'User ID is: ' + userId
        print 'Target path is: ' + targetPath
        fetch_feed_and_create_rss(userId, targetPath)
      else:
        print usageInfo
        sys.exit()
    
    
    if __name__ == "__main__":
      main(sys.argv[1:])
    
    istock_latest_uploads.pyview rawview file on GitHub

    Genau genommen validiert der neue Feed immer noch nicht 🙂 allerdings sind die Fehler nun scheinbar weniger tragisch und er kann nun von „Embed RSS“ gelesen werden. Auch verwende ich nun zur Einbettung in WordPress „HungryFEED“ anstelle von „Embed RSS“, da dieses etwas mächtiger ist und bessere Templating-Option mitbringt.

    Damit ich für die Webseite immer einen relativ aktuellen Feed erhalte, lasse ich das Skript mittels Crontab alle vier Stunden laufen:

    #m h dom mon dow command
    0 */4 * * * python /usr/local/bin/scriptXy.py -i 8197370 -t /dir/subdir/targetfile.xml