手把手系列:用Python3+PyQt5做一個有界面的小爬蟲(三)


升級裝備,讓你的小爬蟲變得更能干

在上一篇文章中我們講到了如何用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的圖形界面。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容