首页 > 代码库 > 再用python写一个文本处理的东东

再用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 = http://www.mamicode.com/M.search(None, ‘(SUBJECT"Essh")‘)16         # 里面要用一个括号,代表是一个查询条件,可以同时指定多个查询条件,例如FROM xxxx SUBJECT "aaa",17         # 注意,命令要用括号罩住(痛苦的尝试)18         typ, data = http://www.mamicode.com/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 Deleted26         output=PATH+‘\output.csv‘27         fp2=open(output, ‘w‘)28 29         last_id=read_config()30         count=031         for idx in range(int(last_id), len(msgList)):32             print("curr id: "+str(idx)+‘\n‘)33             type,data=http://www.mamicode.com/M.fetch(msgList[idx],‘(RFC822)‘)34             deal_mail(data, fp2)35             count=count+136             if count>500:37                 break38 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, emailimport osimport reCONFIG_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 = http://www.mamicode.com/M.search(None, ‘(SUBJECT"Essh")‘)        # 里面要用一个括号,代表是一个查询条件,可以同时指定多个查询条件,例如FROM xxxx SUBJECT "aaa",        # 注意,命令要用括号罩住(痛苦的尝试)        typ, data = http://www.mamicode.com/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)+‘\n‘)            type,data=http://www.mamicode.com/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)+"\n")    else:        print("No match!!")main()

 

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