我們在之前的文章中基本上掌握了Python
爬蟲的原理和方法,不知道大家有沒有練習呢。今天我就來找一個簡單的網頁進行爬取,就當是給之前的兵書做一個實踐。不然不就是紙上談兵的趙括了嗎。
好了,我們這次的目標是豆瓣圖書Top250
,地址是:https://book.douban.com/top250?start=0
準備
爬一個網頁我們至少要知道自己需要什麼信息,大家看截圖:
紅色箭頭標記的地方就是我們要獲取的信息了,包括書的名字,作者和出版社信息,豆瓣評分和一句話簡介。我們有了目標信息,就需要找到信息所在的頁面源碼,然後通過解析源碼來獲取到信息數據。那麼,我們怎樣獲得頁面 HTML 源代碼呢?翻閱兵書,我們知道可以使用requests
之計。代碼實現如下:
運行程序,我們就輕鬆的獲得了敵軍的 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
.對我們獲得結果並沒有影響。
開始工作
現在我們要用到BeautifulSoup
的find_all()
選擇器,因爲我們這一頁有很多書,而每一本書的信息都包含在class=pl2
的div
標籤內,我們使用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的代碼如下:
我們把他保存在 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/