升級裝備,讓你的小爬蟲變得更能干
在上一篇文章中我們講到了如何用requests對網(wǎng)頁進行GET和POST請求,以及用BeautifulSoup對請求到的頁面進行分析。但是單單這樣,小爬蟲只能對單一的頁面進行爬取,還并不能完全解放我們的人力,只能算是個半自動的爬蟲,那么這篇文章中,我們要給這只小爬蟲再升級幾件厲害的裝備,讓它變得更能干!
任務(wù)目標(biāo)
首先,我們面前有一份長長的Excel表格,里面提供了文章的案例編號,需要爬蟲根據(jù)這份表格里面的案例編號,來進行相對應(yīng)文章的爬取。其次,我們還需要對每篇文章爬取的內(nèi)容做保存,不然關(guān)掉IDE后,數(shù)據(jù)就全部丟失了。
根據(jù)這份需求,我們需要給這只小爬蟲裝備上2件裝備
- 對Excel文件的操作
- 對文件進行讀取和寫入操作
裝備一:對Excel文件的讀取操作
第一步還是需要邀請pip來配合,通過pip來自動安裝xlrd模塊。
pip install xlrd
這兒解釋下,xlrd模塊是對Excel進行讀取操作的,寫入操作需要另外一個叫xlwt的模塊來支持。這兒我們只需要讀取操作,所以只要引用xlrd模塊就行了。

這份是我們需要導(dǎo)入讀取的Excel文件,我們要讀取里面第二列的全部案例編號,并取出來。
import xlrd #導(dǎo)入excel 模塊
# 打開EXCEL文檔獲取內(nèi)容
def read_excel():
#文件位置
ExcelFile=xlrd.open_workbook(r'C:\Users\Saber\Desktop\案例編號清單.xlsx')
#獲取目標(biāo)EXCEL文件sheet名
sheet = ExcelFile.sheet_by_name('Sheet1')
print('該Excel文檔共有' + str(sheet.nrows-1) + '篇文章需要爬取\n')
#獲取第二列的值
rows = sheet.col_values(1)
#刪除第一行的標(biāo)題
rows.pop(0)
# 清洗數(shù)據(jù),把空格的都排除掉
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里會有空格,所以獲得原始數(shù)據(jù)后要再把空格都排除掉。\xa0 是不間斷空白符。執(zhí)行一下,成功獲取到我們希望得到的數(shù)據(jù)。

裝備二:對文件進行讀取和寫入操作
完成了Excel文件的讀取,我們還要將數(shù)據(jù)進行輸出。這個需要引用一個OS模塊,這個OS模塊包含在Python的標(biāo)準(zhǔn)庫中,不需要額外的安裝。這兒我們還是拿剛才的導(dǎo)入Excel表數(shù)據(jù)做例子,將Excel表中的信息輸出到文件中并保存起來。
def read_excel():
## ......上面代碼省略......##
print('獲取案例編號成功\n')
# 以寫的方式打開ExcelData.txt
pageFile = open('ExcelData.txt','a+')
# 寫入內(nèi)容
pageFile.write('該EXCEL文檔共有' + str(sheet.nrows-1) + '篇文章需要爬取\n')
# 打開了記得還要關(guān)閉
pageFile.close()
return new_excel
在read_excel()末尾加入這么三行代碼。即可以完成文件的打開,內(nèi)容寫入,文件關(guān)閉的操作,非常簡單。效果如下圖所示。

這里要講解一下open()這個方法。這個方法里面需要傳入2個參數(shù)open("文件名","打開方式"),其中打開方式中有很多種。我在這兒選擇了a+的方式,因為他會判斷文件是否存在,不存在則新建,存在則追加數(shù)據(jù),非常適合這次的使用場景。
r 打開只讀文件,該文件必須存在。
r+ 具有讀寫屬性,從文件頭開始寫,保留原文件中沒有被覆蓋的內(nèi)容,文件必須存在。
w 打開只寫文件,若文件存在則文件長度清為0,即該文件內(nèi)容會消失。若文件不存在則建立該文件。
w+ 具有讀寫屬性,寫的時候如果文件存在,會被清空,從頭開始寫,如文件不存在則創(chuàng)建該文件。
a 以附加的方式打開只寫文件。若文件不存在,則會建立該文件,如果文件存在,寫入的數(shù)據(jù)會被加到文件尾,即文件原先的內(nèi)容會被保留。
a+ 以附加方式打開可讀寫的文件。若文件不存在,則會建立該文件,如果文件存在,寫入的數(shù)據(jù)會被加到文件尾后,即文件原先的內(nèi)容會被保留。
w+與r+區(qū)別:r+:可讀可寫,若文件不存在,報錯;w+: 可讀可寫,若文件不存在,創(chuàng)建
r+與a+區(qū)別:r+,可讀寫,文件必須文件,從文件開頭開始寫;a+,可讀寫,文件存在則在文件末尾追加寫;文件不存在則創(chuàng)建文件
更詳細(xì)的講解可以參考這篇文章:python文件操作
到此為止,小爬蟲的兩個新裝備都介紹完畢啦,我們要對爬蟲改造下,讓他能一鍵自動按照指定Excel表中的案例編號進行自動爬取,并輸出保存爬取到的內(nèi)存。
改造小爬蟲
在上面我們獲取了全部需要爬取的案例編號后,進入網(wǎng)站發(fā)現(xiàn),每篇文章的caseId編號并不是使用這個案例編號的,需要通過案例編號搜索,來獲取每篇文章的caseId,然后才能根據(jù)這個caseId來對文章和文章的評論進行訪問。那么,我們要先通過這個案例編號來獲取文章的caseId。

