溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Python如何一鍵爬取你所關心的書籍信息

發(fā)布時間:2021-10-25 17:27:01 來源:億速云 閱讀:160 作者:柒染 欄目:編程語言

本篇文章給大家分享的是有關Python如何一鍵爬取你所關心的書籍信息,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。

前言 

平時看到的豆瓣爬蟲基本都是爬豆瓣top100電影、某電影熱評、top100圖書、熱門圖書等,最近遇到的一個需求是根據一堆書名的列表(或者書名Excel文件)爬取對應的書目信息,也就是豆瓣圖書頁面上的出版社、出版時間、ISBN、定價、評分、評分人數(shù)等信息,再整合到pandas里進行處理,最后可以進行數(shù)據分析。

需求來源 

最近整理書目的時候需要根據幾百本書的書名整理出對應的出版社、出版時間、ISBN、評分等屬性,書單Excel如下圖1中的表。批量處理肯定是用爬蟲啦,查了一下沒有發(fā)現(xiàn)相似的文章,并且自己操作時也遇到了比較有趣的問題,于是把自己的操作思路和過程整理成本文。

Python如何一鍵爬取你所關心的書籍信息

圖1,書單數(shù)據部分截圖

爬取過程 

頁面分析

首先分析豆瓣圖書首頁:book.douban.com,直接搜索書名時可以看到搜索參數(shù)是寫在url上的,于是想著直接用https://book.douban.com/subject_search?search_text={0}&cat=1001'.format('書名'),直接改search_text參數(shù),在這個頁面按F12調出控制臺,失望的是這個url返回的html是不含數(shù)據的,如圖2。關鍵是找了一段時間還是沒找到異步返回的數(shù)據json(如果有人找到了豆瓣subject_search?search_text={0}&cat=1001這類頁面的書籍數(shù)據的位置歡迎告訴我呀),這時候考慮用Selenium或者查其他接口。

Python如何一鍵爬取你所關心的書籍信息

圖2,基于搜索url的html截圖

json分析

注意到豆瓣圖書的搜索頁面有一個搜索提示,于是在控制臺查Network發(fā)現(xiàn)搜索提示返回的直接是一個json,例如查“未來簡史”,結果如下:

Python如何一鍵爬取你所關心的書籍信息

圖3,未來簡史搜索提示

返回json可以用的屬性有:title:書名、url:對應書的豆瓣頁面、pic:書封面圖資源位置等。如果上面的輸入咱們只有書名,就根據書名和返回的json對應,如果有作者、出版年份等屬性,就可以更好的核對是否是我們要找的書,為了簡化,下面只用了返回json數(shù)據的第1條。

基本代碼

根據返回的url就可以從這個url去定位我們需要爬的信息。走通了就可以正式寫代碼了,以下代碼采用jupyter notebook的組織方式,也就是切分得比較細。先引入所需庫:

import json
import requests
import pandas as pd
from lxml import etree

讀取書名Excel數(shù)據,只用了"書名"列,先不考慮其他列

bsdf=pd.read_excel('booklistfortest.xlsx') 
blst=list(bsdf['書名'])  #書名列表
#bsdf.head(3)

對書名列表進行循環(huán),得到的屬性用字典裝著,每本書的屬性是一個字典,用列表裝各個字典。

通過requests.get('https://book.douban.com/j/subject_suggest?q={0}'.format(bn))獲取搜索建議返回的json數(shù)據,其中bn是書名字符串。
爬蟲的一般解析是用BeautifulSoup或xpath,我更喜歡用xpath,因此下面的代碼主要基于xpath解析文本。
以評分為例,鼠標點擊評分部分,然后按Ctrl+Shift+I,或者右鍵點擊檢查元素,反正就是定位到評分對應的HTML上,定位到評分的代碼部分后,右鍵,選擇Copy->Copy XPath,例如對于評分來說有://*[@id="interest_sectl"]/div/div[2]/strong。

