兩周前,一位同學(xué)問小央,平時有沒有寫過技術(shù)類博客。小央大言不慚,隨口就說下次可以嘗試。
這不,自己挖的坑,哭也得填上。正巧,最近要統(tǒng)計自己的閱讀記錄,一個個看多費(fèi)勁呀,如果能寫個爬蟲程序,自動化獲取數(shù)據(jù),豈不美哉。
一瓶不響半瓶晃蕩,技術(shù)渣小央,剛有點(diǎn)小成績的時候,就忍不住的要分享給小白們了。
一、爬蟲思路
爬蟲是指請求網(wǎng)站并獲取數(shù)據(jù)的自動化程序,又稱網(wǎng)頁蜘蛛或網(wǎng)絡(luò)機(jī)器,最常用領(lǐng)域是搜索引擎,它的基本流程是明確需求-發(fā)送請求-獲取數(shù)據(jù)-解析數(shù)據(jù)-存儲數(shù)據(jù)。
網(wǎng)頁之所以能夠被爬取,主要是有以下三大特征:
網(wǎng)頁都有唯一的URL(統(tǒng)一資源定位符,也就是網(wǎng)址)進(jìn)行定位
網(wǎng)頁都使用HTML(定位超文本標(biāo)記語言)來描述頁面信息
網(wǎng)頁都使用HTTP/HTTPS(超文本傳輸協(xié)議)協(xié)議來傳輸HTML數(shù)據(jù)
因此,只要我們能確定需要爬取的網(wǎng)頁 URL地址,通過 HTTP/HTTPS協(xié)議來獲取對應(yīng)的 HTML頁面,就能提取 HTML頁面里有用的數(shù)據(jù)。
在工具的選擇上,任何支持網(wǎng)絡(luò)通信的語言都可以寫爬蟲,比如 c++、 java、 go、 node等,而 python則是用的最多最廣的,并且也誕生了很多優(yōu)秀的庫和框架,如 scrapy、 BeautifulSoup 、 pyquery、 Mechanize等。
私信小編001 有神秘驚喜大禮包!
BeautifulSoup是一個非常流行的 Pyhon 模塊。該模塊可以解析網(wǎng)頁,并提供定位內(nèi)容的便捷接口。
但是在解析速度上不如 Lxml模塊,因為后者是用C語言編寫,其中 Xpath可用來在 XML文檔中對元素和屬性進(jìn)行遍歷。
綜上,我們本次爬取工具選擇 Python,使用的包有 requests、 Lxml、 xlwt、 xlrd,分別用于模擬賬戶登錄、爬取網(wǎng)頁信息和操作 Excel進(jìn)行數(shù)據(jù)存儲等。
二、實戰(zhàn)解析
1. 模擬賬戶登錄
因為要爬取個人賬戶下的讀書列表,我們首先要讓 Python創(chuàng)建用戶會話,用于在跨請求時保存Cookie值,實現(xiàn)從客戶端瀏覽器連接服務(wù)器開始,到客戶端瀏覽器與服務(wù)器斷開。
其次要處理請求頭 Header,發(fā)送附帶賬戶名和密碼的 POST請求,并獲取登陸后的 Cookie值,保存在會話里。
登陸進(jìn)去之后,我們就可以長驅(qū)直入進(jìn)行爬蟲了;否則,它就會因請求不合法報錯。
代碼示例
# 1. 創(chuàng)建session對象,可以保存Cookie值ssion = requests.session()# 2. 處理 headersuser_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36'headers={'User-Agent':user_agent}# 3. 需要登錄的用戶名和密碼data = {"email":"豆瓣賬戶名XX", "password":"豆瓣密碼XX"}# 4. 發(fā)送附帶用戶名和密碼的請求,并獲取登錄后的Cookie值,保存在ssion里ssion.post("https://www.douban.com/accounts/login", data = data)respon=ssion.get(url,headers=headers)
2. 分析網(wǎng)頁結(jié)構(gòu)
進(jìn)入讀書頁面,右鍵選擇“檢查”,可以看到如下界面:當(dāng)鼠標(biāo)定位在某一行代碼時,就會選中左側(cè)對應(yīng)模塊。
所有圖書內(nèi)容的標(biāo)簽,存放在一個類名為 id="content"的div盒子, class="subject-item"的li盒子。
同樣方法,我們也可以找到書名、作者、出版社、何時讀過、評論信息,分別對應(yīng)哪些標(biāo)簽。
最終,我們通過CSS路徑提取來提取對應(yīng)標(biāo)簽數(shù)據(jù)。
代碼示例
#因為每頁有15本書,所以這里做了個循環(huán)for tr in trs: i = 1 while i <= 15: data = [] title = tr.xpath("./ul/li["+str(i)+"]/div[2]/h2/a/text()") info = tr.xpath('./ul/li['+str(i)+']/div[2]/div[1]/text()') date = tr.xpath('./ul/li['+str(i)+']/div[2]/div[2]/div[1]/span[2]/text()') comment = tr.xpath('./ul/li['+str(i)+']/div[2]/div[2]/p/text()')
3. 自動翻頁,循環(huán)爬取
因為要爬取歷史讀書清單,當(dāng)爬完一頁的時候,需要程序能夠?qū)崿F(xiàn)自動翻頁功能。
一種方法是讓它自動點(diǎn)擊下一頁,但這個學(xué)習(xí)成本有點(diǎn)高,吃力不討好;另一種方法,就是尋找網(wǎng)頁之間的規(guī)律,做個循環(huán)即可。
以央之姑娘的豆瓣賬戶為例:很容易發(fā)現(xiàn)網(wǎng)頁之間的區(qū)別僅在于start值不同,第一頁的 start=0.第二頁 start=15.以此類推,最后一頁,即19頁的 start=(19-1)*15=270.
找到這樣的規(guī)律就很容易實現(xiàn)Python自動翻頁功能,條條大路通羅馬,適合自己的就是最好的。
代碼示例
def getUrl(self): i = 0 while i < 271: url = ('https://book.douban.com/people/81099629/collect?start='+str(i)+'&sort=time&rating=all&filter=all&mode=grid') self.spiderPage(url) i += 15
4. 數(shù)據(jù)存儲
數(shù)據(jù)存儲有很多種方法,因為數(shù)據(jù)量不大,用Excel即可滿足,具體方法就不在這里贅述了。
代碼示例
def __init__(self): self.f = xlwt.Workbook() # 創(chuàng)建工作薄 self.sheet1 = self.f.add_sheet(u'央之姑娘', cell_overwrite_ok=True) # 命名table self.rowsTitle = [u'編號',u'書名', u'信息', u'讀過日期', u'評論'] # 創(chuàng)建標(biāo)題 for i in range(0. len(self.rowsTitle)): # 最后一個參數(shù)設(shè)置樣式 self.sheet1.write(0. i, self.rowsTitle[i], self.set_style('Times new Roman', 220. True)) # Excel保存位置 self.f.save('C://Users//DELL//央之姑娘.xlsx')
def set_style(self, name, height, bold=False): style = xlwt.XFStyle() # 初始化樣式 font = xlwt.Font() # 為樣式創(chuàng)建字體 font.name = name font.bold = bold font.colour_index = 2 font.height = height style.font = font return style
三、效果演示
前面講了爬蟲的原理和方法,那么接下來我們就展示一下具體效果:
附錄:完整代碼
from lxml import etree
import requests
import xlwt
import xlrd
class douban(object):
def __init__(self):
self.f = xlwt.Workbook() # 創(chuàng)建工作薄
self.sheet1 = self.f.add_sheet(u'央之姑娘', cell_overwrite_ok=True) # 命名table
self.rowsTitle = [u'編號',u'書名', u'信息', u'讀過日期', u'評論'] # 創(chuàng)建標(biāo)題
for i in range(0. len(self.rowsTitle)):
# 最后一個參數(shù)設(shè)置樣式
self.sheet1.write(0. i, self.rowsTitle[i], self.set_style('Times new Roman', 220. True))
# Excel保存位置
self.f.save('C://Users//DELL//央之姑娘.xlsx')
def set_style(self, name, height, bold=False):
style = xlwt.XFStyle() # 初始化樣式
font = xlwt.Font() # 為樣式創(chuàng)建字體
font.name = name
font.bold = bold
font.colour_index = 2
font.height = height
style.font = font
return style
def getUrl(self):
#自動翻頁
'''
url = ('https://book.douban.com/people/81099629/collect?start=0&sort=time&rating=all&filter=all&mode=grid')
self.spiderPage(url)
'''
i = 0
while i < 271:
url = ('https://book.douban.com/people/81099629/collect?start='+str(i)+'&sort=time&rating=all&filter=all&mode=grid')
self.spiderPage(url)
i += 15
def spiderPage(self,url):
if url is None:
return None
try:
data=xlrd.open_workbook('C://Users//DELL//央之姑娘.xlsx')
table=data.sheets()[0]
rowCount=table.nrows#獲取行數(shù)
# 1. 創(chuàng)建session對象,可以保存Cookie值
ssion = requests.session()
# 2. 處理 headers
user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36'
headers={'User-Agent':user_agent}
# 3. 需要登錄的用戶名和密碼
data = {"email":"豆瓣賬戶名XX", "password":"豆瓣密碼XX"}
# 4. 發(fā)送附帶用戶名和密碼的請求,并獲取登錄后的Cookie值,保存在ssion里
ssion.post("https://www.douban.com/accounts/login", data = data)
respon=ssion.get(url,headers=headers)
htmltext=respon.text
s = etree.HTML(htmltext)
trs = s.xpath('//*[@id="content"]/div[2]/div[1]')
m=0
for tr in trs:
i = 1
while i <= 15:
data = []
title = tr.xpath("./ul/li["+str(i)+"]/div[2]/h2/a/text()")
info = tr.xpath('./ul/li['+str(i)+']/div[2]/div[1]/text()')
date = tr.xpath('./ul/li['+str(i)+']/div[2]/div[2]/div[1]/span[2]/text()')
comment = tr.xpath('./ul/li['+str(i)+']/div[2]/div[2]/p/text()')
title=title[0] if title else ''
info=info[0] if info else ''
date=date[0] if date else ''
comment=comment[0] if comment else ''
data.append(rowCount+m)
data.append(title)
data.append(info)
data.append(date)
data.append(comment)
i += 1
for n in range(len(data)):
self.sheet1.write(rowCount+m,n,data[n])
m+=1
print(m)
print(title, info, date, comment)
finally:
self.f.save('C://Users//DELL//央之姑娘.xlsx')
if '_main_':
qn=douban()
qn.getUrl()