再用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)+‘\n‘)
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)+‘\n‘)
            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)+"\n")
    else:
        print("No match!!")

main()
时间: 2024-11-03 22:03:33

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

python写一个通讯录

闲着没事,用python写一个模拟通讯录,要求要实现常用的通讯录的功能,基本流程如下 ? 接下来就按照这个流程实现各个模块的功能 1. 定义一个类,并初始化 1 import json 2 import time 3 4 5 class Address(object): 6 def __init__(self): 7 with open("通讯录.txt", 'r', encoding='utf-8') as f: 8 self.data = json.loads(f.read())

用Python写一个ftp下载脚本

用Python写一个ftp下载脚本 ----基于Red Hat Enterprise Linux Server release 6.4 (Santiago):python 2.6.6 Ps:少侠我接触Python半个月以来接到的第一个需求,虽然如此简单的一个脚本,少侠我磕磕绊绊却用了将近一天半的时间才写出来,但还是很开心,毕竟也粗来了,废话不多说,切入正题.因为一开始没有用过ftplib模块,所以各种谷歌度娘一堆资料杂乱不堪,话不清,理不乱的,本文实现的功能简单,下面介绍一下,以免误导读者. 需

老男孩教育每日一题-2017-04-17:使用Shell或Python写一个脚本,CPU使用率超过80%或硬盘超过85%邮件报警

老男孩教育每日一题-2017-04-17: 使用Shell或Python写一个脚本,CPU使用率超过80%或硬盘超过85%邮件报警. 今天是老男孩教育每日一题陪伴大家的第29天.

python写一个脚本解析文件

Python写一个脚本解析文件 ----基于Red Hat Enterprise Linux Server release 6.4 (Santiago):python 2.6.6 需求: 1.去掉空行 2.去掉空行后输出到一个新文件 附加需求(排版):1.'-'缩进n个字符 '-'缩进2n个字符 以此类推 2.'-'开头的所有句子输出在一行 '-'开头的句子输出在一行 以此类推 --------------------------------------------分隔线------------

python写一个通讯录V2.0

python写一个通讯录step by step V2.0 引用知识 list + dict用于临时存储用户数据信息 cPickle用于格式化文件存取 依旧使用file来进行文件的存储 解决问题 1.操刀开始去做 原始代码 实现功能(可做模板) 1.判断输入内容是否在给出的menu目录内,在的话,返回对应结果,不在就报错 2.调用os模块的exit功能 3.字典配合循环加上函数实现switch的功能 #!/usr/bin/env python #coding:utf8 #Author:zhuim

[py]python写一个通讯录step by step V3.0

python写一个通讯录step by step V3.0 参考: http://blog.51cto.com/lovelace/1631831 更新功能: 数据库进行数据存入和读取操作 字典配合函数调用实现switch功能 其他:函数.字典.模块调用 注意问题: 1.更优美的格式化输出 2.把日期换算成年龄 3.更新操作做的更优雅 准备工作 db准备 - 创建数据库 mysql> create database txl charset utf8; Query OK, 1 row affecte

十行代码--用python写一个USB病毒 (知乎 DeepWeaver)

昨天在上厕所的时候突发奇想,当你把usb插进去的时候,能不能自动执行usb上的程序.查了一下,发现只有windows上可以,具体的大家也可以搜索(搜索关键词usb autorun)到.但是,如果我想,比如,当一个usb插入时,在后台自动把usb里的重要文件神不知鬼不觉地拷贝到本地或者上传到某个服务器,就需要特殊的软件辅助. 于是我心想,能不能用python写一个程序,让它在后台运行.每当有u盘插入的时候,就自动拷贝其中重要文件. 如何判断U盘的插入与否? 首先我们打开电脑终端,进入/Volume

基于zabbix用Python写一个运维流量气象图

前言:同事问我,你写运维平台最先写哪一部分?好吧,还真把我问倒了,因为这是在问最应该放在放在第一位的东西~作为一个工作不足两年,运维不足一年的新手来说,还真不敢妄下评论,其实按照我的思路,觉得最重要的部分肯定是故障处理,报警,但是这一块怎么写?怎么说?肯定不能重复造轮子了,不过我最想写的是报表系统,思路是有的,但是一直耽搁了,详情参考http://youerning.blog.51cto.com/10513771/1708925. 好吧,在回到那个问题,应该先写哪个部分.我没回答,反问他了. 他

用 Python 写一个 NoSQL 数据库

本文译自 What is a NoSQL Database? Learn By Writing One In Python. 完整的示例代码已经放到了 GitHub 上, 请 点击这里, 这仅是一个极简的 demo, 旨在动手了解概念. 如果对译文有任何的意见或建议,欢迎 提 issue 讨论, 批评指正. 后续如有更新,可见 博客 . NoSQL 这个词在近些年正变得随处可见. 但是到底 "NoSQL" 指的是什么? 它是如何并且为什么这么有用? 在本文, 我们将会通过纯 Pytho