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

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

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

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

- 他们需要从邮箱中批量取出每一封邮件,放到一个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'))

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

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

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

以下是全部源码

  1. #-*- coding:UTF-8 -*-
  2. import imaplib, string, email
  3. import os
  4. import re
  5.  
  6. CONFIG_FILE='last_id.txt'
  7. PATH=os.path.split(os.path.realpath(__file__))[0]
  8.  
  9. def main():
  10. M = imaplib.IMAP4_SSL("my-host.com","993")
  11. t=0
  12. try:
  13. try:
  14. M.login('my-username','my-password')
  15. except Exception as e:
  16. print('login error: %s' % e)
  17. M.close()
  18.  
  19. M.select('INBOX',False)
  20.  
  21. # result, message = M.select()
  22. # tips: 如果想找Essh邮件的话,使用
  23. # type, data = M.search(None, '(SUBJECT "Essh")')
  24. # 里面要用一个括号,代表是一个查询条件,可以同时指定多个查询条件,例如FROM xxxx SUBJECT "aaa",
  25. # 注意,命令要用括号罩住(痛苦的尝试)
  26. typ, data = M.search(None, 'ALL')
  27.  
  28. msgList = data[0].split()
  29. print("total mails:" + str(len(msgList)))
  30. last = msgList[len(msgList) - 1]
  31. # first = msgList[0]
  32. # M.store(first, '-FLAGS', '(\Seen)')
  33. # M.store("1:*", '+FLAGS', '\\Deleted') #Flag all Trash as Deleted
  34. output=PATH+'\output.csv'
  35. fp2=open(output, 'w')
  36.  
  37. last_id=read_config()
  38. count=0
  39. for idx in range(int(last_id), len(msgList)):
  40. print("curr id: "+str(idx)+'\n')
  41. type,data=M.fetch(msgList[idx],'(RFC822)')
  42. deal_mail(data, fp2)
  43. count=count+1
  44. if count>500:
  45. break
  46.  
  47. write_config(idx)
  48. # print(str(idx))
  49. print("OK!")
  50. M.logout()
  51. except Exception as e:
  52. print('imap error: %s' % e)
  53. M.close()
  54.  
  55. def main2():
  56. path=os.path.split(os.path.realpath(__file__))[0]
  57. input=path+'\input2.txt'
  58. output=path+'\output.csv'
  59.  
  60. fp=open(input, 'rb')
  61. fp2=open(output, 'w')
  62. if True:
  63. line=fp.read()
  64. pharse_content(fp2, line)
  65.  
  66. def get_mime_version(msg):
  67. if msg != None:
  68. return email.utils.parseaddr(msg.get('mime-version'))[1]
  69. else:
  70. empty_obj()
  71. def get_message_id(msg):
  72. if msg != None:
  73. return email.utils.parseaddr(msg.get('Message-ID'))[1]
  74. else:
  75. empty_obj()
  76.  
  77. # 读config文件,获取上次最大id,从这个id开始读邮件
  78. def read_config():
  79. if os.path.isfile(PATH+"\\"+CONFIG_FILE):
  80. _fp=open(PATH+"\\"+CONFIG_FILE)
  81. id=_fp.read()
  82. _fp.close()
  83. else:
  84. id=0
  85. return id
  86.  
  87. # 将本次处理的邮件的最大id写入config,以便下次读取
  88. def write_config(id):
  89. _fp=open(PATH+"\\"+CONFIG_FILE, 'w')
  90. _fp.write(str(id))
  91. _fp.close()
  92.  
  93. def deal_mail(data, fp2):
  94. msg=email.message_from_string(data[0][1].decode('GBK'))
  95. messageid = get_message_id(msg)
  96. print(messageid)
  97. content=msg.get_payload(decode=True)
  98. #print(content)
  99. pharse_content(fp2, content, messageid)
  100.  
  101. def pharse_content(fp2, content, messageid):
  102. #将半角的 , 换成全角的 ,
  103. # content=content.replace(',', ',') # error: a bytes-like object is required, not 'str'
  104. # content=content.replace(',', bytes(',')) # error: string argument without an encoding
  105. content=content.replace(bytearray(',','GBK'), ','.encode('GBK'))
  106. # print(content.decode('GBK'))
  107.  
  108. # strinfo=re.compile(',')
  109. # content=strinfo.sub(',', content) # error: cannot use a string pattern on a bytes-like object
  110.  
  111. matchObj = re.match( r'.*<body>(.*)</body>.*', content.decode('GBK'), re.M|re.I|re.S)
  112. if matchObj:
  113. found=matchObj.group(1) #邮件正文
  114. aa=found.split('#$') #分解为一个个field
  115.  
  116. # 获取申诉涉及号码。匹配模式:申诉问题涉及号码:18790912404;
  117. mobileObj=re.match(r'.*申诉问题涉及号码:(.*);', aa[9], re.M|re.I|re.S)
  118. if mobileObj:
  119. mobile=mobileObj.group(1)
  120. else:
  121. mobile=''
  122.  
  123. # bb 是结果数组,对应生成的csv文件的列
  124. aa[9]=aa[9].replace('"', '“') #我靠前面的写法用不着了! content=content.replace(bytearray(',','GBK'), ','.encode('GBK'))
  125.  
  126. bb=['']*40 #40个元素的数组,对应40个列
  127. bb[3]=aa[0] #D列
  128. bb[4]=aa[4] #E
  129. bb[5]=mobile #F
  130. bb[6]=aa[5] #G
  131. bb[7]=aa[2] #H
  132. bb[8]=aa[1] #I
  133. bb[9]=aa[3] #J
  134. bb[11]=aa[6] #L
  135. bb[12]=aa[6] #M
  136. bb[22]='网站' #W 申诉来源。此处可自行修改为指定类型
  137. bb[36]='"'+aa[9]+'"' #AK,两侧加 "" 是为了保证多行文字都放进一个单元格中
  138.  
  139. DELI=','
  140. # fp2.write("AAAAA,"+DELI.join(bb)+"\\n")
  141. fp2.write(DELI.join(bb)+"\n")
  142. else:
  143. print("No match!!")
  144.  
  145. 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. CodeForces 1110G. Tree-Tac-Toe

    题目简述:给定$n$个节点的树,其中一些节点被染成了白色(其余节点未染色).黑白双方博弈,白先动.轮到黑(白)方时,选择树上的一个未染色的节点并将其染成黑(白)色.率先达成三连色(即存在三个节点$a, ...

  2. 2.8-2.10 HBase集成MapReduce

    一.HBase集成MapReduce 1.查看HBase集成MapReduce需要的jar包 [root@hadoop-senior hbase-0.98.6-hadoop2]# bin/hbase ...

  3. 【eclipse插件开发实战】 Eclipse插件开发6——eclipse在线翻译插件Translator开发实例详解

    Eclipse插件开发6--eclipse在线翻译插件Translator开发实例详解 在上一篇文章中讲到了一个简单的eclipse插件开发实例,主要是对插件工程的基本创建步骤进行了讲解,这篇文章当中 ...

  4. 【eclipse插件开发实战】Eclipse插件开发2——SWT

    Eclipse插件开发实战2--SWT 一.SWT简介 SWT(StandardWidget Toolkit) 标准小窗口工具箱,一开源的GUI编程框架,与AWT/Swing有相似的用处,eclips ...

  5. UVaLive 6847 Zeroes (找规律,水题)

    题意 :给定一个范围,然后让你求在这个范围内所有的数的阶乘末尾0的个数有多少种. 析:找规律,写几个就会发现每隔5个会增加一个0,因为要么乘10了,要么乘5了. 代码如下: #pragma comme ...

  6. IE8 以上版本兼容

    在html的内如下写法 其中最后一行是永远以最新的IE版本模式来显示网页的. 另外加上Emulate模式 Emulate模式后则更重视 (细心的人会注意到,用IE9去访问带有x-ua-compatib ...

  7. forEach方法如何跳出循环

    1.for方法跳出循环 function getItemById(arr, id) { var item = null; for (var i = 0; i < arr.length; i++) ...

  8. Visual Studio 调试卡顿的解决方法

    将不用的断点删除 关闭调试时打开的"IntelliXXX的窗口" 上面的第2条测试有效! https://social.msdn.microsoft.com/Forums/zh-C ...

  9. Educational Codeforces Round 18D(完全二叉树中序遍历&lowbit)

    题目链接:http://codeforces.com/contest/792/problem/D 题意:第一行输入n, q,分别表示给出一颗n个节点的中序遍历满二叉树,后面有q个询问; 接下来有q组形 ...

  10. bzoj 4032: [HEOI2015]最短不公共子串【dp+SAM】

    第一.二问: 就是最小的最长公共长度+1,设f[i][j]为a匹配到i,b匹配到j,第一问的转移是f[i][j]=(a[i]==b[j]?f[i-1][j-1]+1:0),第二问的转移是f[i][j] ...