主題
- 郵件處理
- 日志模塊
- pdf處理
- md5
- mongodb索引和聚合
- excel 讀寫
1. 發(fā)送郵件模塊
這里指的郵件功能當(dāng)然不是指的是職場(chǎng)上所謂的郵件奏路,指的是程序運(yùn)行中希望將程序運(yùn)行的日志信息或者錯(cuò)誤捕獲信息發(fā)送給指定的收件人赴捞,通過郵件可以了解程序運(yùn)行的狀態(tài)或者出錯(cuò)信息。
關(guān)于郵件的基本概念憋肖,這里引用廖雪峰老師python教程中的郵件模塊:
假設(shè)我們自己的電子郵件地址是me@163.com,對(duì)方的電子郵件地址是friend@sina.com(注意地址都是虛構(gòu)的哈)档桃,現(xiàn)在我們用Outlook或者Foxmail之類的軟件寫好郵件昧廷,填上對(duì)方的Email地址粘拾,點(diǎn)“發(fā)送”窄锅,電子郵件就發(fā)出去了。這些電子郵件軟件被稱為MUA:Mail User Agent——郵件用戶代理。
Email從MUA發(fā)出去入偷,不是直接到達(dá)對(duì)方電腦追驴,而是發(fā)到MTA:Mail Transfer Agent——郵件傳輸代理,就是那些Email服務(wù)提供商疏之,比如網(wǎng)易殿雪、新浪等等。由于我們自己的電子郵件是163.com锋爪,所以丙曙,Email首先被投遞到網(wǎng)易提供的MTA,再由網(wǎng)易的MTA發(fā)到對(duì)方服務(wù)商其骄,也就是新浪的MTA亏镰。這個(gè)過程中間可能還會(huì)經(jīng)過別的MTA,但是我們不關(guān)心具體路線拯爽,我們只關(guān)心速度索抓。
Email到達(dá)新浪的MTA后,由于對(duì)方使用的是@sina.com的郵箱毯炮,因此逼肯,新浪的MTA會(huì)把Email投遞到郵件的最終目的地MDA:Mail Delivery Agent——郵件投遞代理。Email到達(dá)MDA后桃煎,就靜靜地躺在新浪的某個(gè)服務(wù)器上汉矿,存放在某個(gè)文件或特殊的數(shù)據(jù)庫里,我們將這個(gè)長(zhǎng)期保存郵件的地方稱之為電子郵箱备禀。
同普通郵件類似,Email不會(huì)直接到達(dá)對(duì)方的電腦奈揍,因?yàn)閷?duì)方電腦不一定開機(jī)曲尸,開機(jī)也不一定聯(lián)網(wǎng)。對(duì)方要取到郵件男翰,必須通過MUA從MDA上把郵件取到自己的電腦上另患。
所以,一封電子郵件的旅程就是:
發(fā)件人 -> MUA -> MTA -> MTA -> 若干個(gè)MTA -> MDA <- MUA <- 收件人
發(fā)送郵件使用到兩個(gè)模塊:smtplib, email
發(fā)送郵件分為兩步:
- email 構(gòu)造郵件
- smtplib 協(xié)議發(fā)送郵件
郵件發(fā)送主要涉及SMTP協(xié)議, 接收主要涉及POP 協(xié)議蛾绎、IMAP協(xié)議昆箕。
在學(xué)習(xí)之前:我們先看看一封郵件基本包含哪些基礎(chǔ)的東西:這里以QQ郵件為例:
- 收件人
- 發(fā)件人
- 郵件主題
- 郵件正文
- 郵件附件
所以使用郵件模塊的步驟大概也就是完成這些基本的構(gòu)造:
這里是使用QQ郵箱發(fā)送給163企業(yè)郵箱的一個(gè)實(shí)例:
import email
from email.mime.text import MIMEText
import smtplib
from email.header import Header
# 構(gòu)造郵件信息
msg = MIMEText('這是一份python自動(dòng)腳本郵件信息。', 'plain', 'utf-8') # 郵件正文
# QQ發(fā)給公司: 多了這步 server.starttls()
from_addr = "xie_wei_sh@foxmail.com" # 發(fā)件人
password = "****" # 發(fā)件人郵箱密碼
smtp_server = "smtp.qq.com" # SMTP 服務(wù)器地址
port = 587 # SMTP 服務(wù)器端口
to_addr = "paul.xie@chinascope.com" # 收件人
msg["From"] = Header("Python愛好者<{}>".format(from_addr)) # 顯示的郵件發(fā)件人
msg["To"] = Header("admin<{}>".format(to_addr))
msg["Subject"] = Header("來自SMTP的問候", "utf-8") # 郵件主題
# 發(fā)送郵件
server = smtplib.SMTP(smtp_server, port)
server.starttls()
server.set_debuglevel(1)
server.login(from_addr, password=password)
server.sendmail(from_addr, [to_addr], msg=msg.as_string())
server.quit()
上文看上去比較復(fù)雜租冠。有人問有沒有更清晰的方法鹏倘。更清晰的接口。
有的顽爹。
模塊:yagmail 第三方模塊纤泵,需要自己安裝
import yagmail
# 設(shè)置發(fā)送人信息及SMTP服務(wù)器和端口
yag = yagmail.SMTP(
user="xie_wei_sh@foxmail.com",
password="****",
host="smtp.qq.com",
port="587"
)
contents = u"系統(tǒng)發(fā)出警告,你已嚴(yán)重違規(guī)。"
yag.send(to=["paul.xie@chinascope.com","1156143589@qq.com"], subject="yagmail", contents=contents)
# 參數(shù)說明
to : 收件人镜粤,可以接受一個(gè)list 發(fā)送至多人
subject: 郵件主題
contents: 郵件正文捏题,默認(rèn)是文本信息玻褪,其實(shí)還可以接收各種常見的文件比如,*.jpg, *.docx, *.pdf 公荧,*.html 等信息带射,只需要設(shè)置完整路徑,或者同一目錄下文件名稱即可循狰。
- 總結(jié)
- 發(fā)送郵件的步驟: 構(gòu)建郵件信息窟社,SMTP協(xié)議發(fā)送郵件
- 更友好的第三方庫yagmail
- 可能遇到的坑:SMTP協(xié)議服務(wù)器地址和端口不一致而產(chǎn)生的錯(cuò)誤;再一個(gè)可能是郵箱設(shè)置中沒有開啟SMTP晤揣,POP等服務(wù)
2. 日志
借用python最佳實(shí)踐中日志模塊的介紹:
關(guān)于日志的作用:
診斷日志 記錄與應(yīng)用程序操作相關(guān)的日志桥爽。例如,用戶遇到的報(bào)錯(cuò)信息昧识,可通過搜索診斷日志獲得上下文信息钠四。
審計(jì)日志 為商業(yè)分析而記錄的日志。從審計(jì)日志中跪楞,可提取用戶的交易信息缀去,并結(jié)合其他用戶資料構(gòu)成用戶報(bào)告或者用來優(yōu)化商業(yè)目標(biāo)。
其實(shí)print
也能做到這些甸祭,那么為什么還使用日志模塊呢缕碎?
一句話:日志更友好的了解程序運(yùn)行中的信息或者錯(cuò)誤信息,方便了解程序運(yùn)行狀態(tài)以及報(bào)錯(cuò)信息池户。
那么如何使用日志模塊呢咏雌。logging
需要了解信息:
- 日志的級(jí)別
- 關(guān)于日志的基本概念:記錄器,處理器校焦,過濾器赊抖,格式化器
- 編寫常規(guī)的日志需要的步驟
- 日志的級(jí)別:日志分等級(jí),設(shè)置好等級(jí)寨典,比設(shè)置好的級(jí)別大的才能在顯示
- DEBUG
- INFO
- WARN
- ERROR
- CRITICAL
默認(rèn)日志名為root
, 默認(rèn)日志級(jí)別為WARN
在程序中配置日志存在三種方法:
- 使用INI文件配置
- 使用字典或者JSON配置
- 在程序源代碼中配置
這里以在程序源代碼中為例進(jìn)行配置:讀者要是感興趣可以了解其他配置方式:
import logging
logger = logging.getLogger("logger_name") # 記錄器
handler = logging.StreamHandler() # 日志顯示在控制臺(tái)氛雪,還可以設(shè)置將日志信息輸出為文本形式FileHandler()
formatter = logging.Formatter(
'%(asctime)s %(name)-12s %(levelname)-8s %(message)s')
handler.setFormatter(formatter) # 設(shè)置日志顯示方式
logger.addHandler(handler) # 添加處理器
logger.setLevel(logging.DEBUG) # 設(shè)置日志級(jí)別
logger.debug('often makes a very good meal of %s', 'visiting tourists') # 日志在程序中的使用
- 總結(jié)
基本步驟: - 創(chuàng)建logger
logger = logging.getLogger("Your_logger_name")
logger.setLevel(logging.DEBUG)
- 創(chuàng)建handler
logger_one = logging.StreamHandler()
logger_one.setLevel(logging.INFO)
- 創(chuàng)建Formatter
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
- 配置Logger
logger_one.setFormatter(formatter)
logger.addHandler(logger_one)
- 使用logger
logger.info('info message')
3. pdf
存在這樣一個(gè)需求:想要抓取網(wǎng)頁上的信心,但發(fā)現(xiàn)所需要的信息在pdf中
在google中發(fā)現(xiàn)了其實(shí)存在將pdf信息轉(zhuǎn)換為字符串信息的這種模塊:pdfminer
# 讀取本地pdf轉(zhuǎn)化為字符串
from cStringIO import StringIO
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.converter import TextConverter
from pdfminer.layout import LAParams
from pdfminer.pdfpage import PDFPage
def convert_pdf_2_text(path):
rsrcmgr = PDFResourceManager()
retstr = StringIO()
device = TextConverter(rsrcmgr, retstr, codec='utf-8', laparams=LAParams())
interpreter = PDFPageInterpreter(rsrcmgr, device)
with open(path, 'rb') as fp:
for page in PDFPage.get_pages(fp, set()):
interpreter.process_page(page)
text = retstr.getvalue()
device.close()
retstr.close()
return text
# 將數(shù)據(jù)讀入內(nèi)存在進(jìn)行處理
url_data = requests.get("http://www.sse.com.cn/disclosure/bond/announcement/bookentry/c/2768799626348021.pdf").content
data = StringIO(url_data)
parser = PDFParser(data)
document = PDFDocument(parser)
rercmgr = PDFResourceManager(document)
retstr = StringIO()
laparams = LAParams()
codec = 'utf-8'
device = TextConverter(rercmgr, retstr, codec=codec, laparams=laparams)
interpreter = PDFPageInterpreter(rercmgr, device)
for page in PDFPage.create_pages(document):
interpreter.process_page(page)
text = retstr.getvalue()
# 返回的text 就能普通字符串的處理提取信息了耸成。
4. md5
from hashlib
md = hashlib.md5()
md.update("字符串")
md5.hexdigest()
// 現(xiàn)在的問題是想要根據(jù)一個(gè)字典數(shù)據(jù)形成一個(gè)hash, 以使得可以判斷字典數(shù)據(jù)沒有完全一致的报亩。
str = ("_".join("%s:%s" % (key, value) for key, value in dict_item.items() if key not in ("ct")) + "_" + str(number)).encode("utf-8")
md5.update(str)
md5.hexdigest()
5. 索引和聚合
遇到的問題是:能夠很好的對(duì)數(shù)據(jù)進(jìn)行判重處理。
之前的思路是:
- 根據(jù)數(shù)據(jù)字段進(jìn)行數(shù)據(jù)唯一索引設(shè)置井氢,這樣處理其實(shí)去掉了好多為0的值弦追,因?yàn)樽ト〉暮枚鄶?shù)據(jù)都是0,為了需要數(shù)據(jù)的完備性花竞,這些數(shù)據(jù)其實(shí)也需要
之后的思路是:
- 根據(jù)入庫的數(shù)據(jù)進(jìn)行生成md5值骗卜,將md5值設(shè)置為唯一索引值,完成了較好的去重操作
- 索引
coll_values.create_index([("md5_values", pymongo.ASCENDING)], unique=True)
- 聚合操作:對(duì)mongodb數(shù)據(jù)中數(shù)據(jù)進(jìn)行一些統(tǒng)計(jì)工作
- match
- group
- limit
- sort
query = [
{
"$match": {"key": code, "year": year, "type": type}
},
{
"$group": {"_id": {"code": "$key", "year":"$year"}, "total": {"$sum":1}}
}
]
total = []
for code in ["ACH", "IKGH", "ALN", "AMCN", "ATAI", "AXN", "ATV", "BIDU", "BITA", "BORN", "BSPM"]:
for one in ["2011", '2012', '2013', '2014', "2015"]:
A = Count(code=code, year=one, type="Annual", factor="Income Statement")
result = A.aggregation()
total.append(list(result))
pprint(total)
# 根據(jù)股票信息統(tǒng)計(jì)年份的數(shù)據(jù)數(shù)
6. excel 讀寫
需求是:想要將mongodb數(shù)據(jù)庫中的數(shù)據(jù)導(dǎo)入入excel中
使用到的模塊是:xlwt
關(guān)于excel的一些基本概念:
- Workbook : 工作簿
- sheet: 工作表
- cell: 單元格
一個(gè)workbook 可以包含多個(gè)sheet, 一個(gè)sheet中包含更多的行列組成的表格。
編寫程序的大概步驟也是:
- 實(shí)例化workbook
- 添加sheet
- 往單元格里面寫入內(nèi)容
- 保存文件
import xlwt
wb = xlwt.Workbook()
ws = wb.add_sheet('A Test Sheet')
ws.write(0, 0, 1234.56) # 第一行第一列寫入123.56
ws.write(2, 0, 1) # 第三行第一列寫入 1
ws.write(2, 1, 1) # 第三行第二列寫入 1
ws.write(2, 2, xlwt.Formula("A3+B3")) # 第三行第三列是前面值之和
wb.save('example.xls') # 保存為文件名example.xls
所以:xlwt的基本使用就是往單元格中進(jìn)行內(nèi)容的寫入寇仓。
// mongodb 實(shí)例
from pymongo import MongoClient
from xlwt import Workbook
from xlwt import easyxf # 可以定義字體举户,顏色等樣式
class MongoToXls(object):
def __init__(self, collection, name):
self.work_book = collection
self.sheet_name = name
self.wb = Workbook()
self.ws = [self.wb.add_sheet(one) for one in self.sheet_name]
self.style = easyxf("align: vert centre, horiz center")
pass
def info(self, number):
self.contents = list(self.work_book[number].find())
self.headers = [key for key in self.contents[0].keys()]
rows = len(self.contents)
columns = len(self.headers)
return rows, columns
def write_header(self, number):
ws = self.ws[number]
_, columns = self.info(number)
for i in range(0, columns):
ws.write(0, i, self.headers[i], style=self.style)
def write_content(self, number):
ws = self.ws[number]
rows, columns = self.info(number)
if rows >= 65536:
rows = 65535
for j in range(1, rows + 1):
for k in range(0, columns):
ws.write(j, k, str(self.contents[j - 1][self.headers[k]]), style=self.style)
def save(self):
name = "_".join(self.sheet_name) +"2"+ str(".xls")
self.wb.save(str(name))
collection1 = MongoClient()["db5"]["base"]
collection2 = MongoClient()["db5"]["items"]
collection3 = MongoClient()["db5"]["values"]
collection = [collection1, collection2, collection3]
name = ["base", "items", "values"]
A = MongoToXls(collection=collection, name=name)
for number in range(len(collection)):
print A.info(number)
A.write_header(number)
A.write_content(number)
A.save()
總結(jié):
- 根據(jù)數(shù)據(jù)獲取行和列數(shù)
- 先寫入header信息
- 再兩重循環(huán)寫入內(nèi)容值