輸入案例編號,點擊搜索后會出現(xiàn)相對應(yīng)的文章。


然后對文章標(biāo)題進行審查元素,可以看到這是個超鏈接,文章caseId就在這個超鏈接中http://xxx.action?caseId=287270。但是經(jīng)過摸索,發(fā)現(xiàn)前面的那個input標(biāo)簽的value也帶有這個caseId,而且這個input標(biāo)簽全文只有2個,比起a標(biāo)簽來更方便解析,于是我決定取這個input標(biāo)簽中value值。

通過抓取請求,分析,這是一個POST請求。Form參數(shù)一共有6個,需要填寫的有3個,其中核心參數(shù)就是這個caseSn。
# 爬取文章鏈接
class BeautifulGetUrl():
def __init__(self,sn,header): #類的初始化操作
self.article_caseId = [] #爬取到的caseId都放到這個list中,實例化這個類后可以隨時調(diào)用
self.headers = header #給請求指定一個請求頭來模擬chrome瀏覽器
self.web_url = 'http://net.chinamobile.com/cmccnkms-case/web/casemajor/caseList.action' #要訪問的網(wǎng)頁地址
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')
#判斷是否可以搜索到文章,搜不到就把內(nèi)容置為-1
if len(find_tag) == 1:
self.article_caseId.append('-1')
print(x)
print('找不到該篇文章')
else:
self.article_caseId.append(find_tag[1]['value']) #獲取標(biāo)簽的value值
print(x)
print(find_tag[1]['value'])
來解讀一下這段代碼,這兒因為這幾個請求的header部分都是一樣的,所以精簡代碼,把header作為參數(shù)來傳入,同時也傳入之前獲取到的案例編號列表。
通過for循環(huán),將caseSn一個一個的傳入,作為form的參數(shù),然后對地址進行post請求。在BeautifulSoup中對標(biāo)簽為input,類型為checkbox進行解析,如果能搜索到這篇文章,就會有兩個tag,取第二tag的value值就是我們所需要的caseId,然后將這個追加到之前定義的article_caseId中。
這兒增加一個判斷是因為,如果該篇文章正在審核或者審核沒有通過的話,是搜索不到的。搜索不到的話,解析到checkbox的tag只有一個,我們默認(rèn)取第二個tag程序就會報錯,這個是需要避免的,所以增加了一個判斷。如果解析出來的tag只有一個,那么就說明找不到該篇文章,加入一個-1的標(biāo)識符,如果不加的話,那么article_caseId的下標(biāo)就和之前Excel獲取到的案例編號下標(biāo)對不上號了,必須要加上去,不能略過。
再來改造下最后類的實例化操作
附上完整的代碼塊
import xlrd #導(dǎo)入excel 模塊
import os #導(dǎo)入os模塊
import requests #導(dǎo)入requests 模塊
from bs4 import BeautifulSoup #導(dǎo)入BeautifulSoup 模塊
# 打開EXCEL文檔獲取內(nèi)容
def read_excel():
......
# 爬取文章caseId
class BeautifulGetCaseId():
def __init__(self,sn,header): #類的初始化操作
......
def get_caseId(self):
......
# 爬取文章案例編號和當(dāng)前周期有效閱讀數(shù)
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) #創(chuàng)建類的實例,根據(jù)Excel爬取文章caseId
beauty.get_caseId #執(zhí)行類中的方法
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該案例編號搜索不到相對應(yīng)的文章\n')
pageFile.close()#開了記得關(guān)
continue
getCaseSN = BeautifulGetCaseSNandReadNum(i,caseId,header) #創(chuàng)建類的實例,根據(jù)caseId爬取文章基礎(chǔ)信息
getData = BeautifulGetData(i,caseId,new_excel[i],header) #創(chuàng)建類的實例,根據(jù)caseId爬取文章評論信息
getCaseSN.get_data()
getData.get_data()
print('爬取具體信息成功')

非常完美,爬取成功,忍不住都想夸一下能干的自己。
后記
截止到現(xiàn)在,一個能解放人力的小爬蟲誕生了。但是現(xiàn)在還有個問題,這個爬蟲并不是自己使用,而是要交給需要的人來操作使用的,交付的人可不會這些代碼(但是他們?nèi)绻戳宋疫@個手把手系列,我相信他們也一定能熟練的調(diào)教這只小爬蟲),虛要改一些參數(shù)的話,就會變得很麻煩。那么我們就需要給這只小爬蟲做一套衣服,讓他成為一只界面友好型的小爬蟲,然后安安心心的交給他人來使用。在下篇文章中我就來講一講Python的圖形界面。