其實(shí)一年前就已經(jīng)接觸了爬蟲,當(dāng)時(shí)是想上手Python,然后想著在實(shí)踐中學(xué)習(xí)是一種比較快的上手方式,看了下Python比較適合寫爬蟲,于是學(xué)會(huì)了怎樣爬取一些反爬不那么厲害的網(wǎng)頁。當(dāng)初是看的MOOC課程視頻,涵蓋了入門所需的知識(shí),這一年來不時(shí)會(huì)用到一些爬蟲,有時(shí)一些爬蟲代碼讓一些事情方便了許多。之前一直記在筆記本上,現(xiàn)在既然在簡書上建了一個(gè)爬蟲的文集,那么我就簡單把筆記總結(jié)一下,放到這篇文章里面
這里只包含最基本的爬蟲筆記,有了這些可以爬取一些反爬措施不那么嚴(yán)格的網(wǎng)站,后續(xù)進(jìn)階的話還有很多需要接觸,像Scrapy框架,手機(jī)抓包,異步爬蟲,處理JavaScript加密的表單數(shù)據(jù)等
發(fā)送請(qǐng)求
requests這個(gè)庫讓發(fā)送請(qǐng)求變得十分簡單,想要訪問某個(gè)頁面,使用
r = requests.get(url)
就可以實(shí)現(xiàn),之后,可以進(jìn)行下列的一些操作:
- 查看訪問的狀態(tài)碼:
r.status_code,一般200代表成功訪問 - 指定解析頁面用的編碼:
r.encoding = 'gbk' - 獲得訪問之后定向到的鏈接,比如訪問之后有重定向,就可以用這個(gè)語句獲取重定向的鏈接:
r.url - 獲取頁面內(nèi)容,有了頁面內(nèi)容之后才能進(jìn)行爬?。?code>r.text或者
r.content,如果頁面是json格式的數(shù)據(jù),還可以用r.json()直接將內(nèi)容轉(zhuǎn)化為json形式方便解析
這是最基本的獲取一個(gè)頁面的內(nèi)容的方式,還可以有更多的設(shè)置,比如:
r = requests.get(url,headers=header,timeout=5)
可以設(shè)置超時(shí)時(shí)間,以及自定義發(fā)送的請(qǐng)求頭headers,一種最基本的反爬蟲措施是驗(yàn)證請(qǐng)求時(shí)的headers,如果訪問時(shí)狀態(tài)碼不是200,不妨試試加上headers,這是一個(gè)Python字典,像這樣:
headers = {
"Host": "www.itdecent.cn",
"Referer": "http://www.itdecent.cn",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36",
}
requests.get(url,headers=headers)
使用requests還可以下載網(wǎng)頁的內(nèi)容,如果某個(gè)鏈接是一張圖片,或者視頻音頻,可以用requests直接訪問之后寫入文件就可以保存下來,比如:
r = requests.get(url)
with open("file.jpg",'w') as f:
f.write(r.content)
當(dāng)然,如果視頻很大,那么在第一行requests的時(shí)候就要等很久,因此可以用下面這種方式以傳輸流的形式下載:
r = requests.get(url,stream=True) #只獲取頭信息,而不緩存整個(gè)頁面內(nèi)容
content_size = int(r.headers['content-length']) #獲得頁面信息比如大小
for data in r.iter_content(): #開始傳輸內(nèi)容
f.write(data) #len(data)代表當(dāng)前傳輸?shù)臄?shù)據(jù)大小
解析頁面內(nèi)容
通過requests成功訪問,并且通過r.text獲取到頁面的內(nèi)容之后,我們可以開始對(duì)這些內(nèi)容進(jìn)行解析,提取出想要的那部分?jǐn)?shù)據(jù),可以直接用正則表達(dá)式進(jìn)行匹配,也可以使用BeautifulSoup庫,使用pip install bs4就可以安裝,使用from bs4 import BeautifulSoup導(dǎo)入。首先,使用
soup = BeautifulSoup(r.text,'html.parser')
之后就可以在這個(gè)soup中查找元素了,所有的html標(biāo)簽都可以查找,有以下用法:
- 查找某個(gè)html標(biāo)簽:
element = soup.find('ul',attrs={'id':'item_list'}),這個(gè)語句的作用是查找頁面中第一個(gè)ul標(biāo)簽,并且它的id是“item_list” - 查找所有標(biāo)簽:
element = soup.find_all('ul'),這個(gè)語句會(huì)返回一個(gè)列表,里面包含了所有ul標(biāo)簽的內(nèi)容
以上返回得到的element仍然是soup對(duì)象,我們還可以繼續(xù)用find和find_all查找下去,對(duì)于以上找到的element,還可以用以下的語句查找一些內(nèi)容: -
element.href或element["href"]可以獲得這個(gè)標(biāo)簽的一些屬性,比如鏈接 -
element.string可以獲得這個(gè)標(biāo)簽中的文本 -
element.ul可以繼續(xù)尋找子元素
對(duì)于網(wǎng)頁解析來說,按標(biāo)簽查找這種方式比較直觀,當(dāng)我們打開網(wǎng)頁審查元素時(shí)就能清晰地定位到我們想要的元素處于哪一層:
定位元素
然后在代碼中使用BeautifulSoup就可以定位到我們想要的地方獲取信息了
抓包
我主要用chrome自帶的開發(fā)者工具(F12)進(jìn)行抓包,如果我們想要做的不只是爬取靜態(tài)頁面內(nèi)容那么簡單,而是涉及到自動(dòng)對(duì)網(wǎng)頁進(jìn)行一些操作(登錄、查詢...),或者頁面內(nèi)容是動(dòng)態(tài)加載的,那么我們就需要抓包看一下需要發(fā)送哪些請(qǐng)求
比如登錄,登錄GitHub的時(shí)候,準(zhǔn)備好開發(fā)者工具:

