Python爬蟲(5):豆瓣讀書練手爬蟲

我們在之前的文章中基本上掌握了Python爬蟲的原理和方法,不知道大家有沒有練習呢。今天我就來找一個簡單的網頁進行爬取,就當是給之前的兵書做一個實踐。不然不就是紙上談兵的趙括了嗎。

好了,我們這次的目標是豆瓣圖書Top250,地址是:https://book.douban.com/top250?start=0

準備

爬一個網頁我們至少要知道自己需要什麼信息,大家看截圖:

紅色箭頭標記的地方就是我們要獲取的信息了,包括書的名字,作者和出版社信息,豆瓣評分和一句話簡介。我們有了目標信息,就需要找到信息所在的頁面源碼,然後通過解析源碼來獲取到信息數據。那麼,我們怎樣獲得頁面 HTML 源代碼呢?翻閱兵書,我們知道可以使用requests之計。代碼實現如下:

import requests

 

resp = requests.get('https://book.douban.com/top250?start=0')

print(resp.text)

運行程序,我們就輕鬆的獲得了敵軍的 HTML 信息了。但是問題又來了,我們得到 HTML 信息後,怎樣得到我們的目標數據呢?

深夜了,一輪彎月躲在雲朵後面,窗外下着雨,我們坐在燭火前,翻閱兵書,頓時茅塞頓開,BeautifulSoup大法好。

我們打開瀏覽器,按f12到開發者工具,我們從網頁源碼裏查找到數據位置,截圖如下:

可以看到書名信息包含在class='pl2' div裏面的a標籤內,是a標籤的title屬性。發現目標位置後,就簡單多了。我們利用BeautifulSoup來獲得一個對象,按找標準的縮進顯示的html代碼:

from bs4 import BeautifulSoup

 

soup = BeautifulSoup(resp.text, 'lxml')

推薦大家使用lxml解析器,因爲他快。如果安裝lxml遇到問題的可以參考 上一篇文章 的方法。當然,如果大家怕麻煩,也完全可以使用Python的內置標準庫html.parser.對我們獲得結果並沒有影響。

開始工作

現在我們要用到BeautifulSoupfind_all()選擇器,因爲我們這一頁有很多書,而每一本書的信息都包含在class=pl2div標籤內,我們使用find_all()就可以直接得到本頁所有書的書名了。我們用find()方法和find_all()方法來做一個比較:

# find_all()方法,

# 注意class是Python關鍵詞,後面要加下劃線_:

alldiv = soup.find_all('div', class_='pl2')

for a in alldiv:

    names = a.find('a')['title']

    print('find_all():', names)

# find()方法:

alldiv2 = soup.find('div', class_='pl2')

names2 = alldiv2.find('a')['title']

print('find():', names2 )

運行結果:

find_all(): 追風箏的人

find_all(): 小王子

# ...

# ...省略部分

# ...

find_all(): 三體Ⅲ

find(): 追風箏的人

 

Process finished with exit code 0

我們通過結果就可以看到兩者之間的差距了,前者輸出了一頁的數據,而後者只輸出了第一條數據。所以包括後面的信息,由於每一天數據所在標籤是一樣的,我們都是用find_all()方法。

上面的代碼寫的優雅點,就是這樣實現,注意結果是一個 list:

# 書名, 注意是L小寫,不是阿拉伯數字1

alldiv = soup.find_all('div', class_='pl2')

names = [a.find('a')['title'] for a in alldiv]

print(names)

這樣書名數據我們就得到了,接下來是作者信息。方法和獲取書名方法一樣:

# 作者,由於信息在一個p標籤內部,

# 我們獲取到標籤直接get_text()方法獲得文本內容

allp = soup.find_all('p', class_='pl')

authors = [p.get_text() for p in allp]

運行結果:

['[美] 卡勒德·胡賽尼 / 李繼宏 / 上海人民出版社 / 2006-5 / 29.00元',

'[法] 聖埃克蘇佩裏 / 馬振聘 / 人民文學出版社 / 2003-8 / 22.00元',

'錢鍾書 / 人民文學出版社 / 1991-2 / 19.00',

'餘華 / 南海出版公司 / 1998-5 / 12.00元',

# ...

# ...省略部分結果

# ...

'高銘 / 武漢大學出版社 / 2010-2 / 29.80元',

'劉慈欣 / 重慶出版社 / 2010-11 / 38.00元']

後面的評分內容和簡介內容也是一樣獲得,只是標籤不同,但是方法一樣,具體也不需要多餘贅述。直接看實現代碼:

# 評分

starspan = soup.find_all('span', class_='rating_nums')

scores = [s.get_text() for s in starspan]

# 簡介

sumspan = soup.find_all('span', class_='inq')

sums = [i.get_text() for i in sumspan]

程序運行成功,我們就獲得了4個list,分別是書名,作者,評分和簡介內容。我們要把他們放在一起,打印出來,就是一頁的數據信息了。
這裏我們使用zip()函數,zip()函數在運算時,會以一個或多個序列做爲參數,返回一個元組的列表。同時將這些序列中並排的元素配對。

