Python一鍵爬取你所關心的書籍資訊
作者:梅破知春近,準資料分析師
個人簡書專欄:放翁lcf
平時看到的豆瓣爬蟲基本都是爬豆瓣top100電影、某電影熱評、top100圖書、熱門圖書等,最近遇到的一個需求是根據一堆書名的列表(或者書名Excel檔案)爬取對應的書目資訊,也就是豆瓣圖書頁面上的出版社、出版時間、ISBN、定價、評分、評分人數等資訊,再整合到pandas裡進行處理,最後可以進行資料分析。
最近整理書目的時候需要根據幾百本書的書名整理出對應的出版社、出版時間、ISBN、評分等屬性,書單Excel如下圖1中的表。批次處理肯定是用爬蟲啦,查了一下沒有發現相似的文章,並且自己操作時也遇到了比較有趣的問題,於是把自己的操作思路和過程整理成本文。
圖1,書單資料部分截圖
爬取過程
頁面分析
首先分析豆瓣圖書首頁:book.douban.com,直接搜尋書名時可以看到搜尋引數是寫在url上的,於是想著直接用{0}&cat=1001'.format('書名')
,直接改search_text引數,在這個頁面按F12調出控制檯,失望的是這個url返回的html是不含資料的,如圖2。關鍵是找了一段時間還是沒找到非同步返回的資料json(如果有人找到了豆瓣subject_search?search_text={0}&cat=1001這類頁面的書籍資料的位置歡迎告訴我呀),這時候考慮用Selenium或者查其他介面。
圖2,基於搜尋url的html截圖
json分析
注意到豆瓣圖書的搜尋頁面有一個搜尋提示,於是在控制檯查Network發現搜尋提示返回的直接是一個json,例如查“未來簡史”,結果如下:
圖3,未來簡史搜尋提示
返回json可以用的屬性有:title:書名、url:對應書的豆瓣頁面、pic:書封面圖資源位置等。如果上面的輸入我們們只有書名,就根據書名和返回的json對應,如果有作者、出版年份等屬性,就可以更好的核對是否是我們要找的書,為了簡化,下面只用了返回json資料的第1條。
基本程式碼
根據返回的url就可以從這個url去定位我們需要爬的資訊。走通了就可以正式寫程式碼了,以下程式碼採用jupyter notebook的組織方式,也就是切分得比較細。先引入所需庫:
import json
import requests
import pandas as pd
from lxml import etree
讀取書名Excel資料,只用了"書名"列,先不考慮其他列
bsdf=pd.read_excel('booklistfortest.xlsx')
blst=list(bsdf['書名']) #書名列表
#bsdf.head(3)
對書名列表進行迴圈,得到的屬性用字典裝著,每本書的屬性是一個字典,用列表裝各個字典。
透過requests.get('{0}'.format(bn))
獲取搜尋建議返回的json資料,其中bn是書名字串。
爬蟲的一般解析是用BeautifulSoup或xpath,我更喜歡用xpath,因此下面的程式碼主要基於xpath解析文字。
以評分為例,滑鼠點選評分部分,然後按Ctrl+Shift+I,或者右鍵點選檢查元素,反正就是定位到評分對應的HTML上,定位到評分的程式碼部分後,右鍵,選擇Copy->Copy XPath,例如對於評分來說有://*[@id="interest_sectl"]/div/div[2]/strong
。
圖4,複製評分的xpath
透過con.xpath('//*[@id="interest_sectl"]/div/div[2]/strong/text()')
就可以得到評分資料,返回的是列表,一般就是第0個值。同樣,其他地方也是這樣,而作者、出版社那幾個屬性是結構比較散的,需要特殊處理。
圖5,自由度較大的書目資訊部分
透過//*[@id="info"]/span[2]
可以確定 出版社 這個屬性,但是屬性的值,具體是哪個出版社不能確定,這些文字是在info這個節點上的。對於這種長度不定的一個html區域,不能寫死xpath解析式,需要理清其HTML樹結構,建立info的樹結構。透過分析幾個具體的頁面的info部分,建立樹結構如下:
圖6,info部分的HTML樹
需要得到的是{'出版社’:'中信出版集團'}
這樣的資料,透過HTML樹結構可以看到的特徵是鍵(如出版社)在span裡,值可能在text裡,也可能封裝在span裡的子元素裡,反正每個鍵值對之後都有一個br去切分。考慮這些情況寫出的程式碼如下:
def getBookInfo(binfo,cc):
i=0
rss={}
k=''
v=''
f=0
clw=[]
for c in cc:
if '\n' in c:
if '\xa0' in c:
clw.append(c)
else:
clw.append(c)
for m in binfo[0]:
if m.tag=='span':
mlst=m.getchildren()
if len(mlst)==0:
k=m.text.replace(':','')
if '\xa0' in clw[i]:
f=1#需要m.tag=='a'下的值
else:
v=clw[i].replace('\n','').replace(' ','')
i+=1
elif len(mlst)>0:#下面有子span 一種判斷是m.attrib=={} 不夠精確
for n in mlst:
if n.tag=='span':
k=n.text.replace('\n','').replace(' ','') #不至於下面還有span,懶得用遞迴了
elif n.tag=='a':
v=n.text.replace('\n','').replace(' ','')
elif m.tag=='a':
if f==1: #是否可以不用這個if
v=m.text.replace('\n','').replace(' ','')
f=0
elif m.tag=='br':
if k=='':
print(i,'err')
else:
rss[k]=v
else:
print(m.tag,i)
return rss
為了在大迴圈裡好呼叫,上面的部分封裝成函式,呼叫getBookInfo()返回的是一個字典,要整合到已有的字典裡。涉及字典的組合,查了一下可以用d=dict(d,**dw)
,其中d是舊字典,dw是要加到d裡的新字典,更簡便的方式是用d.update(dw)函式,下面的程式碼就是用的update的。
主迴圈程式碼:
rlst=[]
for bn in blst:
res={}
r=requests.get('{0}'.format(bn))
rj=json.loads(r.text)
#對rj進行一下驗證和篩選
html=requests.get(rj[0]['url']) #之後再考慮多個返回值的驗證
con = etree.HTML(html.text)
bname=con.xpath('//*[@id="wrapper"]/h1/span/text()')[0] #和bn比較
res['bname_sq']=bn
res['bname']=bname
res['dbid']=rj[0]['id'] #不需要存url,存id就夠了
#這部分取到info就夠了,之後再用高階方法去匹配需要的元素,目前對應不對
binfo=con.xpath('//*[@id="info"]')
cc=con.xpath('//*[@id="info"]/text()')
res.update(getBookInfo(binfo,cc)) #呼叫上面的函式處理binfo
bmark=con.xpath('//*[@id="interest_sectl"]/div/div[2]/strong/text()')[0]
if bmark==' ':
bits=con.xpath('//*[@id="interest_sectl"]/div/div[2]/div/div[2]/span/a/text()')[0]
if bits=='評價人數不足':
res['評分']=''
res['評價人數']='評價人數不足'
else:
res['評分']=''
res['評價人數']=''
else:
res['評分']=bmark.replace(' ','')
bmnum=con.xpath('//*[@id="interest_sectl"]/div/div[2]/div/div[2]/span/a/span/text()')[0]
res['評價人數']=bmnum
rlst.append(res)
得到的資料可以進行一定的標準化然後進行分析再輸出。上面得到的列表rlst=[{'書名':'a','出版社':'b'},{'','','':''}],可以直接變成dataframe,
outdf=pd.DataFrame(rlst) #轉dataframe
outdf.to_excel('out_douban_binfo.xlsx',index=False) #輸出資料
圖7,爬到的資料概覽
我們開始時讀入的bsdf有書名、作者、閱讀時間等屬性,因為爬下來的資料可能會有缺失值,將兩個表合併起來進行分析。分析的維度有書名、作者、閱讀時間、出版社、頁數等。首先是用merge整合兩表然後看一些基本的統計量。
bdf=bsdf.merge(outdf,on='書名',how='left') # 資料合併
# 基本統計值
print('一共有{0}本書,{1}個作者,{2}個出版社;'.format(len(bdf),len(set(list(bdf['作
者']))),len(set(list(bdf['出版社'])))))
輸出是一共有421本書,309個作者,97個出版社;
我們就來看看前幾位的作者和出版社,透過bdf['作者'].value_counts().head(7)
可以輸出前7位書單裡出現最多的作者,出版社同理,結果如下:
圖8,出版社和作者統計
從作者出現次數來看,前6位都是小說型別的書,可以看一下吳軍的是哪些書:
bdf.loc[bdf['作者']=='吳軍',['書名','閱讀時間','閱讀情況','出版社']]
#output:
'''
書名 閱讀時間 閱讀情況 出版社
103 數學之美 2016-10-20 P5 人民郵電出版社
233 智慧時代 2017-06-22 P4 中信出版社
237 矽谷之謎 2017-07-01 P4 人民郵電出版社
383 見識--商業的本質和人生的智慧 2018-10-21 P4 中信出版社
'''
對每月閱讀數量進行統計:
import matplotlib.pyplot as plt #繪圖用到matplotlib庫
%matplotlib inline
bdf['閱讀年月']=bdf['閱讀時間'].apply(lambda x : x.strftime('%Y-%m'))
read_date=bdf['閱讀年月'].value_counts() #每月閱讀量,按月計數
read_date=pd.DataFrame(read_date,columns=['閱讀年月']) #從Series變為DataFrame
read_date=read_date.sort_index()
plt.figure(figsize=(15,5))
plt.xticks(rotation=90)#設定時間標籤顯示格式
plt.plot(read_date) #因為jupyter裡寫了 %matplotlib inline 不用寫 plt.show()
圖9,每月閱讀數量_時間軸折線圖.png
好奇不同年份每個月是否有一定規律呢。要統計這個比較方便的就是用資料透視表了,pandas裡的pivot_table
出場。
import numpy as np
bdf['閱讀年']=bdf['閱讀時間'].apply(lambda x : x.strftime('%Y'))
bdf['閱讀月']=bdf['閱讀時間'].apply(lambda x : x.strftime('%m')) #這裡也可以用.month .year
r_dd=bdf.loc[:,['閱讀年','閱讀月']]
r_dd['val']=1 #用以初始化
r_dd=pd.pivot_table(r_dd,values='val',index=['閱讀月'],columns=['閱讀年'],aggfunc=np.sum).fillna(value=0)
#這部分程式碼的細節可以看本人github裡jupyter notebook檔案的輸出
r_dd=r_dd.loc[:,['2016','2017','2018']] #因為其他年份月份不全,只取這3年來看
plt.figure()
r_dd.plot(xticks=range(1,13),figsize=(12,5))
圖10,每月閱讀數量_按年統計
可以看到這3年在2月和7月閱讀普遍數量更多,在7月份之前每月閱讀量是逐年上漲的,而從8月到12月則是遞減的規律,2016年11月閱讀的書籍最多,達到40本以上。
評分是一個數值型變數,用箱線圖[圖片上傳中...(圖12_書單內資料相關的書籍.png-5352ab-1551272966564-0)]
展現其特徵:
b_rank=pd.DataFrame(bdf['評分']) #評分分佈(箱線圖)
b_rank.boxplot()
#另,評分 top 10:
#bdf.sort_values(by='評分',ascending=False).head(10).loc[:,['書名','作者','閱讀時間'
,'閱讀情況','出版社','評分']]
圖11,書籍評分箱線圖
從箱線圖來看,書單有評分的書籍的豆瓣平均分在7.8左右,75%的書評分在7.2以上,也有一些書是在4分一下的。
圖12,書單內資料相關的書籍
書單裡書名直接包含資料的書有37本,資料科學相關的書籍數量應該大於這個值。
可以進一步分析的有:
看的書的書名詞雲、作者的詞雲
出版社省份
把字數統計和爬下來的頁數進行擬合,把字數和頁數一起處理
把含有多國貨幣的價格屬性按匯率換算後看價格的分佈
上面透過一個具體的需求實踐了能解決問題的爬蟲,豆瓣還是比較容易爬的,上面解析書目資訊的做法還是很有意義的,當然我是用xpath做的,如果用BeautifulSoup又會是另一種實現方式,但分析問題->建立HTML樹的過程是通用的。上面的程式碼還是比較簡略的,沒有考慮過多的驗證和異常處理,有任何意見或建議歡迎交流。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31555699/viewspace-2637594/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- java爬取豆瓣書籍資訊Java
- python爬蟲練習之爬取豆瓣讀書所有標籤下的書籍資訊Python爬蟲
- 手機版python爬取網頁書籍Python網頁
- 爬蟲利器Pyppeteer的介紹和使用 爬取京東商城書籍資訊爬蟲
- Python書籍|分享一本Python的書籍Python
- Python爬蟲教程+書籍分享Python爬蟲
- 利用requests+BeautifulSoup爬取網頁關鍵資訊網頁
- Python第一個爬蟲,爬取噹噹網 Top 500 本五星好評書籍Python爬蟲
- 網路爬蟲:使用Scrapy框架編寫一個抓取書籍資訊的爬蟲服務爬蟲框架
- python爬蟲——爬取大學排名資訊Python爬蟲
- 你的個人資訊正暴露在網際網路中!Python 爬蟲獲取 URP 教務系統學籍資訊Python爬蟲
- python爬取北京租房資訊Python
- python爬蟲58同城(多個資訊一次爬取)Python爬蟲
- Python爬蟲實戰:爬取淘寶的商品資訊Python爬蟲
- python爬蟲--爬取鏈家租房資訊Python爬蟲
- python itchat 爬取微信好友資訊Python
- 推薦一本 Laravel 書籍<Laravel 框架關鍵技術解析>Laravel框架
- 用python爬取鏈家的租房資訊Python
- Python爬蟲爬取淘寶,京東商品資訊Python爬蟲
- 小白學 Python 爬蟲(25):爬取股票資訊Python爬蟲
- Python筆記:網頁資訊爬取簡介(一)Python筆記網頁
- [python爬蟲] 招聘資訊定時系統 (一).BeautifulSoup爬取資訊並儲存MySQLPython爬蟲MySql
- 2019最新Python爬蟲教程+書籍分享Python爬蟲
- selenium + xpath爬取csdn關於python的博文博主資訊Python
- python3.6.5 爬取糗事百科,開心一下Python
- selenium 知網爬蟲之根據【關鍵詞】獲取文獻資訊爬蟲
- python爬取365好書中小說Python
- python爬蟲學習01--電子書爬取Python爬蟲
- 關於python爬取網頁Python網頁
- python書籍推薦-Python爬蟲開發與專案實戰Python爬蟲
- 初學程式設計所看的書籍(個人版)程式設計
- Python 招聘資訊爬取及視覺化Python視覺化
- python實現微博個人主頁的資訊爬取Python
- 讀一本跟技術無關的書籍
- iOS安全相關書籍iOS
- 爬蟲實戰(一):爬取微博使用者資訊爬蟲
- 傳統的書籍VS電子書–資料資訊圖
- 搜狐新聞推薦演算法原理 | “呈現給你的,都是你所關心的”演算法