-前言
之前一直用scrapy與urllib姿勢爬取資料,最近使用requests感覺還不錯,這次希望通過對知乎資料的爬取為 各位爬蟲愛好者和初學者更好的瞭解爬蟲製作的準備過程以及requests請求方式的操作和相關問題。當然這是一個簡單的爬蟲專案,我會用重點介紹爬蟲從開始製作的準備過程,目的是為了讓和我一樣自學的爬蟲愛好者和初學者更多的瞭解爬蟲工作。
一、觀察目標網頁模板和策略
很多人都忽略這一步,其實這一步最為重要,因為它決定了你將採取什麼策略來獲取資料,也可以評估出你能夠做到什麼程度
(1)開啟瀏覽器的開發工具F12
這裡我用的是Google瀏覽器,開啟瀏覽器按F12,你將看到你載入網頁情況,以及網路請求的方式和互動的引數情況。如果你沒有看到,你應該百度自己的瀏覽器開發者工具,如何開啟。我們在開啟知乎門戶網頁後,F12看到開發者工具的Network一欄沒有出現任何東西。如圖1.1所示:
開發者工具 圖 1.1
然後我們在知乎搜尋框內輸入需要搜尋的內容,你將會看到網頁後臺與前臺資料互動的變化,載入的資料以及資料請求的方式和引數。如圖1.2:
服務端與瀏覽器互動的資訊 圖1.2
這裡你可以看到有很多js檔案和png格式檔案,這些檔案都是通過你的搜尋這個動作,對方伺服器返回的檔案,根據這些你可以更加了解網頁服務端與瀏覽器的互動過程。這裡如果你很有經驗的話,可以根據它的size和name欄位快速找出你想要的互動檔案。
因為我們之前的搜尋操作,所以很容易可以看出來第一個帶有search欄位的是搜尋操作時和網站伺服器互動的檔案。點選我們可以看到 如圖1.3:
與伺服器通訊請求引數 圖1.3
這裡有返回給我們與伺服器通訊後的過程以及相關資料,右上方可以看到Headers、Previes、Response、cookie等選項 。
-
headers可以看到請求的引數,我們很多時候寫爬蟲訪問伺服器被拒絕就是因為這裡有很多引數驗證沒有通過,所以學會運用這裡的引數是很有必要的。Requests Headers 是你請求時所用的請求頭,重要的是cookie與User-Agent,cookie可以讓你免登陸,雖然有時效性,但是有時候會幫助你節省時間,同時它有時候也是網站監測你是否是爬蟲的手段,userAgent基本都會被服務端檢測,所以每次請求都需要帶上。Query String Parameters是你請求時所帶的引數,因為這裡是get方式請求,所以這裡的引數在你的請求連結是可以看到的。post請求時,這裡的引數會很重要,每次請求都必需帶上這些引數,比如賬號登入,你就可以在這裡看到賬號和密碼。
-
Previes 這個檔案內容就是網站將會呈現給你的東西,也就是預覽
-
Response 可以看到返回的資料文字,有時候這裡可以直接看到json資料,解決動態頁面時,如果你能夠直接找到想要的資料,你可以很輕鬆的避開js檔案,直接訪問這個檔案獲取資料。
在知乎首頁搜尋後,你會發現沒有換頁,通過不停的下拉,會有新的資料產生。這裡我們通過下拉時可以看到它產生了新的檔案 如圖 1.4:下拉後變化 圖1.4
多了一個search的請求檔案,我們點開和第一個對比發現,offset欄位從0變成了10。我們複製一下這裡的url在新開的標籤頁貼上後,發現如圖1.5:
複製連結的頁面 圖1.5
看到返回的一個json型別的資料,這裡中文都是unicode編碼格式,所以我們需要自己寫點程式碼,複製這些字串,decode解碼出中文。解碼後你會發現,這些都是下拉時網頁顯示的資料。我們就可以通過正規表示式從這裡將自己需要的資料提取出來,這裡只是網頁的第一層,為了進入第二層(點選一個連結,跳轉的下一個頁面),我們將提取這裡所有的連結。
回顧我之前文章requests介紹用法,很容易寫出:
#假設每個搜尋項有500頁,如果沒有想要的內容,可以breakfor i in range(500): # key是要搜尋的關鍵詞 url = 'https://www.zhihu.com/r/search?q='+key+'&correction=1&type=content&offset='+str(i*10) try: #try用在這裡,後面會解釋原因 response = requests.get(url,headers=headers) except: continue response.encoding='unicode-escape' page = lxml.html.fromstring(response.text) rule = re.compile('<a target="_blank" href="(.*?)"') hrefs = re.findall(rule,response.text)
好了第一層我們差不多做好了,進入網站第二層,隨意點選一個我們搜尋產生的內容標題,跳轉至一個新的頁面,我們用同樣的方法,觀察第二層我們與服務端互動的資訊 如圖 1.6:
第二層服務端互動資訊 圖1.6
這裡有很多網路載入好的檔案,我們需要找到具有關鍵資訊的檔案,如果各位有嘗試在程式裡直接請求你開啟的url連結,會發現你得到的東西不是你在網上看到的,這是因為很多檔案是通過瀏覽器渲染了js而得到的資料,而我們寫的程式碼並沒有這一步,所以我們很多時候用程式訪問動態頁面時,看不到資料。
當然你也可以新增這裡面的js檔案在程式碼中觸發,但是這會變得很複雜,因為js檔案作用很多,你要找到你想要的js檔案並不容易。我們需要找到更快速有效的方法,這也是為什麼“觀察”是爬蟲最重要的一環。
這裡我們開始一個一個檔案的去看,先看Response,你可以看到他返回的資料和內容。還有就是看它的檔名,這裡的answers很容易可以猜到是評論和回答,我們點選進去Response看到資料,發現確實如我們所想
如圖1.7:
回答請求引數頁面 圖1.7
有上次經驗,可以準確知道offset是用來翻頁的,limit是回答條數,我們同樣複製這條連結,和第一層同樣開啟一個新的標籤頁,就能看到返回的json資料。我們要做的只需要通過解析工作,提取相關資料就行了。
二、爬蟲的製作
通過觀察所得到的結論,可以找到伺服器的請求入口,從第一層找到翻頁的連結,然後第二層獲取資料。知乎第二層其實有幾種版式,一種就是我們上文那種問答的形式,還有文章的形式,/question/ 和zhuanlan ,還有視訊,這幾種都要分開討論,分別解析。我這裡只介紹問答和文章的解析方式,方法都大同小異。
(1) 搜尋關鍵詞進入相關頁面
import requestsimport reimport lxml.html keys = ['.....'] #key 自己定義一個list for key in keys: #假設有500頁 for i in range(500): url = 'https://www.zhihu.com/r/search?q='+key+'&correction=1&type=content&offset='+str(i*10) try: response = requests.get(url,headers=headers) except: continue response.encoding='unicode-escape' page = lxml.html.fromstring(response.text) rule = re.compile('<a target="_blank" href="(.*?)"') hrefs = re.findall(rule,response.text) rule2 = re.compile('<em>(.*?)<') keyword = re.findall(rule2,response.text) response.close() hrefs = [i.replace('\\','') for i in hrefs] if key in keyword: for href in hrefs: if 'question' in href: num = href.replace('/question/','') GetQuestionMsg(key,num) elif 'zhuanlan' in href: GetPageMsg(key,href) else: break
(2) 文章樣式提取資料
def GetPageMsg(key,url): headers = { 'User-Agent': 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Mobile Safari/537.36' } try: response = requests.get(url,headers=headers) except: return #print(response.status_code) print(key) #這裡我用xpath方式來解析網頁文字 page = lxml.html.fromstring(response.text) title = page.xpath('//title/text()') title = title[0] content = page.xpath('//div[@class="RichText Post-RichText"]//p/text()')
(3)問答模板提取資料
def GetQuestionMsg(key,num): #這裡假設它有500頁 for i in range(500): url = 'https://www.zhihu.com/api/v4/questions/'+num+'/answers?include=data%5B*%5D.is_normal%2Cadmin_closed_comment%2Creward_info%2Cis_collapsed%2Cannotation_action%2Cannotation_detail%2Ccollapse_reason%2Cis_sticky%2Ccollapsed_by%2Csuggest_edit%2Ccomment_count%2Ccan_comment%2Ccontent%2Ceditable_content%2Cvoteup_count%2Creshipment_settings%2Ccomment_permission%2Ccreated_time%2Cupdated_time%2Creview_info%2Crelevant_info%2Cquestion%2Cexcerpt%2Crelationship.is_authorized%2Cis_author%2Cvoting%2Cis_thanked%2Cis_nothelp%2Cupvoted_followees%3Bdata%5B*%5D.mark_infos%5B*%5D.url%3Bdata%5B*%5D.author.follower_count%2Cbadge%5B%3F(type%3Dbest_answerer)%5D.topics&offset='+str(i*20)+'&limit=20&sort_by=created' headers = { 'User-Agent': 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Mobile Safari/537.36' } #自行新增自己的cookie值,按第一步所說步驟,複製新增訪問此處網頁的Requests Headers中的cookie cookies={ '_zap':'', ' q_c1':'', ' d_c0':'', ' z_c0':'', ' __DAYU_PP':'', ' q_c1':'', ' aliyungf_tc':'', ' _xsrf':'', ' __utmc':'', ' __utmv':'', ' __utmz':'', ' __utma':'', ' s-q':'', ' sid':'', ' s-i':'', } try: response = requests.get(url,headers=headers,cookies=cookies) except: return response.encoding='unicode-escape' print(key) rule = re.compile('"excerpt": "(.*?)"') content = re.findall(rule,response.text) rule2 = re.compile('"title": "(.*?)"') title = re.findall(rule2,response.text) content =','.join(content) response.close() try: print(title[1]) title = title[1] print(content) except: return
問答時候,新增了cookies,因為直接訪問得不到資料,所以我們需要模擬我們觀察得到的請求頭資訊,新增了cookies,發現訪問成功。
三、測試與問題
用requests爬知乎的過程中,經常會遇到一個異常
requests.exceptions.ConnectionError: HTTPSConnectionPool: Max retries exceeded with url:
百度出來的解釋說requests連線請求次數超過了限制次數,需要關閉連線或設定更大的預設連線數,但我都嘗試了,還是會有這種問題。我想應該是其它原因導致的這個錯誤,所以我在每次的response= response.get()時都要加try,丟擲異常來保證程式持續執行。如果你們有更好的解決方案和對這個問題的理解,請您回復我或者私信我,不甚感激。
結語 電動堆高車
知乎爬蟲專案到這裡就告一段落了,前期工作講的比較細,因為我覺得作為爬蟲工程師對前期工作應該要重視,我之前沒有重視,所以填了很多坑,走了很多彎路,所以借這個專案來講下爬蟲工程師前期工作,當然我講的只是一小塊,實際工作有的會更加複雜,有時候找很久都找不到獲取資料入口,有時候找到了獲取途徑又需要引數驗證,甚至好不容易獲取到了資料,才發現網站能提供的資料量遠遠不夠,這都是前期工作需要做好的內容。這一章獻給和我一樣自學的爬蟲愛好者與初學者。