for name, author, score, sum in zip(names, authors, scores, sums):

    name = '書名:' + str(name) + '\n'

    author = '作者:' + str(author) + '\n'

    score = '評分:' + str(score) + '\n'

    sum = '簡介:' + str(sum) + '\n'

    data = name + author + score + sum

我們使用換行符’\n‘給數據信息一點整齊的樣式。我們可以查看到打印的結果,並沒有所有數據黏在一起,顯得醜陋。
獲得信息後,就是保存數據了。保存數據也很簡單,Python的文件讀寫操作就可以實現。代碼如下:

# 文件名

filename = '豆瓣圖書Top250.txt'

# 保存文件操作

with open(filename, 'w', encoding='utf-8') as f:

    # 保存數據

    f.writelines(data + '=======================' + '\n')

print('保存成功')

運行成功,我們就可以看到項目文件下面的 txt 文件了,上面保存了我們上面打印出來的內容。

書名:追風箏的人

作者:[美] 卡勒德·胡賽尼 / 李繼宏 / 上海人民出版社 / 2006-5 / 29.00

評分:8.8

簡介:爲你,千千萬萬遍

==================

# ...

# ...

書名:活着

作者:餘華 / 南海出版公司 / 1998-5 / 12.00

評分:9.1

簡介:活着本身就是人生最大的意義

==================

但是,我們要的是 250 條數據,而不是一頁的十幾條數據,那麼要怎麼獲得到所有的數據呢。我們可以檢查頁面的信息,可以看到頁面一共 10 頁,第一頁的URL是https://book.douban.com/top250?start=0。而最後一頁的 URL 是https://book.douban.com/top250?start=225
我們接着多看幾頁,第二頁是https://book.douban.com/top250?start=25,第三頁是https://book.douban.com/top250?start=50

規律已經很清晰了,我們的頁面的頁數信息是最後的start=後面的數字。而且數字從0開始到225,每一頁數字加 25.這就很簡單了,我們以https://book.douban.com/top250?start=爲基層URL,每一頁在後面加頁面的頁數數字。就可以得到所有的頁面 url 了。再以for循環迭代每一個 url,使用上面獲取數據的方法,獲得所有的數據信息。

獲取所有頁面URL的代碼如下:

base_url = 'https://book.douban.com/top250?start='

urllist = []

# 從0到225,間隔25的數組

for page in range(0, 250, 25):

    allurl = base_url + str(page)

    urllist.append(allurl)

我們把他保存在 list 裏面,好用循環迭代。

代碼

那麼,所有的功能都實現了。現在,我們只要將所有的代碼組合起來,就可以實現我們需要的所有功能了。
上代碼:

# -*- coding:utf-8 -*-

#  author: yukun

import requests

from bs4 import BeautifulSoup

 

 

# 發出請求獲得HTML源碼的函數

def get_html(url):

    # 僞裝成瀏覽器訪問

    headers = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36'}

    resp = requests.get(url, headers=headers).text

 

    return resp

 

# 解析頁面,獲得數據信息

def html_parse():

    # 調用函數,for循環迭代出所有頁面

    for url in all_page():

        # BeautifulSoup的解析

        soup = BeautifulSoup(get_html(url), 'lxml')

        # 書名

        alldiv = soup.find_all('div', class_='pl2')

        names = [a.find('a')['title'] for a in alldiv]

        # 作者

        allp = soup.find_all('p', class_='pl')

        authors = [p.get_text() for p in allp]

        # 評分

        starspan = soup.find_all('span', class_='rating_nums')

        scores = [s.get_text() for s in starspan]

        # 簡介

        sumspan = soup.find_all('span', class_='inq')

        sums = [i.get_text() for i in sumspan]

        for name, author, score, sum in zip(names, authors, scores, sums):

            name = '書名:' + str(name) + '\n'

            author = '作者:' + str(author) + '\n'

            score = '評分:' + str(score) + '\n'

            sum = '簡介:' + str(sum) + '\n'

            data = name + author + score + sum

            # 保存數據

            f.writelines(data + '=======================' + '\n')

 

# 獲得所有頁面的函數

def all_page():

    base_url = 'https://book.douban.com/top250?start='

    urllist = []

    # 從0到225,間隔25的數組

    for page in range(0, 250, 25):

        allurl = base_url + str(page)

        urllist.append(allurl)

 

    return  urllist

 

# 文件名

filename = '豆瓣圖書Top250.txt'

# 保存文件操作

f = open(filename, 'w', encoding='utf-8')

# 調用函數

html_parse()

f.close()

print('保存成功。')

我們只用了36行的代碼(去掉空行和註釋)就實現了抓取豆瓣圖書的數據了。大家是不是覺得很簡單了,不要興奮,這只是一個小白最基礎的練手項目,大家快去找更有挑戰性的項目實現吧。大家加油。

謝謝閱讀

 

原文地址:https://www.yukunweb.com/2017/6/python-spider-douban/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章