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


寫出你的第一個(gè)小爬蟲

在上一篇文章中對(duì)Python有了一定的基礎(chǔ)學(xué)習(xí)后,我們現(xiàn)在要開始對(duì)網(wǎng)頁進(jìn)行爬取啦。

這次我們要爬取的網(wǎng)站是中國移動(dòng)集團(tuán)的運(yùn)維案例文章和評(píng)論內(nèi)容。這篇文章中將會(huì)涉及到GET請(qǐng)求和POST請(qǐng)求,以及BeautifulSoup的使用。

擴(kuò)展模塊的安裝

要讓python可以對(duì)網(wǎng)頁發(fā)起請(qǐng)求,那就需要用到requests之類的包。我們可以用命令行來下載安裝,打開cmd,直接輸入命令就會(huì)自動(dòng)下載安裝,非常的方便。

pip install requests

既然用到了pip,那就順便解釋一下這個(gè)東東。

pip 是 Python 著名的包管理工具,在 Python 開發(fā)中必不可少。一般來說當(dāng)你安裝完python后,pip也會(huì)自動(dòng)安裝完畢,可以直接享用,十分鮮美。

附上一些常用的pip命令

# 查看pip信息
pip –version

#升級(jí)pip
pip install -U pip

# 查看已經(jīng)安裝的包
pip list

# 安裝包
Pip install PackageName

# 卸載已經(jīng)安裝好的包
Pip uninstall PackageName

以上這四個(gè)命令非常的實(shí)用,我在做這個(gè)爬蟲期間有多次使用到。

在安裝完requests包后,還需要再安裝一個(gè)神器BeautifulSoup,配合lxml庫,這是最近非常流行的兩個(gè)庫,這個(gè)是用來解析網(wǎng)頁的,并提供定位內(nèi)容的便捷接口,API非常人性化,支持CSS選擇器、Python標(biāo)準(zhǔn)庫中的HTML解析器,也支持 lxml 的 XML解析器

語法使用類似于XPath。通過官方的文檔的閱讀,很容易上手。

安裝方式:

  • BeautifulSoup的安裝
pip install beautifulsoup4
  • lxml的安裝
pip install lxml

不報(bào)錯(cuò)即為安裝成功

BeautifulSoup官方文檔傳送門

安裝完上面三個(gè)包后,就開始制作我們的第一個(gè)小爬蟲吧

我們先來分析一下我們這次要爬取數(shù)據(jù)的網(wǎng)站數(shù)據(jù)。可以使用chrome瀏覽器自帶的工具來抓包,按F12,選擇Network,就可以看到所有的請(qǐng)求內(nèi)容了。當(dāng)然也可以用Fiddler這個(gè)優(yōu)秀的工具來抓包,還能對(duì)請(qǐng)求進(jìn)行截獲并修改參數(shù),大家有時(shí)間的話可以去玩一玩。這兒因?yàn)榉奖悖揖筒捎昧薱hrome瀏覽器自帶的來進(jìn)行分析了。

根據(jù)這個(gè)請(qǐng)求,我們能看出這個(gè)是一個(gè)GET請(qǐng)求。我們將headers里的內(nèi)容綁定到類的屬性里,接著綁定一個(gè)請(qǐng)求的地址。

import requests  #導(dǎo)入requests 模塊
from bs4 import BeautifulSoup  #導(dǎo)入BeautifulSoup 模塊

# 爬取文章案例編號(hào)和當(dāng)前周期有效閱讀數(shù)
class BeautifulGetCaseSN():
    def __init__(self):  #類的初始化操作
        #頭部信息
        self.headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.221 Safari/537.36 SE 2.X MetaSr 1.0',
            'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
            'Accept-Language':'zh-CN,zh;q=0.8',
            'Connection':'keep-alive',
            'Cookie':'JSESSIONID=48A82C60BE65ED14C5978297C03AF776; PHPSESSID=ST-10-QybrBt31XVfPBqKAT2jr'
        }  
        #要訪問的網(wǎng)頁地址
        self.web_url = 'http://net.chinamobile.com/cmccnkms-case/web/casesPortal/getCaseInfor.action?caseId=75058'  

接下來我們?cè)賮矸治鲆幌乱廊〉膬?nèi)容,下圖中的三個(gè)框是我們要爬取的目標(biāo)內(nèi)容,分別是標(biāo)題,當(dāng)前周期有效閱讀數(shù),案例編號(hào)。

分別右擊審查元素,分析一下HTML的結(jié)構(gòu),以方便用BeautifulSoup來解析。

看了一遍這個(gè)網(wǎng)頁的HTML,發(fā)現(xiàn)寫這個(gè)網(wǎng)站的人真的是隨意發(fā)揮,哈哈哈,都是直接用標(biāo)簽對(duì)的,標(biāo)簽class屬性或者id屬性幾乎都沒有,還好我們現(xiàn)在有了神器Beautifulsoup再手,根本不用愁無從下手這種的事兒。

通過分析,我們發(fā)現(xiàn)標(biāo)題和當(dāng)前有效周期的父級(jí)標(biāo)簽都是<td width=“78%”>,而且我搜索了下,發(fā)現(xiàn)width=“78%”屬性只有這兩個(gè)標(biāo)簽有。那么好辦了,利用Beautifulsoup能對(duì)CSS屬性解析的特性,我們就從這個(gè)屬性下手。接著通過對(duì)案例編號(hào)的分析,我們發(fā)現(xiàn)這個(gè)<td class=“txleft”>標(biāo)簽有一個(gè)class屬性,那就根據(jù)這個(gè)屬性進(jìn)行獲取。

def get_data(self):
    print('開始文章基礎(chǔ)信息GET請(qǐng)求')
    r = requests.get(self.web_url, headers=self.headers)
    all_soup = BeautifulSoup(r.text, 'lxml')

    caseSn = all_soup.find_all('td','txleft') #案例編號(hào)抓取
    print(caseSn[2].text)

    all_a = all_soup.find_all('td',width='78%')
    title = all_a[0].find('h4').text #標(biāo)題抓取
    print(title)

    read_num = all_a[1].find_all('span')
    print(read_num[3].text) #有效閱讀數(shù)抓取

解釋一下這段代碼:

r = requests.get(self.web_url, headers=self.headers)
all_soup = BeautifulSoup(r.text, 'lxml')

對(duì)網(wǎng)站進(jìn)行GET請(qǐng)求,GET請(qǐng)求需要的參數(shù)有請(qǐng)求地址和頭部信息,然后將獲取的文本放入BeautifulSoup進(jìn)行解析。這兒順帶說一句,python語言真的是人生苦短啊,請(qǐng)求網(wǎng)址,解析網(wǎng)頁2句話就能完成,簡潔的不得了,當(dāng)然這個(gè)BeautifulSoup我為了邏輯清楚分開寫了,不然也是能用一句代碼來完成的。

caseSn = all_soup.find_all('td','txleft') #案例編號(hào)抓取
print(caseSn[2].text)

解析獲取所有class類名為txleft的td標(biāo)簽,然后發(fā)現(xiàn)我們需要的案例編號(hào)是在第三個(gè)tag中,獲取這個(gè)tag的文本內(nèi)容。

all_a = all_soup.find_all('td',width='78%')
title = all_a[0].find('h4').text #標(biāo)題抓取
print(title)

read_num = all_a[1].find_all('span')
print(read_num[3].text) #有效閱讀數(shù)抓取

通過打斷點(diǎn)的方式,我們可以看到解析獲取所有寬度屬性為78%的td標(biāo)簽,一共有2個(gè)tag集,再在第1個(gè)標(biāo)簽集中解析獲取為h4的標(biāo)簽,這個(gè)全文只存在唯一的一個(gè),所以就獲取到了我們所需要的文章標(biāo)題。有效閱讀數(shù)獲取同理,在第2個(gè)標(biāo)簽集繼續(xù)中解析獲取叫span的標(biāo)簽,有效閱讀數(shù)的內(nèi)容藏在第四個(gè)span標(biāo)簽中。

關(guān)于BeautifulSoup的find_all()find()的官方使用說明:

find_all(name, attrs, recursive, text, **kwargs)
find_all()方法搜索當(dāng)前tag的所有tag子節(jié)點(diǎn),并判斷是否符合過濾器的條件.

find( name , attrs , recursive , text , **kwargs )
find_all() 方法將返回文檔中符合條件的所有tag,盡管有時(shí)候我們只想得到一個(gè)結(jié)果.比如文檔中只有一個(gè)<body>標(biāo)簽,那么使用 find_all() 方法來查找<body>標(biāo)簽就不太合適, 使用 find_all 方法并設(shè)置 limit=1 參數(shù)不如直接使用 find() 方法.

現(xiàn)在讓我們來實(shí)例化一個(gè)爬蟲,滿懷憧憬的按下F5,讓他跑起來。

getCaseSN = BeautifulGetCaseSN()  #創(chuàng)建類的實(shí)例,根據(jù)caseId爬取文章基礎(chǔ)信息
getCaseSN.get_data()
print('爬取具體信息over')

在調(diào)試控制臺(tái)里查看結(jié)果。
哇!爬取數(shù)據(jù)成功!第一個(gè)小爬蟲誕生了。

然鵝!現(xiàn)在還不能開始慶祝,畢竟任務(wù)才進(jìn)行到一半。接下來,我們根據(jù)需求,還需要爬取文章的評(píng)論者的名字做爬取統(tǒng)計(jì)。繼續(xù)對(duì)網(wǎng)頁進(jìn)行分析。

需要對(duì)上圖中的評(píng)論者姓名進(jìn)行爬取,而且還需要做到翻頁。

通過對(duì)請(qǐng)求的分析,發(fā)現(xiàn)這個(gè)評(píng)論塊是個(gè)獨(dú)立的請(qǐng)求,然后加入到文章頁面的<frame>標(biāo)簽塊中。點(diǎn)進(jìn)去發(fā)現(xiàn)這是個(gè)POST請(qǐng)求,帶有Form數(shù)據(jù)。通過對(duì)數(shù)據(jù)分析,發(fā)現(xiàn)有3個(gè)元素,第一個(gè)是評(píng)論的排序方式,我們不用動(dòng)他,第二個(gè)是頁碼,就是翻頁的關(guān)鍵參數(shù),第三個(gè)是文章的Id。

開始構(gòu)建POST請(qǐng)求

# 爬取具體信息
class BeautifulGetData():
    def __init__(self):  #類的初始化操作
        self.headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.221 Safari/537.36 SE 2.X MetaSr 1.0',
            'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
            'Accept-Language':'zh-CN,zh;q=0.8',
            'Connection':'keep-alive',
            'Cookie':'JSESSIONID=48A82C60BE65ED14C5978297C03AF776; PHPSESSID=ST-10-QybrBt31XVfPBqKAT2jr''
        }
        #要訪問的網(wǎng)頁地址
        self.web_url = 'http://net.chinamobile.com/cmccnkms-case/web/casesPortal/loadComments.action'  

    def get_data(self):
        print('開始文章評(píng)論內(nèi)容POST請(qǐng)求')
        print('具體評(píng)論獲取\n')
        for x in range(1,10):
            #post請(qǐng)求的數(shù)據(jù)
            web_data = {
                'sort':'desc',
                'pageNum':x,
                'caseId':75058
            }
            r = requests.post(self.web_url,data=web_data, headers=self.headers)

這里增加一個(gè)for循環(huán)就是為了模擬請(qǐng)求的頁碼,根據(jù)pageNum的不同,對(duì)該網(wǎng)址進(jìn)行多次請(qǐng)求獲取不同頁面信息。

通過分析評(píng)論頁的HTML數(shù)據(jù),我發(fā)現(xiàn)每個(gè)評(píng)論都用<div class=“month”>包含,于是我們可以用find_all來獲取全部的這個(gè)class,因?yàn)槊宽摱加形鍌€(gè)評(píng)論,所以可以用for循環(huán)來進(jìn)行分析并輸出。

下面是完整的請(qǐng)求代碼

def get_data(self):
        print('開始文章評(píng)論內(nèi)容POST請(qǐng)求')
        print('具體評(píng)論獲取\n')
        get_name = []
        get_next_name = []
        for x in range(1,10):
            web_data = {
                'sort':'desc',
                'pageNum':x,
                'caseId':75058
            }
            r = requests.post(self.web_url,data=web_data, headers=self.headers)
            all_a = BeautifulSoup(r.text, 'lxml').find_all('div','month')
            print('第',x,'頁')            
            
            #將上頁獲取的評(píng)論記錄并清空當(dāng)前頁
            get_name = get_next_name
            get_next_name = []

            for a in enumerate(all_a):                 
                str_name = a[1].text.split(':') #對(duì)獲取的文本內(nèi)容做切割
                get_next_name.append(str_name[0]) #將名字加入到當(dāng)前獲取記錄中
            
            if get_name == get_next_name:
                print('完成')
                break
            else:
                for a in get_next_name:
                    print(a)

這里說一下我定義的兩個(gè)list:get_nameget_next_name。之所以定義這兩個(gè)是因?yàn)槊科恼碌脑u(píng)論數(shù)量我是不知道的,所以我不能直接控制需要爬取的頁數(shù),只能盡可能大的寫一個(gè)數(shù)。但是當(dāng)頁數(shù)小于我設(shè)定的頁碼值后會(huì)發(fā)生如下的數(shù)據(jù)重復(fù)顯示事件。

于是我加入了這兩個(gè)參數(shù),來存放前一頁的獲取的數(shù)據(jù),如果單頁獲取的數(shù)據(jù)與前一頁獲取的數(shù)據(jù)相同,那說明就是到了評(píng)論的最后一頁,直接跳出循環(huán),結(jié)束該篇文章的評(píng)論爬取。

好了,把這兩個(gè)類實(shí)例化一下,然后開始run起來吧。

getCaseSN = BeautifulGetCaseSN()  #創(chuàng)建類的實(shí)例,根據(jù)caseId爬取文章基礎(chǔ)信息
getData = BeautifulGetData()  #創(chuàng)建類的實(shí)例,根據(jù)caseId爬取文章評(píng)論信息
getCaseSN.get_data()
getData.get_data()

成功獲取到了我希望得到的目標(biāo)數(shù)據(jù),完美!

后記

但是,只對(duì)一篇固定文章的爬取,遠(yuǎn)遠(yuǎn)不是我的最終目的,我的目的是,導(dǎo)入一份需要爬取的表格,然后進(jìn)行自動(dòng)的爬取,并將獲取的數(shù)據(jù)輸出并保存下來。在下一篇文章中,我就來講一講,Excel文件的導(dǎo)入讀取與文件的導(dǎo)出保存。

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

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

  • 升級(jí)裝備,讓你的小爬蟲變得更能干 在上一篇文章中我們講到了如何用requests對(duì)網(wǎng)頁進(jìn)行GET和POST請(qǐng)求,以...
    杳杳靈鳯閱讀 3,839評(píng)論 0 6
  • Python爬蟲入門(urllib+Beautifulsoup) 本文包括:1、爬蟲簡單介紹2、爬蟲架構(gòu)三大模塊3...
    廖少少閱讀 10,083評(píng)論 0 6
  • 聲明:本文講解的實(shí)戰(zhàn)內(nèi)容,均僅用于學(xué)習(xí)交流,請(qǐng)勿用于任何商業(yè)用途! 一、前言 強(qiáng)烈建議:請(qǐng)?jiān)陔娔X的陪同下,閱讀本文...
    Bruce_Szh閱讀 13,004評(píng)論 6 28
  • 上網(wǎng)原理 1、爬蟲概念 爬蟲是什麼? 蜘蛛,蛆,代碼中,就是寫了一段代碼,代碼的功能從互聯(lián)網(wǎng)中提取數(shù)據(jù) 互聯(lián)網(wǎng): ...
    riverstation閱讀 8,625評(píng)論 1 2
  • 關(guān)于毫州警方發(fā)布的這個(gè)圖片視頻,獲贊二百余萬,乍一看屬于“弘揚(yáng)正能量,共筑中國夢(mèng)”引導(dǎo)式宣傳題材,但細(xì)想心中卻有股...
    二兩觀點(diǎn)閱讀 1,270評(píng)論 1 2

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