本來工作上用的是Java爬蟲,但是感覺Java爬蟲太麻煩,耦合度太高,自己想搗鼓一些爬蟲demo不太友善。是以想到了Python爬蟲,使用Python爬取了一下,發現真的很友善。
一、第三方類庫安裝
Python的強大之處就在于它擁有豐富的第三方類庫,某些功能有人已經寫好了的,我們直接使用能大大簡化開發過程。
第三方庫 | 作用 | 安裝 |
---|---|---|
re | 用來進行正則比對 | |
requests | 用來發送HTTP請求 | |
requests-html | 用來發送HTTP請求 | |
BeautifulSoup | 用來從HTML或XML中提取資料 | |
pandas | 生成資料表 | |
二、擷取書名及其連結
如上圖所示,第一步需要擷取紅色方框裡面的書名及其連結位址。先寫一個擷取頁面連結名稱和連結位址的方法
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))
運作代碼後輸出結果為
注:每次運作的結果都可能會不一樣,這是因為每次通路豆瓣首頁新書速遞都會發生變化。
同理,拷貝第二本書的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']
再次運作以上代碼輸出結果
那麼,如果我想爬取很多書的話需要在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']
運作代碼後發現輸出結果就有這十本書名及其連結了
三、擷取書籍資訊
這裡假設我要爬取作者、出版年、定價、序列号、評分、内容簡介和作者簡介。在之前的代碼下方添加以下代碼(當然,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
接着把上面那三行測試代碼換為這行測試代碼
可以看到輸出結果
注:有些書籍可能沒有評分資訊。若沒有爬取到資料,有可能是豆瓣書籍資訊頁面樣式發生改變,需要自行修改代碼裡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
檔案便在該檔案夾下,打開檔案便能看到書籍資訊
在檔案夾視窗中,該檔案預設會被Excel打開,但是
utf-8
編碼的csv檔案Excel打開是亂碼的,用導入的方式指定編碼方式便能正确打開。
至此,一個爬蟲的小demo便完成了。