點(diǎn)擊登錄,就會(huì)顯示出所有的請(qǐng)求,并且發(fā)現(xiàn)登錄的請(qǐng)求是這個(gè):

所以想要實(shí)現(xiàn)模擬登錄GitHub,我們就要在程序中使用
requests幫助我們模擬出這個(gè)請(qǐng)求,看到這個(gè)請(qǐng)求的方法是POST,那么找到請(qǐng)求的URL以及請(qǐng)求所發(fā)送的data:
然后就可以構(gòu)造請(qǐng)求了:
r = requests.post(url,data=form_data),其中url就是“Request Url”,form_data是自己構(gòu)造的一個(gè)字典,里面存放了"Form Data"里面的數(shù)據(jù)。這個(gè)請(qǐng)求就實(shí)現(xiàn)了模擬登錄,完了之后可以用r.url檢查是不是跳轉(zhuǎn)到了登錄之后的鏈接(GitHub用戶的主頁)想要對(duì)頁面發(fā)出查詢請(qǐng)求或者獲取動(dòng)態(tài)加載的內(nèi)容也是一樣的,先在瀏覽器中進(jìn)行一次操作,抓包,然后看發(fā)出的請(qǐng)求,找到最關(guān)鍵的那個(gè)請(qǐng)求,比如獲取知乎回答的內(nèi)容:

這里捕獲到的URL其實(shí)就是獲取回答的API,觀察以下這個(gè)鏈接,可以對(duì)其進(jìn)行一定的簡化:
https://www.zhihu.com/api/v4/questions/23819007/answers?include=data%5B%2A%5D.is_normal%2Ccontent%2Ceditable_content%2Cvoteup_count&limit=5&offset=5&platform=desktop&sort_by=default
這個(gè)鏈接返回的是JSON格式的數(shù)據(jù):

那么,首先用GET方法請(qǐng)求這個(gè)API:
r = requests.get(url,headers=headers)一般API會(huì)檢測(cè)
headers,添加了headers才能成功訪問,然后,由于這個(gè)請(qǐng)求得到的是JSON數(shù)據(jù)格式,直接用page_content = r.json()就可以對(duì)其進(jìn)行進(jìn)一步處理了
使用requests.session()
剛剛在模擬登錄GitHub的過程中,可以看到提交的表單數(shù)據(jù)“Form Data”里面有一項(xiàng)“authenticity_token”,這個(gè)東西找到它很簡單,復(fù)制“authenticity_token”然后在審查頁面元素里面搜索就可以找到了:

在代碼中定位這個(gè)token可以用下面這種寫法:
import requests
from lxml import etree
ses = requests.session()
check_data = ses.get(login_url,).content
selector=etree.HTML(check_data)
authenticity_token = selector.xpath('//input[@name="authenticity_token"]/@value')[0]
這里為什么要用“session”來發(fā)送請(qǐng)求呢,因?yàn)槟忝看侮P(guān)掉瀏覽器,再打開GitHub,就會(huì)有一個(gè)不同的“authenticity_token”,而如果直接用requests.get先獲得這個(gè)“authenticity_token”,再用requests.post發(fā)送包含“authenticity_token”的表單數(shù)據(jù),那么是無法登錄成功的。而使用“session”相當(dāng)于開了一個(gè)瀏覽器,保持著你和GitHub的會(huì)話,所以在使用“session”的時(shí)候,你的“authenticity_token”是不會(huì)變的,其他時(shí)候比如請(qǐng)求驗(yàn)證碼等等也是同理
一年前當(dāng)有人建議我直接通過爬蟲來上手Python的時(shí)候,穩(wěn)扎穩(wěn)打看教材學(xué)C++的我是無法理解的,但是后來發(fā)現(xiàn)用Python寫爬蟲確實(shí)不難,主要是有很多方便易用的庫,直接調(diào)用就好了。剛開始的時(shí)候爬一些簡單的網(wǎng)站,給我?guī)砹嗽S多成就感,也在寫代碼的過程中不斷熟悉了Python和爬蟲。一開始的時(shí)候,我甚至興奮地在GitHub上面創(chuàng)建了我的第一個(gè)repository,是一個(gè)計(jì)算自動(dòng)登錄并計(jì)算績點(diǎn)的repository,后來,我也幫同學(xué)解決了一個(gè)shua-piao的問題成功用爬蟲裝逼。再后來,我發(fā)現(xiàn)我也沒有什么需求和動(dòng)力繼續(xù)進(jìn)階更深更有難度的爬蟲,我的爬蟲知識(shí)不算多但還算夠用,總是寫一些基礎(chǔ)的對(duì)自己的能力提高也沒有幫助。于是我就只有在需要的時(shí)候才寫爬蟲程序,開始學(xué)習(xí)其他的方向了