Python如何一鍵爬取你所關心的書籍信息

圖4,復制評分的xpath

通過con.xpath('//*[@id="interest_sectl"]/div/div[2]/strong/text()')就可以得到評分數(shù)據,返回的是列表,一般就是第0個值。同樣,其他地方也是這樣,而作者、出版社那幾個屬性是結構比較散的,需要特殊處理。

Python如何一鍵爬取你所關心的書籍信息

圖5,自由度較大的書目信息部分

通過//*[@id="info"]/span[2]可以確定 出版社 這個屬性,但是屬性的值,具體是哪個出版社不能確定,這些文字是在info這個節(jié)點上的。對于這種長度不定的一個html區(qū)域,不能寫死xpath解析式,需要理清其HTML樹結構,建立info的樹結構。通過分析幾個具體的頁面的info部分,建立樹結構如下:

Python如何一鍵爬取你所關心的書籍信息

圖6,info部分的HTML樹

需要得到的是{'出版社’:'中信出版集團'}這樣的數(shù)據,通過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

為了在大循環(huán)里好調用,上面的部分封裝成函數(shù),調用getBookInfo()返回的是一個字典,要整合到已有的字典里。涉及字典的組合,查了一下可以用d=dict(d,**dw),其中d是舊字典,dw是要加到d里的新字典,更簡便的方式是用d.update(dw)函數(shù),下面的代碼就是用的update的。

主循環(huán)代碼:

rlst=[]
for bn in blst:
   res={}
   r=requests.get('https://book.douban.com/j/subject_suggest?q={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"]/h2/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))  #調用上面的函數(shù)處理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=='評價人數(shù)不足':
           res['評分']=''
           res['評價人數(shù)']='評價人數(shù)不足'
       else:
           res['評分']=''
           res['評價人數(shù)']=''
   else:
       res['評分']=bmark.replace(' ','')
       bmnum=con.xpath('//*[@id="interest_sectl"]/div/div[2]/div/div[2]/span/a/span/text()')[0]
       res['評價人數(shù)']=bmnum
   rlst.append(res)
得到的數(shù)據可以進行一定的標準化然后進行分析再輸出。上面得到的列表rlst=[{'書名':'a','出版社':'b'},{'','','':''}],可以直接變成dataframe,

outdf=pd.DataFrame(rlst) #轉dataframe
outdf.to_excel('out_douban_binfo.xlsx',index=False) #輸出數(shù)據

Python如何一鍵爬取你所關心的書籍信息

圖7,爬到的數(shù)據概覽

基礎數(shù)據統(tǒng)計分析

我們開始時讀入的bsdf有書名、作者、閱讀時間等屬性,因為爬下來的數(shù)據可能會有缺失值,將兩個表合并起來進行分析。分析的維度有書名、作者、閱讀時間、出版社、頁數(shù)等。首先是用merge整合兩表然后看一些基本的統(tǒng)計量。

bdf=bsdf.merge(outdf,on='書名',how='left') # 數(shù)據合并
# 基本統(tǒng)計值
print('一共有{0}本書,{1}個作者,{2}個出版社;'.format(len(bdf),len(set(list(bdf['作
者']))),len(set(list(bdf['出版社'])))))

輸出是一共有421本書,309個作者,97個出版社;
我們就來看看前幾位的作者和出版社,通過bdf['作者'].value_counts().head(7)可以輸出前7位書單里出現(xiàn)最多的作者,出版社同理,結果如下:

Python如何一鍵爬取你所關心的書籍信息

圖8,出版社和作者統(tǒng)計

從作者出現(xiàn)次數(shù)來看,前6位都是小說類型的書,可以看一下吳軍的是哪些書:

bdf.loc[bdf['作者']=='吳軍',['書名','閱讀時間','閱讀情況','出版社']]
#output:
'''
                 書名       閱讀時間 閱讀情況      出版社
103             數(shù)學之美 2016-10-20   P5  人民郵電出版社
233             智能時代 2017-06-22   P4    中信出版社
237             硅谷之謎 2017-07-01   P4  人民郵電出版社
383  見識--商業(yè)的本質和人生的智慧 2018-10-21   P4    中信出版社
'''
對每月閱讀數(shù)量進行統(tǒng)計:

import matplotlib.pyplot as plt #繪圖用到matplotlib庫
%matplotlib inline

bdf['閱讀年月']=bdf['閱讀時間'].apply(lambda x : x.strftime('%Y-%m'))
read_date=bdf['閱讀年月'].value_counts() #每月閱讀量,按月計數(shù)
read_date=pd.DataFrame(read_date,columns=['閱讀年月']) #從Series變?yōu)镈ataFrame
read_date=read_date.sort_index()

plt.figure(figsize=(15,5))
plt.xticks(rotation=90)#設置時間標簽顯示格式
plt.plot(read_date) #因為jupyter里寫了 %matplotlib inline 不用寫 plt.show()

Python如何一鍵爬取你所關心的書籍信息

圖9,每月閱讀數(shù)量_時間軸折線圖.png

好奇不同年份每個月是否有一定規(guī)律呢。要統(tǒng)計這個比較方便的就是用數(shù)據透視表了,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)
#這部分代碼的細節(jié)可以看本人github里jupyter notebook文件的輸出
r_dd=r_dd.loc[:,['2016','2017','2018']] #因為其他年份月份不全,只取這3年來看
plt.figure()
r_dd.plot(xticks=range(1,13),figsize=(12,5))

Python如何一鍵爬取你所關心的書籍信息

圖10,每月閱讀數(shù)量_按年統(tǒng)計

可以看到這3年在2月和7月閱讀普遍數(shù)量更多,在7月份之前每月閱讀量是逐年上漲的,而從8月到12月則是遞減的規(guī)律,2016年11月閱讀的書籍最多,達到40本以上。
評分是一個數(shù)值型變量,用箱線圖[圖片上傳中...(圖12_書單內數(shù)據相關的書籍.png-5352ab-1551272966564-0)]
展現(xiàn)其特征:

b_rank=pd.DataFrame(bdf['評分']) #評分分布(箱線圖)
b_rank.boxplot()

#另,評分 top 10:
#bdf.sort_values(by='評分',ascending=False).head(10).loc[:,['書名','作者','閱讀時間'
,'閱讀情況','出版社','評分']]

Python如何一鍵爬取你所關心的書籍信息

圖11,書籍評分箱線圖

從箱線圖來看,書單有評分的書籍的豆瓣平均分在7.8左右,75%的書評分在7.2以上,也有一些書是在4分一下的。

Python如何一鍵爬取你所關心的書籍信息

圖12,書單內數(shù)據相關的書籍

書單里書名直接包含數(shù)據的書有37本,數(shù)據科學相關的書籍數(shù)量應該大于這個值。

可以進一步分析的有:

  • 看的書的書名詞云、作者的詞云

  • 出版社省份

  • 把字數(shù)統(tǒng)計和爬下來的頁數(shù)進行擬合,把字數(shù)和頁數(shù)一起處理

  • 把含有多國貨幣的價格屬性按匯率換算后看價格的分布

數(shù)據輸出

上面通過一個具體的需求實踐了能解決問題的爬蟲,豆瓣還是比較容易爬的,上面解析書目信息的做法還是很有意義的,當然我是用xpath做的,如果用BeautifulSoup又會是另一種實現(xiàn)方式,但分析問題->建立HTML樹的過程是通用的。上面的代碼還是比較簡略的,沒有考慮過多的驗證和異常處理,有任何意見或建議歡迎交流。

以上就是Python如何一鍵爬取你所關心的書籍信息,小編相信有部分知識點可能是我們日常工作會見到或用到的。希望你能通過這篇文章學到更多知識。更多詳情敬請關注億速云行業(yè)資訊頻道。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI