Python爬蟲入門 | 4 爬取豆瓣TOP250圖書

?
先來(lái)看看頁(yè)面長(zhǎng)啥樣的:https://book.douban.com/top250

?
我們將要爬取哪些信息:書名、鏈接、評(píng)分、一句話評(píng)價(jià)……

?

1. 爬取單個(gè)信息

我們先來(lái)嘗試爬取書名,利用之前的套路,還是先復(fù)制書名的xpath:

?
得到第一本書《追風(fēng)箏的人》的書名xpath如下:

//*[@id="content"]/div/div[1]/div/table[1]/tbody/tr/td[2]/div[1]/a

?
得到xpath,我們就可以按照之前的方法來(lái)嘗試一下:

?
返回的竟然是空值,這就很尷尬了。

這里需要注意,瀏覽器復(fù)制的 xpath 信息并不是完全可靠的,瀏覽器經(jīng)常會(huì)自己在里面增加多余的 tbody 標(biāo)簽,我們需要手動(dòng)把這些標(biāo)簽刪掉。

?
修改 xpath 后再來(lái)嘗試,結(jié)果如下:

切記:瀏覽器復(fù)制 xpath 不是完全可靠,看到 tbody 標(biāo)簽特別要注意。

?
分別復(fù)制《追風(fēng)箏的人》、《小王子》、《圍城》、《解憂雜貨店》的 xpath 信息進(jìn)行對(duì)比:

//*[@id="content"]/div/div[1]/div/table[1]/tbody/tr/td[2]/div[1]/a
//*[@id="content"]/div/div[1]/div/table[2]/tbody/tr/td[2]/div[1]/a
//*[@id="content"]/div/div[1]/div/table[3]/tbody/tr/td[2]/div[1]/a
//*[@id="content"]/div/div[1]/div/table[4]/tbody/tr/td[2]/div[1]/a

比較可以發(fā)現(xiàn)書名的 xpath 信息僅僅 table 后的序號(hào)不一樣,并且跟書的序號(hào)一致,于是去掉序號(hào)(去掉 tbody),我們可以得到通用的 xpath 信息:

//*[@id=“content”]/div/div[1]/div/table/tr/td[2]/div[1]/a

?
好了,我們?cè)囋嚢堰@一頁(yè)全部書名爬下來(lái):

?

2.爬取多個(gè)信息

?
分別復(fù)制《追風(fēng)箏的人》、《小王子》、《圍城》、《解憂雜貨店》評(píng)分的 xpath 信息進(jìn)行對(duì)比:

//*[@id="content"]/div/div[1]/div/table[1]/tbody/tr/td[2]/div[2]/span[2]
//*[@id="content"]/div/div[1]/div/table[2]/tbody/tr/td[2]/div[2]/span[2]
//*[@id="content"]/div/div[1]/div/table[3]/tbody/tr/td[2]/div[2]/span[2]
//*[@id="content"]/div/div[1]/div/table[4]/tbody/tr/td[2]/div[2]/span[2]

相信你已經(jīng)可以秒寫出爬取全部評(píng)分的xpath了:

//*[@id=“content”]/div/div[1]/div/table/tr/td[2]/div[2]/span[2]

把評(píng)分的xpath放入之前的代碼,運(yùn)行:

?
現(xiàn)在我們?cè)侔褧驮u(píng)分同時(shí)爬取下來(lái):

?
這里我們默認(rèn)書名和評(píng)分爬到的都是完全的、正確的信息,這種默認(rèn)一般情況沒問題,但其實(shí)是有缺陷的,如果我們某一項(xiàng)少爬或多爬了信息,那么兩種數(shù)據(jù)的量就不一樣了,從而匹配錯(cuò)誤。比如下面的例子:

書名xpath 后的@title 改為 text(),獲取的文本數(shù)量與評(píng)分?jǐn)?shù)量不一致,出現(xiàn)匹配錯(cuò)位。

?
如果我們以每本書為單位,分別取獲取對(duì)應(yīng)的信息,那肯定完全匹配

?
書名的標(biāo)簽肯定在這本書的框架內(nèi),于是我們從書名的標(biāo)簽向上找,發(fā)現(xiàn)覆蓋整本書的標(biāo)簽(左邊網(wǎng)頁(yè)會(huì)有代碼包含內(nèi)容的信息),把xpath 信息復(fù)制下來(lái):

//*[@id="content"]/div/div[1]/div/table[1]

?
我們將整本書和書名的xpath進(jìn)行對(duì)比

//*[@id=“content”]/div/div[1]/div/table[1]   #整本書
//*[@id=“content”]/div/div[1]/div/table[1]/tr/td[2]/div[1]/a   #書名
//*[@id=“content”]/div/div[1]/div/table[1]/tr/td[2]/div[2]/span[2]   #評(píng)分

?
不難發(fā)現(xiàn),書名和評(píng)分 xpath 的前半部分和整本書的 xpath 一致的,
那我們可以通過(guò)這樣寫 xpath 的方式來(lái)定位信息:

file=s.xpath(“//*[@id=“content”]/div/div[1]/div/table[1]”)
title =div.xpath(“./tr/td[2]/div[1]/a/@title”)
score=div.xpath(“./tr/td[2]/div[2]/span[2]/text()”)

?
在實(shí)際的代碼中來(lái)看一下:

?
剛剛我們爬了一本書的信息,那如何爬這個(gè)頁(yè)面所有書呢?很簡(jiǎn)單啊,把 xpath 中<table>后面定位的序號(hào)去掉就ok。

?
終于看到廬山真面目了,不過(guò),等等~

title = div.xpath("./tr/td[2]/div[1]/a/@title")[0]    
score=div.xpath("./tr/td[2]/div[2]/span[2]/text()")[0]

為什么這兩行后面多了個(gè) [0] 呢?我們之前爬出來(lái)的數(shù)據(jù)是列表,外面帶個(gè)方框,看著非常難受,列表只有一個(gè)值,對(duì)其取第一個(gè)值就OK。如果不熟悉列表的知識(shí),可以回去補(bǔ)補(bǔ)。

接下來(lái)就是按照這樣的方式多爬幾個(gè)元素啦!

?
有一個(gè)點(diǎn)需要注意的是:

num=div.xpath("./tr/td[2]/div[2]/span[3]/text()")[0].strip("(").strip().strip(")")

這行代碼用了幾個(gè) strip() 方法,()里面表示要?jiǎng)h除的內(nèi)容,strip(“(”) 表示刪除括號(hào), strip() 表示刪除空白符。

嗯,已經(jīng)把一個(gè)頁(yè)面搞定了,接下來(lái)需要,把所有頁(yè)面的信息都爬下來(lái)。

?

3.翻頁(yè),爬取所有頁(yè)面信息

先來(lái)看一下翻頁(yè)后url是如何變化的:

https://book.douban.com/top250?start=0   #第一頁(yè)
https://book.douban.com/top250?start=25   #第二頁(yè)
https://book.douban.com/top250?start=50   #第三頁(yè)

url 變化的規(guī)律很簡(jiǎn)單,只是 start=() 的數(shù)字不一樣而已,而且是以每頁(yè)25為單位,遞增25,這不正是每頁(yè)的書籍的數(shù)量嗎?于是,我們只需要寫一個(gè)循環(huán)就可以了啊。

for a in range(10):    
  url = 'https://book.douban.com/top250?start={}'.format(a*25)
  #總共10個(gè)頁(yè)面,用 a*25 保證以25為單位遞增

?
這里要強(qiáng)調(diào)一下 Python range() 函數(shù)

基本語(yǔ)法:range(start, stop, step)
start:計(jì)數(shù)從 start 開始。默認(rèn)是從 0 開始。例如 range(5) 等價(jià)于range(0,5);
end:計(jì)數(shù)到 end 結(jié)束,但不包括 end。例如:range(0,5)是 [0,1,2,3,4] 沒有5
step:步長(zhǎng),默認(rèn)為1。例如:range(0,5) 等價(jià)于 range(0,5,1)

>>>range(10)    #從 0 開始到 10 (不包含)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 

>>> range(1, 11)    #從 1 開始到 11 (不包含)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 

>>> range(0, 30, 5)    #從0到30(不包含),步長(zhǎng)為5 
[0, 5, 10, 15, 20, 25]

?
加上循環(huán)之后,完整代碼如下:

from lxml import etree
import requests
import time

for a in range(10):
    url = 'https://book.douban.com/top250?start={}'.format(a*25)
    data = requests.get(url).text

    s=etree.HTML(data)
    file=s.xpath('//*[@id="content"]/div/div[1]/div/table')
    time.sleep(3)

    for div in file:
        title = div.xpath("./tr/td[2]/div[1]/a/@title")[0]
        href = div.xpath("./tr/td[2]/div[1]/a/@href")[0]
        score=div.xpath("./tr/td[2]/div[2]/span[2]/text()")[0]
        num=div.xpath("./tr/td[2]/div[2]/span[3]/text()")[0].strip("(").strip().strip(")").strip()
        scrible=div.xpath("./tr/td[2]/p[2]/span/text()")

        if len(scrible) > 0:
            print("{},{},{},{},{}\n".format(title,href,score,num,scrible[0]))
        else:
            print("{},{},{},{}\n".format(title,href,score,num))

?
來(lái)運(yùn)行一下:

請(qǐng)務(wù)必要自己練習(xí)幾遍,你覺得自己看懂了,還是會(huì)出錯(cuò),不信我們賭五毛錢。

Python 的基礎(chǔ)語(yǔ)法很重要,沒事的時(shí)候多去看看:字符串、列表、字典、元組、條件語(yǔ)句、循環(huán)語(yǔ)句……

編程最重要的是實(shí)戰(zhàn),比如你已經(jīng)能夠爬TOP250的圖書了,去試試TOP250電影呢。

好了,這節(jié)課就到這里!


?
下節(jié)預(yù)告:Python爬蟲入門 | 5 爬蟲必備Python知識(shí)

完整7節(jié)課程目錄:
Python爬蟲入門 | 1 Python環(huán)境的安裝
Python爬蟲入門 | 2 爬取豆瓣電影信息
Python爬蟲入門 | 3 爬蟲必備Python知識(shí)
Python爬蟲入門 | 4 爬取豆瓣TOP250圖書信息
Python爬蟲入門 | 5 爬取小豬短租租房信息
Python爬蟲入門 | 6 將爬回來(lái)的數(shù)據(jù)存到本地
Python爬蟲入門 | 7 分類爬取豆瓣電影,解決動(dòng)態(tài)加載問題

最后編輯于
?著作權(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),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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