requests 获取div_爬虫系列第五篇 使用requests与BeautifulSoup爬取豆瓣图书Top250
上一篇我們學習了BeautifulSoup的基本用法,本節我們使用它來爬取豆瓣圖書Top250。
一、網頁分析
我們爬取的網頁的url是https://book.douban.com/top250?icn=index-book250-all。首頁如圖
與豆瓣電影Top250差不多,將頁面拉到最底部,可以看到分頁列表
并且每一頁的url也是以25遞增,所以爬取思路與豆瓣電影Top250一致。
二、爬取目標
我們本篇要爬取的信息包括書名、作者、出版社、價格、評分、推薦語。
三、爬取首頁
- 網頁獲取源代碼
輸出結果
- 解析提取所需信息
如圖,查看源代碼我們可以知道頁面中書的信息均包含在一個個<table>標簽中,所以我們可以先用CSS選擇器將一個個<table>節點選出來,然后在使用循環提取每一本書的信息。提取<table>節點的代碼如下:
def parse_html(html):soup = BeautifulSoup(html,'lxml')books = soup.select('div.article div.indent table')print(books)運行結果如下:
可以看到輸出為列表,并且第一個元素包含《追風箏的人》的相關信息。這里我們使用BeautifulSoup中的select()加CSS選擇器提取<table>節點。傳入的CSS選擇器為:div.article div.indent table。其中div.article的意思為選擇包含屬性class="article"的<div>標簽,然后跟空格代表嵌套關系,表示接著選擇該<div>下的包含class="indent"的<div>標簽,再跟空格表示接著嵌套,繼續選擇第二個<div>標簽下的<table>標簽。
接下來,對選出來的<table>標簽循環,在每一個<table>標簽中去提取圖書信息。這里我們先提出書名信息,先看一種寫法,代碼如下:
def parse_html(html):soup = BeautifulSoup(html,'lxml')books = soup.select('div.article div.indent table')for book in books:title = book.div.a.stringprint(title)輸出結果
可以看到確實獲取到了書名信息,但是有些書的書名沒有得到,返回了None,這就不是很完美了呀。我們先解釋寫這里獲取標題的方法,這里我們使用了節點選擇器的嵌套選擇:首先選擇了<div>標簽,然后繼續選擇其下的<a>標簽。為什么不直接選擇<a>標簽呢,因為包含書名信息的<a>標簽是<table>節點下的第二個<a>標簽,直接選擇<a>只會選擇第一個不包含書名信息的那個<a>標簽。下面我們來研究下為什么使用string屬性不能獲取某些書的書名信息,我們先將獲取到的<a>標簽打印出來。
def parse_html(html):soup = BeautifulSoup(html,'lxml')books = soup.select('div.article div.indent table')for book in books:title = book.div.aprint(title)并截取返回None的那本書的位置
可以看到《三體》這本書的<a>標簽的內部結構不同,所以導致調用string屬性返回None,但是我們可以注意到每條<a>標簽都包含title屬性,我們是否可以通過title屬性獲取書名?
def parse_html(html):soup = BeautifulSoup(html,'lxml')books = soup.select('div.article div.indent table')for book in books:title = book.div.a['title']print(title)輸出結果如下:
可以看到不僅獲取到了想要的信息,而數據更加干凈。這里獲取title部分說了這么多主要是想告訴大家一個獲取相同的信息有很多的方法,當一種獲取方式不理想時可以考慮換一種思路。
接下來我們一次性將所有的信息抓取下來。
def parse_html(html):soup = BeautifulSoup(html,'lxml')tables = soup.select('div.article div.indent table')books = []for table in tables:title = table.div.a['title']'''由于information中包含多個信息,某些書與大多數書的信息格式不一致在進行列表索引的時候非常容易引起IndexError異常,為了保證爬蟲的健壯性我們對該異常進行處理'''information = table.p.stringinformations = information.split('/')while(len(informations)>4):del informations[1]try:author = informations[0]press = informations[1]date = informations[2]price = informations[3]except IndexError:continue'''像這樣子進行數據提取很容易遇到某一個特殊部分的網頁結構與大部分的不一樣,這會導致首頁能抓取到的節點,在該部分會返回None,從而導致調用string屬性產生AttributeError異常,我們需要進行異常處理'''try:score = table.find(attrs={'class':'rating_nums'}).stringrecommendation = table.find(attrs={'class':'inq'}).stringexcept AttributeError:continuebook = {'書名':title,'作者':author,'出版社':press,'出版日期':date,'價格':price,'評分':score,'推薦語':recommendation}books.append(book)return books輸出結果:
這里可以看到輸出結果雖然為字典,但是不好看。保存為字典格式只是方便存儲與后續使用,假如我們要將其打印到屏幕上的話,并不好看,所以我們接著寫一個打印函數,專門用于輸出:
def print_(books):for book in books:print('*'*50)for key,value in zip(book.keys(),book.values()):print(key+':'+value)print('*'*50)我們將爬取首頁的代碼匯總在一起,看看輸出效果
import requests from bs4 import BeautifulSoupdef get_html(url):headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'}html = requests.get(url,headers=headers)return html.textdef parse_html(html):soup = BeautifulSoup(html,'lxml')tables = soup.select('div.article div.indent table')books = []for table in tables:title = table.div.a['title']'''由于information中包含多個信息,某些書與大多數書的信息格式不一致在進行列表索引的時候非常容易引起IndexError異常,為了保證爬蟲的健壯性我們對該異常進行處理'''information = table.p.stringinformations = information.split('/')while(len(informations)>4):del informations[1]try:author = informations[0]press = informations[1]date = informations[2]price = informations[3]except IndexError:continue'''像這樣子進行數據提取很容易遇到某一個特殊部分的網頁結構與大部分的不一樣,這會導致首頁能抓取到的節點,在該部分會返回None,從而導致調用string屬性產生AttributeError異常,我們需要進行異常處理'''try:score = table.find(attrs={'class':'rating_nums'}).stringrecommendation = table.find(attrs={'class':'inq'}).stringexcept AttributeError:continuebook = {'書名':title,'作者':author,'出版社':press,'出版日期':date,'價格':price,'評分':score,'推薦語':recommendation}books.append(book)return booksdef print_(books):for book in books:print('*'*50)for key,value in zip(book.keys(),book.values()):print(key+':'+value)print('*'*50)if __name__ == '__main__':url = 'https://book.douban.com/top250?start=0'html = get_html(url)books = parse_html(html)print_(books)輸出結果:
注意,這里我們沒有寫存儲的相關函數,因為這里只為演示BeautifulSoup的用法,假如需要存儲數據參考爬蟲系列第三篇 使用requests與正則表達式爬取豆瓣電影Top250
四、爬取整個豆瓣圖書Top250
與前面個爬蟲實例一樣,構造url列表,使用循環即可。全部代碼如下
import requests from bs4 import BeautifulSoupdef get_html(url):headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'}html = requests.get(url,headers=headers)return html.textdef parse_html(html):soup = BeautifulSoup(html,'lxml')tables = soup.select('div.article div.indent table')books = []for table in tables:title = table.div.a['title']'''由于information中包含多個信息,某些書與大多數書的信息格式不一致在進行列表索引的時候非常容易引起IndexError異常,為了保證爬蟲的健壯性我們對該異常進行處理'''information = table.p.stringinformations = information.split('/')while(len(informations)>4):del informations[1]try:author = informations[0]press = informations[1]date = informations[2]price = informations[3]except IndexError:continue'''像這樣子進行數據提取很容易遇到某一個特殊部分的網頁結構與大部分的不一樣,這會導致首頁能抓取到的節點,在該部分會返回None,從而導致調用string屬性產生AttributeError異常,我們需要進行異常處理'''try:score = table.find(attrs={'class':'rating_nums'}).stringrecommendation = table.find(attrs={'class':'inq'}).stringexcept AttributeError:continuebook = {'書名':title,'作者':author,'出版社':press,'出版日期':date,'價格':price,'評分':score,'推薦語':recommendation}books.append(book)return booksdef print_(books):for book in books:print('*'*50)for key,value in zip(book.keys(),book.values()):print(key+':'+value)print('*'*50)if __name__ == '__main__':urls = [f'https://book.douban.com/top250?start={i*25}' for i in range(0,10)]for url in urls: html = get_html(url)books = parse_html(html)print_(books)五、總結
通過本篇的學習,讀者應該著重掌握:
- BeautifulSoup庫三種節點選擇方式的靈活運用
- 對可能的異常要進行處理(請求部分的異常一般不用處理)
- 與正則表達式進行優劣比較
- 讀者可以自行將正則表達式與BeautifulSoup結合起來靈活使用
這里解釋一下為什么請求部分的異常一般不需要處理,因為請求出現異常一般意味著url錯誤、網絡連接有問題等,這些異常都需要我們處理好而不是用try...except語句跳過它,否則爬蟲無法繼續。
如果覺得本篇文章不錯,歡迎關注我的爬蟲系列教程公眾號【痕風雨】,一起學習交流。
總結
以上是生活随笔為你收集整理的requests 获取div_爬虫系列第五篇 使用requests与BeautifulSoup爬取豆瓣图书Top250的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: OpenGL视点跟踪物体运动
- 下一篇: 包与模块管理及面向对象初步