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

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

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

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

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

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

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

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

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

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

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

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

这是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()

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

  1. 【Python】如何基于Python写一个TCP反向连接后门

    首发安全客 如何基于Python写一个TCP反向连接后门 https://www.anquanke.com/post/id/92401 0x0 介绍 在Linux系统做未授权测试,我们须准备一个安全的 ...

  2. Python写一个自动点餐程序

    Python写一个自动点餐程序 为什么要写这个 公司现在用meican作为点餐渠道,每天规定的时间是早7:00-9:40点餐,有时候我经常容易忘记,或者是在地铁/公交上没办法点餐,所以总是没饭吃,只有 ...

  3. 用Python写一个简单的Web框架

    一.概述 二.从demo_app开始 三.WSGI中的application 四.区分URL 五.重构 1.正则匹配URL 2.DRY 3.抽象出框架 六.参考 一.概述 在Python中,WSGI( ...

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

    昨天在上厕所的时候突发奇想,当你把usb插进去的时候,能不能自动执行usb上的程序.查了一下,发现只有windows上可以,具体的大家也可以搜索(搜索关键词usb autorun)到.但是,如果我想, ...

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

    python写一个通讯录step by step V3.0 参考: http://blog.51cto.com/lovelace/1631831 更新功能: 数据库进行数据存入和读取操作 字典配合函数 ...

  6. python统计一个文本中重复行数的方法

    python统计一个文本中重复行数的方法 这篇文章主要介绍了python统计一个文本中重复行数的方法,涉及针对Python中dict对象的使用及相关本文的操作,具有一定的借鉴价值,需要的朋友可以参考下 ...

  7. 用python写一个自动化盲注脚本

    前言 当我们进行SQL注入攻击时,当发现无法进行union注入或者报错等注入,那么,就需要考虑盲注了,当我们进行盲注时,需要通过页面的反馈(布尔盲注)或者相应时间(时间盲注),来一个字符一个字符的进行 ...

  8. python写一个能变身电光耗子的贪吃蛇

    python写一个不同的贪吃蛇 写这篇文章是因为最近课太多,没有精力去挖洞,记录一下学习中的收获,python那么好玩就写一个大一没有完成的贪吃蛇(主要还是跟课程有关o(╥﹏╥)o,课太多好烦) 第一 ...

  9. python写一个双色球彩票计算器

    首先声明,赌博一定不是什么好事,也完全没有意义,不要指望用彩票发财.之所以写这个,其实是用来练手的,可以参考这个来预测一些其他的东西,意在抛砖引玉. 啰嗦完了,马上开始,先上伪代码 打开网址 读取内容 ...

随机推荐

  1. bzoj1195

    AC自动机+状压dp 多串匹配要想ac自动机 dp[i][S]表示在i状态选中S 转移就用bfs,每个点通过fail收集信息,不要忘记通过fail传递 昨天搞不明白为什么自动机每次只可以转移儿子,不可 ...

  2. python 去停用词

    Try caching the stopwords object, as shown below. Constructing this each time you call the function ...

  3. atom 的一些东东

    一. 配置atom atom 有些插件被墙了, 往往导致无法下载插件, 网上查了一些解决方案, 大部分就两种解决方案. 配置国内源 离线下载插件 1. 配置国内源 Linux 在 /home/user ...

  4. python--numpy模块、spicy模块、 matplotlib模块

    一:numpy模块 ndarray:存储单一数据类型的多维数组 ufunc:能够对数组进行处理的函数(universal function object) #numpy 中arange用法,指定开始值 ...

  5. B - Equidistant String

    B - Equidistant String Time Limit:1000MS     Memory Limit:262144KB     64bit IO Format:%I64d & % ...

  6. HDOJ-2037

    今年暑假不AC Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Sub ...

  7. Qt开篇

    使用Qt两年有余,遇到问题多是现查现用,由于之前供职于一家保密性较强的单位,遇到的很多问题没有被记录下来.从今天开始,我会记记录自己的笔记.

  8. UICollctionView 刷新 item 刷新 消失

    在需要局部刷新的时候,可能出现的问题: 当时采用的局部刷新,第一次刷新没问题,当多次刷新的时候 item 就会消失 NSIndexSet *]; [collectionView reloadSecti ...

  9. 使用Bootstrap实现表格列的显示与隐藏

    来人,上效果图 走官方通道 1.引入样式文件(去github下载样式文件) <!--插件开--> <link rel="stylesheet" href=&quo ...

  10. JavaScript代码风格和分号使用问题

    1.推荐代码风格 JavaScript Standard Style  规定相对松散更多人使用此规范 Airbnb JavaScript Style  规定更严格但也没毛病 2.JavaScript代 ...