天天看點

一個Python爬取豆瓣書籍資訊的例子

  本來工作上用的是Java爬蟲,但是感覺Java爬蟲太麻煩,耦合度太高,自己想搗鼓一些爬蟲demo不太友善。是以想到了Python爬蟲,使用Python爬取了一下,發現真的很友善。

一、第三方類庫安裝

  Python的強大之處就在于它擁有豐富的第三方類庫,某些功能有人已經寫好了的,我們直接使用能大大簡化開發過程。

第三方庫 作用 安裝
re 用來進行正則比對

pip3 install re

requests 用來發送HTTP請求

pip3 install requests

requests-html 用來發送HTTP請求

pip3 install requests-html

BeautifulSoup 用來從HTML或XML中提取資料

pip3 install beautifulsoup4

pandas 生成資料表

pip3 install pandas

二、擷取書名及其連結

一個Python爬取豆瓣書籍資訊的例子

  如上圖所示,第一步需要擷取紅色方框裡面的書名及其連結位址。先寫一個擷取頁面連結名稱和連結位址的方法

from requests_html import HTMLSession

headers = {'User-Agent': 'User-Agent:Mozilla/5.0'}


def get_text_link_from_sel(url, sels=None):
    """
    根據選擇器傳回文字和其對應的連結
    :param url:  要爬取的url
    :param sels: 多組selector
    :return:
    """
    if sels is None:
        sels = []
    session = HTMLSession()
    r = session.get(url, headers=headers)
    my_list = []
    try:
        for sel in sels:
            the_results = r.html.find(sel)

            for result in the_results:
                my_text = result.text
                my_link = list(result.absolute_links)[0]
                my_list.append((my_text, my_link))

        return my_list
    except:
        return None

           

然後在浏覽器上按F12進入開發人員工具,如上圖所示拷貝第一本書的selector為

#content > div > div.article > div.section.books-express > div.bd > div > div > ul:nth-child(2) > li:nth-child(1) > div.info > div.title > a
           

然後測試一下該方法。在上面代碼的下面添加以下代碼

url = 'https://book.douban.com'
sels = ['#content > div > div.article > div.section.books-express > div.bd > div > div > ul:nth-child(2) > li:nth-child(1) > div.info > div.title > a']
print(get_text_link_from_sel(url, sels))
           

運作代碼後輸出結果為

一個Python爬取豆瓣書籍資訊的例子

注:每次運作的結果都可能會不一樣,這是因為每次通路豆瓣首頁新書速遞都會發生變化。

同理,拷貝第二本書的selector加入到sels裡,修改sels如下

sels = ['#content > div > div.article > div.section.books-express > div.bd > div > div > ul:nth-child(2) > li:nth-child(1) > div.info > div.title > a', '#content > div > div.article > div.section.books-express > div.bd > div > div > ul:nth-child(2) > li:nth-child(2) > div.info > div.title > a']
           

再次運作以上代碼輸出結果

一個Python爬取豆瓣書籍資訊的例子

那麼,如果我想爬取很多書的話需要在sels裡填寫很多selector嗎?當然不是。我們仔細觀察一下第一本書的selector和第二本書的selector,發現它們的差別在

li:nth-child(1)

li:nth-child(2)

,那麼我把

:nth-child(1)

去掉如何,修改sels如下

sels = ['#content > div > div.article > div.section.books-express > div.bd > div > div > ul:nth-child(2) > li > div.info > div.title > a']
           

運作代碼後發現輸出結果就有這十本書名及其連結了

一個Python爬取豆瓣書籍資訊的例子

三、擷取書籍資訊

一個Python爬取豆瓣書籍資訊的例子

  這裡假設我要爬取作者、出版年、定價、序列号、評分、内容簡介和作者簡介。在之前的代碼下方添加以下代碼(當然,import的内容可以放到最上方):

import re
import requests
from bs4 import BeautifulSoup

def crawl_book_info(url):
    results = []
    temp_results = []
    author = ''
    public_time = ''
    price = ''
    serial = ''
    score = ''
    book_desc = ''
    author_desc = ''
    print("GET " + url)
    response = requests.get(url, headers=headers)
    content = response.content.decode("utf-8")
    soup = BeautifulSoup(content, 'html.parser')

    temp = soup.select("#info")
    pattern1 = re.compile(r'出版年:([\s\d-]+)')
    pattern2 = re.compile(r'定價:([\s\d.]+)')
    pattern3 = re.compile(r'ISBN:([\s\d.]+)')
    pattern4 = re.compile(r'作者:\s*([\u4E00-\u9FA5/•\[\]\s\w\W]+)出版社')
    if temp:
        author = re.findall(pattern4, temp[0].text)[0]
        public_time = re.findall(pattern1, temp[0].text)[0]
        price_temp = re.findall(pattern2, temp[0].text)
        if price_temp:
            price = price_temp[0]
        serial = re.findall(pattern3, temp[0].text)[0]

    temp = soup.select("#interest_sectl > div > div.rating_self.clearfix > strong")
    if temp:
        score = temp[0].text

    temp = soup.select("div .intro")
    if temp:
        author_desc = temp[1].text
        book_desc = temp[0].text

    temp_results.append(author)
    temp_results.append(public_time)
    temp_results.append(price)
    temp_results.append(serial)
    temp_results.append(score)
    temp_results.append(book_desc)
    temp_results.append(author_desc)
    for result in temp_results:
        results.append(result.replace("\n", "").replace(" ", ""))
    print(results)
    return results
           

接着把上面那三行測試代碼換為這行測試代碼

可以看到輸出結果

一個Python爬取豆瓣書籍資訊的例子

注:有些書籍可能沒有評分資訊。若沒有爬取到資料,有可能是豆瓣書籍資訊頁面樣式發生改變,需要自行修改代碼裡select方法内容和正規表達式

四、爬取書籍資訊并儲存到檔案

  在上面兩個方法都成功之後,接下來添加爬蟲主方法,用來篩選出書籍名稱及其資訊頁面連結後,進而爬取資料儲存到檔案。删掉那一行測試代碼,在之前的代碼下方添加以下方法:

import time
import pandas as pd

def crawl():
    url = "https://book.douban.com"
    my_sels = ['#content > div > div.article > div.section.books-express > div.bd > div > div > ul:nth-child(2) > li > div.info > div.title > a']
    my_list = get_text_link_from_sel(url, my_sels)

    results = []

    for name_to_url in my_list:
        info = crawl_book_info(name_to_url[1])
        info.insert(1, name_to_url[0])
        results.append(info)
        time.sleep(1)

    df = pd.DataFrame(results)
    df.columns = ['book_name', 'author', 'public_time', 'price', 'serial', 'score', 'book_desc', 'author_desc']
    # gbk用Excel打開時才不會亂碼
    df.to_csv('files/豆瓣圖書.csv', encoding='utf-8', index=False)
    
if __name__ == '__main__':
    crawl()
           

運作代碼,待到控制台輸出停止時,項目下會被自動建立一個files檔案夾,

豆瓣圖書.csv

檔案便在該檔案夾下,打開檔案便能看到書籍資訊

一個Python爬取豆瓣書籍資訊的例子

在檔案夾視窗中,該檔案預設會被Excel打開,但是

utf-8

編碼的csv檔案Excel打開是亂碼的,用導入的方式指定編碼方式便能正确打開。

至此,一個爬蟲的小demo便完成了。