升级装备,让你的小爬虫变得更能干
在上一篇文章中我们讲到了如何用requests对网页进行GET和POST请求,以及用BeautifulSoup对请求到的页面进行分析。但是单单这样,小爬虫只能对单一的页面进行爬取,还并不能完全解放我们的人力,只能算是个半自动的爬虫,那么这篇文章中,我们要给这只小爬虫再升级几件厉害的装备,让它变得更能干!
任务目标
首先,我们面前有一份长长的Excel表格,里面提供了文章的案例编号,需要爬虫根据这份表格里面的案例编号,来进行相对应文章的爬取。其次,我们还需要对每篇文章爬取的内容做保存,不然关掉IDE后,数据就全部丢失了。
根据这份需求,我们需要给这只小爬虫装备上2件装备
- 对Excel文件的操作
- 对文件进行读取和写入操作
装备一:对Excel文件的读取操作
第一步还是需要邀请pip
来配合,通过pip
来自动安装xlrd
模块。
pip install xlrd
这儿解释下,xlrd
模块是对Excel进行读取操作的,写入操作需要另外一个叫xlwt
的模块来支持。这儿我们只需要读取操作,所以只要引用xlrd
模块就行了。
这份是我们需要导入读取的Excel文件,我们要读取里面第二列的全部案例编号,并取出来。
import xlrd #导入excel 模块
# 打开EXCEL文档获取内容
def read_excel():
#文件位置
ExcelFile=xlrd.open_workbook(r'C:\Users\Saber\Desktop\案例编号清单.xlsx')
#获取目标EXCEL文件sheet名
sheet = ExcelFile.sheet_by_name('Sheet1')
print('该Excel文档共有' + str(sheet.nrows-1) + '篇文章需要爬取\n')
#获取第二列的值
rows = sheet.col_values(1)
#删除第一行的标题
rows.pop(0)
# 清洗数据,把空格的都排除掉
new_excel = []
for a in rows:
a = a.replace(u'\xa0', u'').replace(u' ', u'')
new_excel.append(a)
print('获取案例编号成功\n')
return new_excel
因为Excel里会有空格,所以获得原始数据后要再把空格都排除掉。\xa0
是不间断空白符。执行一下,成功获取到我们希望得到的数据。
装备二:对文件进行读取和写入操作
完成了Excel文件的读取,我们还要将数据进行输出。这个需要引用一个OS模块,这个OS模块包含在Python的标准库中,不需要额外的安装。这儿我们还是拿刚才的导入Excel表数据做例子,将Excel表中的信息输出到文件中并保存起来。
def read_excel():
## ......上面代码省略......##
print('获取案例编号成功\n')
# 以写的方式打开ExcelData.txt
pageFile = open('ExcelData.txt','a+')
# 写入内容
pageFile.write('该EXCEL文档共有' + str(sheet.nrows-1) + '篇文章需要爬取\n')
# 打开了记得还要关闭
pageFile.close()
return new_excel
在read_excel()
末尾加入这么三行代码。即可以完成文件的打开,内容写入,文件关闭的操作,非常简单。效果如下图所示。
这里要讲解一下open()
这个方法。这个方法里面需要传入2个参数open("文件名","打开方式")
,其中打开方式中有很多种。我在这儿选择了a+的方式,因为他会判断文件是否存在,不存在则新建,存在则追加数据,非常适合这次的使用场景。
r 打开只读文件,该文件必须存在。
r+ 具有读写属性,从文件头开始写,保留原文件中没有被覆盖的内容,文件必须存在。
w 打开只写文件,若文件存在则文件长度清为0,即该文件内容会消失。若文件不存在则建立该文件。
w+ 具有读写属性,写的时候如果文件存在,会被清空,从头开始写,如文件不存在则创建该文件。
a 以附加的方式打开只写文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留。
a+ 以附加方式打开可读写的文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾后,即文件原先的内容会被保留。
w+与r+区别:r+:可读可写,若文件不存在,报错;w+: 可读可写,若文件不存在,创建
r+与a+区别:r+,可读写,文件必须文件,从文件开头开始写;a+,可读写,文件存在则在文件末尾追加写;文件不存在则创建文件
更详细的讲解可以参考这篇文章:python文件操作
到此为止,小爬虫的两个新装备都介绍完毕啦,我们要对爬虫改造下,让他能一键自动按照指定Excel表中的案例编号进行自动爬取,并输出保存爬取到的内存。
改造小爬虫
在上面我们获取了全部需要爬取的案例编号后,进入网站发现,每篇文章的caseId编号并不是使用这个案例编号的,需要通过案例编号搜索,来获取每篇文章的caseId,然后才能根据这个caseId来对文章和文章的评论进行访问。那么,我们要先通过这个案例编号来获取文章的caseId。
输入案例编号,点击搜索后会出现相对应的文章。
然后对文章标题进行审查元素,可以看到这是个超链接,文章caseId就在这个超链接中http://xxx.action?caseId=287270
。但是经过摸索,发现前面的那个input标签的value也带有这个caseId,而且这个input标签全文只有2个,比起a标签来更方便解析,于是我决定取这个input标签中value值。
通过抓取请求,分析,这是一个POST请求。Form参数一共有6个,需要填写的有3个,其中核心参数就是这个caseSn。
# 爬取文章链接
class BeautifulGetUrl():
def __init__(self,sn,header): #类的初始化操作
self.article_caseId = [] #爬取到的caseId都放到这个list中,实例化这个类后可以随时调用
self.headers = header #给请求指定一个请求头来模拟chrome浏览器
self.web_url = 'http://net.chinamobile.com/cmccnkms-case/web/casemajor/caseList.action' #要访问的网页地址
self.caseSn = sn
def get_caseId(self):
print('开始文章CaseID获取的POST请求')
for x in self.caseSn:
web_data = {
'pageNum':'',
'majorName':'无线',
'orderType':'ZRequestTime',
'caseTitle':'',
'caseSn':x,
'caseAuthor':''
}
r = requests.post(self.web_url,data=web_data, headers=self.headers)
find_tag = BeautifulSoup(r.text, 'lxml').find_all('input',type='checkbox')
#判断是否可以搜索到文章,搜不到就把内容置为-1
if len(find_tag) == 1:
self.article_caseId.append('-1')
print(x)
print('找不到该篇文章')
else:
self.article_caseId.append(find_tag[1]['value']) #获取标签的value值
print(x)
print(find_tag[1]['value'])
来解读一下这段代码,这儿因为这几个请求的header部分都是一样的,所以精简代码,把header作为参数来传入,同时也传入之前获取到的案例编号列表。
通过for循环,将caseSn一个一个的传入,作为form的参数,然后对地址进行post请求。在BeautifulSoup中对标签为input,类型为checkbox进行解析,如果能搜索到这篇文章,就会有两个tag,取第二tag的value值就是我们所需要的caseId,然后将这个追加到之前定义的article_caseId
中。
这儿增加一个判断是因为,如果该篇文章正在审核或者审核没有通过的话,是搜索不到的。搜索不到的话,解析到checkbox的tag只有一个,我们默认取第二个tag程序就会报错,这个是需要避免的,所以增加了一个判断。如果解析出来的tag只有一个,那么就说明找不到该篇文章,加入一个-1的标识符,如果不加的话,那么article_caseId
的下标就和之前Excel获取到的案例编号下标对不上号了,必须要加上去,不能略过。
再来改造下最后类的实例化操作
附上完整的代码块
import xlrd #导入excel 模块
import os #导入os模块
import requests #导入requests 模块
from bs4 import BeautifulSoup #导入BeautifulSoup 模块
# 打开EXCEL文档获取内容
def read_excel():
......
# 爬取文章caseId
class BeautifulGetCaseId():
def __init__(self,sn,header): #类的初始化操作
......
def get_caseId(self):
......
# 爬取文章案例编号和当前周期有效阅读数
class BeautifulGetCaseSNandReadNum():
def __init__(self,i,a,header): #类的初始化操作
......
def get_data(self):
......
# 爬取具体信息
class BeautifulGetData():
def __init__(self,i,caseId,caseSN,header): #类的初始化操作
......
def get_data(self):
......
#给请求指定一个请求头来模拟chrome浏览器
header = {
......
}
new_excel = read_excel() #从excel中获取需要的信息
print('获取案例编号成功')
beauty = BeautifulGetCaseId(new_excel,header) #创建类的实例,根据Excel爬取文章caseId
beauty.get_caseId #执行类中的方法
print('爬取文章链接成功')
for i,caseId in enumerate(beauty.article_caseId):
if caseId == '-1':
pageFile = open('ExcelData.txt','a+') #以写的方式打开ExcelData.txt
pageFile.write('这是爬取的第' + str(i+1) + '篇文章\n案例编号:' + new_excel[i] + '\n\n该案例编号搜索不到相对应的文章\n')
pageFile.close()#开了记得关
continue
getCaseSN = BeautifulGetCaseSNandReadNum(i,caseId,header) #创建类的实例,根据caseId爬取文章基础信息
getData = BeautifulGetData(i,caseId,new_excel[i],header) #创建类的实例,根据caseId爬取文章评论信息
getCaseSN.get_data()
getData.get_data()
print('爬取具体信息成功')
非常完美,爬取成功,忍不住都想夸一下能干的自己。
后记
截止到现在,一个能解放人力的小爬虫诞生了。但是现在还有个问题,这个爬虫并不是自己使用,而是要交给需要的人来操作使用的,交付的人可不会这些代码(但是他们如果看了我这个手把手系列,我相信他们也一定能熟练的调教这只小爬虫),虚要改一些参数的话,就会变得很麻烦。那么我们就需要给这只小爬虫做一套衣服,让他成为一只界面友好型的小爬虫,然后安安心心的交给他人来使用。在下篇文章中我就来讲一讲Python的图形界面。