【详解】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()









0 0
原创粉丝点击