• 再用python写一个文本处理的东东


    朋友遇到一点麻烦,我自告奋勇帮忙。事情是这样的:

    - 他们的业务系统中,数据来自一个邮箱;

    - 每一个邮件包含一条记录;

    - 这些记录是纯文本的,字段之间由一些特殊字符分隔;

    - 他们需要从邮箱中批量取出每一封邮件,放到一个excel文件中。

    这些对python来说,真是小菜一碟。(事后证明,还是有些小坑,让我头疼了好一会儿。)

    因为是初学者,没有必要从python2起步,我直接用了python3。

    首先是收信。邮箱不支持pop3取信,好在支持IMAP。查了一下,python3有专门的库可以做到。

    然后是要用正则表达式处理文本。

    生成excel需要用到什么什么第三方库,找了一下,没下下来。干脆就简单点,生成csv文件吧。

    ==============

     1 def main():
     2     M = imaplib.IMAP4_SSL("my-host.com","993")
     3     t=0
     4     try:
     5         try:
     6             M.login('my-username','my-password')
     7         except Exception as e:
     8             print('login error: %s' % e)
     9             M.close()
    10         
    11         M.select('INBOX',False)
    12         
    13         # result, message = M.select()
    14         # tips: 如果想找Essh邮件的话,使用
    15         # type, data = M.search(None, '(SUBJECT "Essh")')
    16         # 里面要用一个括号,代表是一个查询条件,可以同时指定多个查询条件,例如FROM xxxx SUBJECT "aaa",
    17         # 注意,命令要用括号罩住(痛苦的尝试)
    18         typ, data = M.search(None, 'ALL')
    19 
    20         msgList = data[0].split()
    21         print("total mails:" + str(len(msgList)))
    22         last = msgList[len(msgList) - 1]
    23         # first = msgList[0]
    24         # M.store(first, '-FLAGS', '(Seen)')
    25         # M.store("1:*", '+FLAGS', '\Deleted') #Flag all Trash as Deleted
    26         output=PATH+'output.csv'
    27         fp2=open(output, 'w')
    28 
    29         last_id=read_config()
    30         count=0
    31         for idx in range(int(last_id), len(msgList)):
    32             print("curr id: "+str(idx)+'
    ')
    33             type,data=M.fetch(msgList[idx],'(RFC822)')
    34             deal_mail(data, fp2)
    35             count=count+1
    36             if count>500:
    37                 break
    38 
    39         write_config(idx)
    40         # print(str(idx))
    41         print("OK!")
    42         M.logout()
    43     except Exception as e:
    44         print('imap error: %s' % e)
    45         M.close()

    这是main()部分。主要是连接IMAP服务器、取信、调用处理函数。

    我发现,IMAP提供的接口比较怪异。不管怎么说,没怎么掉坑,网上的资料都很齐全。关于搜索的语法以及删除和置为已读/未读的命令都放在注释里。

    这里面的逻辑是:首先获取上次处理的序号last_id,从此处开始,处理500条信件。然后将新的last_id写入配置文件中,下次读取。

    在写这个程序的时候,遇到的最多的麻烦是关于str和bytes类型的。因为网上许多代码都是来自python2,所以在python3中就遇到多次提示:

    cannot use a string pattern on a bytes-like object
    write() argument must be str, not bytes
    error: a bytes-like object is required, not 'str'
    error: string argument without an encoding
    error: cannot use a string pattern on a bytes-like object
     
    等等。。。一个头两个大。比如,我要把半角逗号替换成全角逗号,这个最简单不过的功能,就试了半天:

    这个错:

    content=content.replace(',', ',')
    error: a bytes-like object is required, not 'str'

    这个也错:

    content=content.replace(',', bytes(','))
    error: string argument without an encoding

    最终这个才对了:

    content=content.replace(bytearray(',','GBK'), ','.encode('GBK'))

    可是当我继续要把半角双引号变成全角双引号时,情况又不一样:

    matchObj = re.match( r'.*<body>(.*)</body>.*', content.decode('GBK'), re.M|re.I|re.S)
    if matchObj:
        found=matchObj.group(1) #邮件正文
        aa=found.split('#$') #分解为一个个field
        aa[9]=aa[9].replace('"', '“') #我靠前面的写法用不着了! content=content.replace(bytearray(',','GBK'), ','.encode('GBK'))

    我汗。。。总之肯定里面有什么东西还不大明白,导致走了许多弯路。记录下来,利已助人吧。

    以下是全部源码

    #-*- coding:UTF-8 -*-
    import imaplib, string, email
    import os
    import re
    
    CONFIG_FILE='last_id.txt'
    PATH=os.path.split(os.path.realpath(__file__))[0]
    
    def main():
        M = imaplib.IMAP4_SSL("my-host.com","993")
        t=0
        try:
            try:
                M.login('my-username','my-password')
            except Exception as e:
                print('login error: %s' % e)
                M.close()
            
            M.select('INBOX',False)
            
            # result, message = M.select()
            # tips: 如果想找Essh邮件的话,使用
            # type, data = M.search(None, '(SUBJECT "Essh")')
            # 里面要用一个括号,代表是一个查询条件,可以同时指定多个查询条件,例如FROM xxxx SUBJECT "aaa",
            # 注意,命令要用括号罩住(痛苦的尝试)
            typ, data = M.search(None, 'ALL')
    
            msgList = data[0].split()
            print("total mails:" + str(len(msgList)))
            last = msgList[len(msgList) - 1]
            # first = msgList[0]
            # M.store(first, '-FLAGS', '(Seen)')
            # M.store("1:*", '+FLAGS', '\Deleted') #Flag all Trash as Deleted
            output=PATH+'output.csv'
            fp2=open(output, 'w')
    
            last_id=read_config()
            count=0
            for idx in range(int(last_id), len(msgList)):
                print("curr id: "+str(idx)+'
    ')
                type,data=M.fetch(msgList[idx],'(RFC822)')
                deal_mail(data, fp2)
                count=count+1
                if count>500:
                    break
    
            write_config(idx)
            # print(str(idx))
            print("OK!")
            M.logout()
        except Exception as e:
            print('imap error: %s' % e)
            M.close()
    
    def main2():
        path=os.path.split(os.path.realpath(__file__))[0]
        input=path+'input2.txt'
        output=path+'output.csv'
    
        fp=open(input, 'rb')
        fp2=open(output, 'w')
        if True:
            line=fp.read()
            pharse_content(fp2, line)
    
    def get_mime_version(msg):
        if msg != None:
            return email.utils.parseaddr(msg.get('mime-version'))[1]
        else:
            empty_obj()
    def get_message_id(msg):
        if msg != None:
            return email.utils.parseaddr(msg.get('Message-ID'))[1]
        else:
            empty_obj()
    
    # 读config文件,获取上次最大id,从这个id开始读邮件
    def read_config():
        if os.path.isfile(PATH+"\"+CONFIG_FILE):
            _fp=open(PATH+"\"+CONFIG_FILE)
            id=_fp.read()
            _fp.close()
        else:
            id=0
        return id
    
    # 将本次处理的邮件的最大id写入config,以便下次读取
    def write_config(id):
        _fp=open(PATH+"\"+CONFIG_FILE, 'w')
        _fp.write(str(id))
        _fp.close()
    
    def deal_mail(data, fp2):
        msg=email.message_from_string(data[0][1].decode('GBK'))
        messageid = get_message_id(msg)
        print(messageid)
        content=msg.get_payload(decode=True)
        #print(content)
        pharse_content(fp2, content, messageid)
    
    def pharse_content(fp2, content, messageid):
        #将半角的 , 换成全角的 ,
        # content=content.replace(',', ',')   # error: a bytes-like object is required, not 'str'
        # content=content.replace(',', bytes(',')) # error: string argument without an encoding
        content=content.replace(bytearray(',','GBK'), ','.encode('GBK'))
        # print(content.decode('GBK'))
    
        # strinfo=re.compile(',')
        # content=strinfo.sub(',', content)  # error: cannot use a string pattern on a bytes-like object
    
        matchObj = re.match( r'.*<body>(.*)</body>.*', content.decode('GBK'), re.M|re.I|re.S)
        if matchObj:
            found=matchObj.group(1) #邮件正文
            aa=found.split('#$')    #分解为一个个field
    
            # 获取申诉涉及号码。匹配模式:申诉问题涉及号码:18790912404;
            mobileObj=re.match(r'.*申诉问题涉及号码:(.*);', aa[9], re.M|re.I|re.S)
            if mobileObj:
                mobile=mobileObj.group(1)
            else:
                mobile=''
    
            # bb 是结果数组,对应生成的csv文件的列
            aa[9]=aa[9].replace('"', '“') #我靠前面的写法用不着了! content=content.replace(bytearray(',','GBK'), ','.encode('GBK'))
    
            bb=['']*40    #40个元素的数组,对应40个列
            bb[3]=aa[0]   #D列
            bb[4]=aa[4]   #E
            bb[5]=mobile  #F
            bb[6]=aa[5]   #G
            bb[7]=aa[2]   #H
            bb[8]=aa[1]   #I
            bb[9]=aa[3]   #J
            bb[11]=aa[6]  #L
            bb[12]=aa[6]  #M
            bb[22]='网站' #W 申诉来源。此处可自行修改为指定类型
            bb[36]='"'+aa[9]+'"'  #AK,两侧加 "" 是为了保证多行文字都放进一个单元格中
    
            DELI=','
            # fp2.write("AAAAA,"+DELI.join(bb)+"\n")
            fp2.write(DELI.join(bb)+"
    ")
        else:
            print("No match!!")
    
    main()
  • 相关阅读:
    网络服务管理手册
    Oracle随机函数的取法
    oracle 分析函数over
    RMAN故障解决——RMAN用户手册
    SQL*PLUS命令的使用大全
    如何监测一个PLSQL过程的运行情况(三)
    oracle for in loop 两例
    自定义组件——按钮(转)
    新人报到安家!
    Delphi中生成控件的两种方法
  • 原文地址:https://www.cnblogs.com/anjo/p/5965051.html
Copyright © 2020-2023  润新知