【详解】Python爬虫脚本M9优化
来源:互联网 发布:如何关淘宝店铺 编辑:程序博客网 时间:2024/04/30 00:23
本例中,在原来爬虫脚本的基础上添加了新的需求。
需要生成一个新的csv文件,写入M9充值方式的结果。
其实在原有的基础上改的话,很简单,因为逻辑很清晰,只需要在查询的时候加一个查询条件,
分析得知,条件是pcid=26。
queryUrl2 = "http://money.downjoy.com/connectchannel/list_prompt_new_channel_stat.html?startDate=%s&endDate=%s&pcid=26"%(lastDayDateStr,todayStr )知道了要使用这个链接,我们甚至可以直接只改这里,然后跑一遍脚本就能够得到我们所需要的csv文件,并且自动发送邮件。
改个附件名就可以了。
但是,我们的脚本是需要在脚本后台自动跑的,所以我们需要做的就是在一个脚本中,
生成两个文件,并把这两个文件添加到邮件的附件,发送。
这样,优化原有的Python脚本就很必要了。
因为,两个文件的写入逻辑是非常非常一样的,只是查询链接不同而已。
一样,意味着代码会重复,代码重复意味着代码可重用,代码可重用的意思就是,可以抽象出方法来。
我们需要花很大精力要做的,即是抽象出方法来。
一者,使得逻辑更加清晰;
二者,方便以后维护,如果以后再添加一个文件,改起来就很方便了。
【问题】
在抽象方法的过程中。
我们遇到了这样几个问题
【全局变量的使用】
today = datetime.datetime.today()todayStr = datetime.datetime.strftime(today, "%Y-%m-%d")lastDayDate = today - datetime.timedelta(1)lastDayDateStr = datetime.datetime.strftime(lastDayDate, "%Y-%m-%d")xlsfile = r'read.xlsx'fileName='downjoyBackendData_'+lastDayDateStr+'_.csv'fileNameM9='downjoyBackendData_'+lastDayDateStr+'_M9.csv'mainUrl="http://money.downjoy.com/connectchannel/login.jsp"loginUrl="http://money.downjoy.com/connectchannel/login.html"queryUrl1 = "http://money.downjoy.com/connectchannel/list_prompt_new_channel_stat.html?startDate=%s&endDate=%s"%(lastDayDateStr,todayStr )queryUrl2 = "http://money.downjoy.com/connectchannel/list_prompt_new_channel_stat.html?startDate=%s&endDate=%s&pcid=26"%(lastDayDateStr,todayStr )logoutUrl ="http://money.downjoy.com/connectchannel/logout.html"csvfile1=file(fileName, 'ab+')writer1 = csv.writer(csvfile1, dialect='excel')csvfile2=file(fileNameM9, 'ab+')writer2 = csv.writer(csvfile2, dialect='excel')book = xlrd.open_workbook(xlsfile)sheet = book.sheet_by_index(0)nrows = sheet.nrows以上,是我们在改进这个爬虫脚本时在最顶端定义的全局变量。
我们知道,Python以及其他我们所知的编程语言中,参数的使用,
一者,是定义变量,然后调用,全局变量的好处是,定义了然后可以在任意地方使用;
二者,是传递参数,主要是用到了函数或者方法的时候,我们需要传参。
这里我们采用全局变量的定义这种方式!
因为很多地方都用到了,传递太过麻烦,直接调用反而简便。
而且我们需要知道一点,Python脚本运行的时候,最先执行的代码其实不是main方法,而是顶端的全局变量代码。
如果我们的全局变量定义有问题,最一开始就会有提示!
【文件的写入模式】
至今为止,我们写入csv经常使用的方式是ab+,即追加模式,防止我们再次或多次写入的时候,覆盖原先的内容。
但是,我们之前不清楚的是,这里的覆盖指的是多次写入才会覆盖。
我们之前对多次写入的概念不太清楚!
多次写入覆盖发生,是因为多次open和close了。即,
open---写入---close---open---写入----close!
如果这个时候我们采用w的写入,当然只有最后一次的写入内容了。
但是本例的改进,我们做了一件事,即,将open这样的操作放到了全局变量中,
def main(): print "===%s start===%s"%(sys.argv[0], datetime.datetime.strftime(datetime.datetime.now(), "%Y-%m-%d %H:%M:%S")) writeHeader(writer1) writeHeader(writer2) toLogin() toWrite() csvfile1.close() csvfile2.close() sendEmail() print "===%s end===%s"%(sys.argv[0], datetime.datetime.strftime(datetime.datetime.now(), "%Y-%m-%d %H:%M:%S"))
我们一开始运行脚本的时候,就是open的状态,只有最终我们执行完整个脚本,才会close。
这也是全局变量的一个好处!
所以这里我们采用覆盖写入的方式!即w写入!
为什么要这么写呢?
因为如果一天之内跑多次脚本的话,写出来的表就会是两张表的结合。而我们需要的表,只需要一份数据而已。
所以我们这里改为w模式写入。
【w模式写入出现空行的问题】
出现空行,我们的解决方法是将w改为wb。
b是什么意思呢?这里作一下解释!
Python中csv的writer打开文件的时候要小心,要通过binary模式去打开,即带b的,比如
wb,ab+
指明了是binary模式后,就不会出现空行了!
【Python中的异常处理】
我们在测试这个脚本的时候遇到过一个问题,报了http的错误。
即,运行脚本的时候网络出了问题,导致脚本运行中断!
这属于异常的一种,我们要对其进行捕获处理。如果该脚本部署到脚本后台,运行的时候遇到网络问题,就会异常中断。
会给我们的正常工作带来很大的困扰。
所以我们将遍历登录部分的代码修改如下:
def toWrite(): # 遍历登录并登出 for i in range(1, nrows): id = getId(i) username = getUserName(i) password = getPassword(i) try: login(id, password) data1=getData(queryUrl1) data2=getData(queryUrl2) writeContent(id,username, password,data1,writer1) writeContent(id,username,password,data2,writer2) # 退出登录 urllib2.urlopen(logoutUrl) except Exception, e: print str(e)即,对所有可能会出现的异常捕获并打印错误信息!
最后我们贴一下最终版的代码,方便以后查看!
主要是我们再看代码的时候,要注意这些方法的抽象!
如果我们以后还改,有了抽象出来的方法,就能很方便的添加新的文件写入之类的。
万一以后又有个需求说要将易联充值也写出一个csv文件来,就方便很多了,我们只需要在各个关键部分添加一行一行的代码就能搞定了。
不需要动关键逻辑部分的代码!只需要添加!
#!/usr/bin/python# -*- coding: utf-8 -*-__author__ = "$Author: wangxin.xie$"__version__ = "$Revision: 1.1 $"__date__ = "$Date: 2015-11-19 16:55$"################################################################ 功能:自动抓取当乐网与渠道合作后台数据###############################################################import reimport cookielibimport urllibimport urllib2import datetimeimport xlrdimport csvimport mathimport smtplibimport sysfrom email.mime.text import MIMETextfrom email.mime.multipart import MIMEMultipartfrom bs4 import BeautifulSoup#####################全局变量###########################################today = datetime.datetime.today()todayStr = datetime.datetime.strftime(today, "%Y-%m-%d")lastDayDate = today - datetime.timedelta(1)lastDayDateStr = datetime.datetime.strftime(lastDayDate, "%Y-%m-%d")xlsfile = r'read.xlsx'fileName='downjoyBackendData_'+lastDayDateStr+'_.csv'fileNameM9='downjoyBackendData_'+lastDayDateStr+'_M9.csv'mainUrl="http://money.downjoy.com/connectchannel/login.jsp"loginUrl="http://money.downjoy.com/connectchannel/login.html"queryUrl1 = "http://money.downjoy.com/connectchannel/list_prompt_new_channel_stat.html?startDate=%s&endDate=%s"%(lastDayDateStr,todayStr )queryUrl2 = "http://money.downjoy.com/connectchannel/list_prompt_new_channel_stat.html?startDate=%s&endDate=%s&pcid=26"%(lastDayDateStr,todayStr )logoutUrl ="http://money.downjoy.com/connectchannel/logout.html"csvfile1=file(fileName, 'wb')writer1 = csv.writer(csvfile1, dialect='excel')csvfile2=file(fileNameM9, 'ab+')writer2 = csv.writer(csvfile2, dialect='excel')book = xlrd.open_workbook(xlsfile)sheet = book.sheet_by_index(0)nrows = sheet.nrows#####################模拟登录##############################################def writeCsv(x,writer): # 写入csv writer.writerow(x)# 打印表头def writeHeader(writer): writeCsv(["账号".decode('utf-8').encode('gbk'), "用户名".decode('utf-8').encode('gbk'), "密码".decode('utf-8').encode('gbk'), "应用名称".decode('utf-8').encode('gbk'), "日期".decode('utf-8').encode('gbk'), "充值金额".decode('utf-8').encode('gbk'), "用户付费次数".decode('utf-8').encode('gbk')],writer)def toLogin(): # 进入登录页面 urllib2.urlopen(mainUrl) # 处理cookie cj = cookielib.CookieJar() opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj)) urllib2.install_opener(opener)def getId(i): id=int(math.floor(sheet.cell_value(i, 0))) return iddef getUserName(i): username=sheet.cell_value(i, 1) return usernamedef getPassword(i): password=sheet.cell_value(i, 2) return passworddef login(id,password): # 登录 postDict = { 'channelId' :id, 'password' : password, } postData = urllib.urlencode(postDict) req = urllib2.Request(loginUrl, postData) urllib2.urlopen(req)# 删除多余的tag标签def delTag(x): arr = [] for i in range(0, len(x)): b = str(x[i]).replace('<td>', '') c = b.replace('</td>', '') arr.append(c) return arrdef getData(url): resp=urllib2.urlopen(url) html = resp.read().decode('utf-8').encode('gbk') soup = BeautifulSoup(html, "html.parser", from_encoding="gbk") # data表示td标签的所有内容 data = soup.find_all(name="td", attrs={}, text=re.compile("\S")) data = delTag(data) return datadef writeData(id, username, password, data, writer): # 抓取数据的数组 gameName=[None]*(len(data)/4) date=[None]*(len(data)/4) account=[None]*(len(data)/4) times=[None]*(len(data)/4) for i in range(len(gameName)): gameName[i]=data[i*4+1].decode('utf-8').encode('gbk') for i in range(len(date)): date[i]=data[i*4+2].decode('utf-8').encode('gbk') for i in range(len(account)): account[i]=data[i*4+3].decode('utf-8').encode('gbk') for i in range(len(times)): times[i]=data[(i+1)*4].decode('utf-8').encode('gbk') # 实现按列写入 rowNum = len(gameName) columnNum = 7 chartData = [None]*rowNum for i in range(0, len(chartData)): chartData[i] = [None]*columnNum for i in range(0, len(gameName)): chartData[i] = [id, username, password, gameName[i], date[i], account[i], times[i]] writeCsv(chartData[i],writer)def writeContent(id,username,password,data,writer): # 若提取的用户名密码错误 if len(data)==2: writeCsv([id, username, "password_error", "error", "please", "update", "password"],writer) return # 若查询数据为空 if len(data)==3: writeCsv([id, username, password, 0, lastDayDateStr, 0, 0],writer) return writeData(id, username, password, data, writer)def toWrite(): # 遍历登录并登出 for i in range(1, nrows): id = getId(i) username = getUserName(i) password = getPassword(i) try: login(id, password) data1=getData(queryUrl1) data2=getData(queryUrl2) writeContent(id,username, password,data1,writer1) writeContent(id,username,password,data2,writer2) # 退出登录 urllib2.urlopen(logoutUrl) except Exception, e: print str(e)# 发送邮件def sendEmail(): # 创建一个带附件的实例 msg = MIMEMultipart() # 构造附件1 att1 = MIMEText(open('downjoyBackendData_'+lastDayDateStr+'_.csv', 'rb').read(), 'base64', 'gb2312') att1["Content-Type"] = 'application/octet-stream' # fileName以数据加日期命名 fileName="数据".decode('utf-8').encode('gbk')+todayStr+".csv" att1["Content-Disposition"] = 'attachment; filename=%s'%fileName msg.attach(att1) # 构造附件2 att2 = MIMEText(open(fileNameM9, 'rb').read(), 'base64', 'gb2312') att2["Content-Type"] = 'application/octet-stream' # fileName以数据加日期命名 fileName="M9数据".decode('utf-8').encode('gbk')+todayStr+".csv" att2["Content-Disposition"] = 'attachment; filename=%s'%fileName msg.attach(att2) # 写入邮件正文 text="数据见附件,如有问题,请联系ZC@chuan-mei.com" # 邮件正文乱码,所以在这里指定编码 part1 = MIMEText(text, 'plain', _charset='utf-8') msg.attach(part1) # 加邮件头 #strTo = ['shijia.ren@chuan-mei.com', 'ybb@chuan-mei.com'] strTo = ['wangxin.xie@chuan-mei.com', '1520481162@qq.com'] msg['to'] = ','.join(strTo) msg["from"] = 'datacentre@chuan-mei.com' msg['subject'] = '当乐商人每日分发数据_'.decode('utf-8')+todayStr # 发送邮件 try: server = smtplib.SMTP() # 数据中心测试时 server.connect('mail.chuan-mei.com') # 用户名,密码 server.login('datacentre@chuan-mei.com', 'DtCtre@CM-19') server.sendmail(msg['from'], strTo, msg.as_string()) server.quit() print '发送成功'.decode("utf-8") except Exception, e: print str(e)def main(): print "===%s start===%s"%(sys.argv[0], datetime.datetime.strftime(datetime.datetime.now(), "%Y-%m-%d %H:%M:%S")) writeHeader(writer1) writeHeader(writer2) toLogin() toWrite() csvfile1.close() csvfile2.close() sendEmail() print "===%s end===%s"%(sys.argv[0], datetime.datetime.strftime(datetime.datetime.now(), "%Y-%m-%d %H:%M:%S"))#################################################################################if __name__ == "__main__": main()
- 【详解】Python爬虫脚本M9优化
- 【详解】Python CH爬虫脚本
- 【详解】Python写爬虫脚本的教程
- 爬虫/脚本/Python语言- 脚本
- python之自定义爬虫脚本
- 魅族 M9 的刷机脚本代码
- python 脚本优化
- Python爬虫利器requests详解
- python爬虫中的cookie详解
- python爬虫requests库详解
- [Python脚本]——网页爬虫开始
- 我的第一个python爬虫脚本
- python爬虫脚本下载YouTube视频
- python 脚本通用优化技巧
- python爬虫学习--pixiv爬虫(4)--代码优化
- python爬虫--scrapy 框架 之 项目外运行爬虫(用脚本运行爬虫)
- Python爬虫和情感分析详解
- Python爬虫学习之Selenium详解
- MAC下的Brew
- LTE Frame Structure - Downlink
- PAT 1023. Have Fun with Numbers (20)
- Java进阶(十七)ArrayList与LinkedList的区别
- Django中的原子事务相关注意事项
- 【详解】Python爬虫脚本M9优化
- hdoj Robberies 2955 (概率Dp&&01背包) 好题
- LeetCode 292: Nim Game (Nim游戏)
- html和php编写系统登录和信息查询
- 浅谈C语言的存储区
- 叶子问题
- 关于LaunchImage的警告
- java投票系统
- linux内核Kmalloc分配内存需要注意的问题(GFP_KERNEL可能会造成内核调度错误)