異步加載與請(qǐng)求頭課件_第1頁
異步加載與請(qǐng)求頭課件_第2頁
異步加載與請(qǐng)求頭課件_第3頁
異步加載與請(qǐng)求頭課件_第4頁
異步加載與請(qǐng)求頭課件_第5頁
已閱讀5頁,還剩159頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

PythonCrawlerDevelopment極客學(xué)院J互聯(lián)網(wǎng)+職業(yè)技能系列

Python爬蟲開發(fā)從入門到實(shí)戰(zhàn)(微課版)人民郵電出版社謝乾坤

PythonCrawlerDevelopmen1第7章異步加載與請(qǐng)求頭

如果讀者在本世紀(jì)初就接觸過互聯(lián)網(wǎng),那么應(yīng)該會(huì)記得,那個(gè)時(shí)候每單擊一個(gè)鏈接,瀏覽器就會(huì)短暫地“白屏”一兩秒,然后才會(huì)進(jìn)入一個(gè)新的頁面。不同的頁面,網(wǎng)址也是不一樣的。

隨著技術(shù)的不斷進(jìn)步,現(xiàn)在不少網(wǎng)站已經(jīng)引入了異步加載技術(shù),單擊新的鏈接以后,幾乎看不到“白屏”的現(xiàn)象了。而且更神奇的是,單擊了鏈接,網(wǎng)頁的內(nèi)容已經(jīng)發(fā)生了改變,但是網(wǎng)址竟然沒有變。第7章異步加載與請(qǐng)求頭如果讀者在本世紀(jì)初2

通過這一章的學(xué)習(xí),你將會(huì)掌握如下知識(shí)。(1)抓取異步加載的數(shù)據(jù)。(2)偽造HTTP請(qǐng)求頭。(3)模擬瀏覽器獲取網(wǎng)站數(shù)據(jù)。

通過這一章的學(xué)習(xí),你將會(huì)掌握如下知識(shí)37.1

異步加載7.1.1AJAX技術(shù)介紹AJAX是AsynchronousJavaScriptAndXML的首字母縮寫,意為異步JavaScript與XML。

使用AJAX技術(shù),可以在不刷新網(wǎng)頁的情況下更新網(wǎng)頁數(shù)據(jù)。使用AJAX技術(shù)的網(wǎng)頁,一般會(huì)使用HTML編寫網(wǎng)頁的框架。

在打開網(wǎng)頁的時(shí)候,首先加載的是這個(gè)框架。剩下的部分將會(huì)在框架加載完成以后再通過JavaScript從后臺(tái)加載。7.1異步加載7.1.1AJAX技術(shù)介紹4如何判斷一個(gè)網(wǎng)頁有沒有使用AJAX技術(shù)呢?請(qǐng)?jiān)L問http:///exercise_ajax_1.html,這個(gè)頁面用瀏覽器訪問的結(jié)果如圖7-1所示。但是如果檢查它的源代碼,會(huì)發(fā)現(xiàn)源代碼里面并沒有網(wǎng)頁上面顯示的這兩段文字,如圖7-2所示。如何判斷一個(gè)網(wǎng)頁有沒有使用AJAX技術(shù)呢?請(qǐng)?jiān)L問5

圖7-1異步加載練習(xí)頁面1

圖7-2使用異步加載技術(shù)的網(wǎng)頁,數(shù)據(jù)不在源代碼中圖7-1異步加載練習(xí)頁面167.1.2JSON介紹與應(yīng)用JSON的全稱是JavaScriptObjectNotation,是一種輕量級(jí)的數(shù)據(jù)交換格式。網(wǎng)絡(luò)之間使用HTTP方式傳遞數(shù)據(jù)的時(shí)候,絕大多數(shù)情況下傳遞的都是字符串。

因此,當(dāng)需要把Python里面的數(shù)據(jù)發(fā)送給網(wǎng)頁或者其他編程語言的時(shí)候,可以先將Python的數(shù)據(jù)轉(zhuǎn)化為JSON格式的字符串,然后將字符串傳遞給其他語言,其他語言再將JSON格式的字符串轉(zhuǎn)換為它自己的數(shù)據(jù)格式。7.1.2JSON介紹與應(yīng)用JSON的全稱是7

為了直觀地觀察一個(gè)JSON格式的字符串,先在Python中初始化一個(gè)字典:person={'basic_info':{'name':'kingname','age':24,'sex':'male','merry':False},

為了直觀地觀察一個(gè)JSON格式的字符串,先8'work_info':{'salary':99999,'position':'engineer','department':None}}'work_info':{'salary':99999,9

不僅是字典,Python中的列表或者包含字典的列表,也可以轉(zhuǎn)換為JSON格式的字符串,如圖7-3所示。

如果要把JSON格式的字符串轉(zhuǎn)換為Python的字典或者列表,只需要使用一行代碼即可:person_dict=json.loads(person_json_indent)

這里得到的person_dict就是一個(gè)字典,可以像使用普通字典一樣來使用它,如圖7-4所示。不僅是字典,Python中的列表或者包含字10

圖7-3將包含字典的列表轉(zhuǎn)換為JSON格式的字符串

圖7-4把JSON格式的字符串轉(zhuǎn)換為字典圖7-3將包含字典的列表轉(zhuǎn)換為JSON格式的字符串117.1.3異步GET與POST請(qǐng)求

使用異步加載技術(shù)的網(wǎng)站,被加載的內(nèi)容是不能在源代碼中找到的。對(duì)于這種情況,應(yīng)該如何抓取被加載的內(nèi)容呢?

為了解決這個(gè)問題,就需要使用GoogleChrome瀏覽器的開發(fā)者模式。在網(wǎng)頁上單擊右鍵,選擇“檢查”命令,然后定位到“Network”選項(xiàng)卡,如圖7-5所示。

圖7-5打開GoogleChrome開發(fā)者工具7.1.3異步GET與POST請(qǐng)求使用12

接下來需要刷新網(wǎng)頁。在Windows下,按F5鍵或者單擊地址欄左邊的“刷新”按鈕,在MacOS下,按Shift+Command+R組合鍵或者單擊地址欄左邊的“刷新”按鈕。刷新以后,可以看到“Network”選項(xiàng)卡下面出現(xiàn)了一些內(nèi)容,如圖7-6所示。

圖7-6刷新網(wǎng)頁以后,“Network”選項(xiàng)卡下出現(xiàn)的內(nèi)容接下來需要刷新網(wǎng)頁。在Windows下,按F13

單擊“Network”選項(xiàng)卡下面出現(xiàn)的“ajax_1_backend”和“ajax_1_postbackend”,并定位到“Response”選項(xiàng)卡,可以看到這里出現(xiàn)了網(wǎng)頁上面的內(nèi)容,如圖7-7和圖7-8所示。圖7-7被異步加載的數(shù)據(jù)之一,使用GET方式

單擊“Network”選項(xiàng)卡下面出現(xiàn)的“a14圖7-8被異步加載的數(shù)據(jù)之二,使用POST方式圖7-8被異步加載的數(shù)據(jù)之二,使用POST方式15

再選擇“Headers”選項(xiàng)卡,可以看到這個(gè)請(qǐng)求使用GET方式,發(fā)送到/ajax_1_backend,其頭部信息如圖7-9所示。

于是,嘗試使用requests發(fā)送這個(gè)請(qǐng)求,即可成功獲取到網(wǎng)頁上的第1條內(nèi)容,如圖7-10所示。再選擇“Headers”選項(xiàng)卡,可以看到這個(gè)16

圖7-9使用GET方式的異步請(qǐng)求的頭部信息

圖7-10使用requests獲得被異步加載的信息圖7-9使用GET方式的異步請(qǐng)求的頭部信息17

對(duì)于網(wǎng)頁中的第2條內(nèi)容,查看“Headers”選項(xiàng)卡,可以看到,這是使用POST方式向http:///ajax_1_postbackend發(fā)送請(qǐng)求,并以JSON格式提交數(shù)據(jù),其頭部信息如圖7-11所示。

使用requests發(fā)送這個(gè)請(qǐng)求,也成功地獲取了網(wǎng)頁上面的第2條信息。通過修改請(qǐng)求的數(shù)據(jù)內(nèi)容,還能夠修改網(wǎng)頁的返回內(nèi)容,如圖7-12所示。對(duì)于網(wǎng)頁中的第2條內(nèi)容,查看“Headers18圖7-11使用POST方式的異步請(qǐng)求的頭部信息圖7-12使用requests模擬發(fā)送POST請(qǐng)求獲取第2條異步加載內(nèi)容圖7-11使用POST方式的異步請(qǐng)求的頭部信息圖7-12197.1.4特殊的異步加載7.1.3小節(jié)中介紹的是最常見、最簡單的異步加載情況,但并非所有的異步加載都會(huì)向后臺(tái)發(fā)送請(qǐng)求。打開AJAX的第2個(gè)練習(xí)頁面,可以看到頁面上有圖7-13所示的內(nèi)容。

圖7-13異步加載練習(xí)頁面27.1.4特殊的異步加載7.1.3小節(jié)中介紹20分析Chrome開發(fā)者工具的“Network”選項(xiàng)卡下面的內(nèi)容,可以看到整個(gè)頁面的打開過程并沒有嘗試請(qǐng)求后臺(tái)的行為。其中的exercise_ajax_2.html就是這個(gè)頁面自身,而jquery-3.2.1.min.js是jQuery的庫,都不是對(duì)后臺(tái)的請(qǐng)求。打開網(wǎng)頁源代碼可以看到,確實(shí)沒有“天王蓋地虎”這幾個(gè)漢字,如圖7-14所示。圖7-14網(wǎng)頁源代碼中確實(shí)沒有網(wǎng)頁中的內(nèi)容分析Chrome開發(fā)者工具的“Network21

那么這個(gè)頁面上的漢字到底是從哪里加載進(jìn)來的?這種情況稱為偽裝成異步加載的后端渲染。數(shù)據(jù)就在源代碼里,但卻不直接顯示出來。注意,源代碼最下面的JavaScript代碼,其中有一段:{"code":"\u884c\u52a8\u4ee3\u53f7\uff1a\u5929\u738b\u76d6\u5730\u864e"}其外形看起來有點(diǎn)像JSON格式的字符串。嘗試使用Python去解析,發(fā)現(xiàn)可以得到網(wǎng)頁上面的內(nèi)容,如圖7-15所示。那么這個(gè)頁面上的漢字到底是從哪里加載進(jìn)來的?這種情22圖7-15解析JSON字符串得到網(wǎng)頁上顯示的內(nèi)容圖7-15解析JSON字符串得到網(wǎng)頁上顯示的內(nèi)容23

這種假的異步加載頁面,其處理思路一般是使用正則表達(dá)式從頁面中把數(shù)據(jù)提取出來,然后直接解析。對(duì)于異步加載練習(xí)頁面2,完整的處理代碼為:importjsonimportrequestsimportre

這種假的異步加載頁面,其處理思路一般是使用24url='/exercise_ajax_2.html'html=requests.get(url).content.decode()code_json=re.search("secret='(.*?)'",html,re.S).group(1)code_dict=json.loads(code_json)print(code_dict['code'])

運(yùn)行后的結(jié)果如圖7-16所示。url='http://exercise.kingnam25圖7-16獲取假異步加載的數(shù)據(jù)圖7-16獲取假異步加載的數(shù)據(jù)267.1.5多次請(qǐng)求的異步加載圖7-17異步加載練習(xí)頁面3還有一些網(wǎng)頁,顯示在頁面上的內(nèi)容要經(jīng)過多次異步請(qǐng)求才能得到。第1個(gè)AJAX請(qǐng)求返回的是第2個(gè)請(qǐng)求的參數(shù),第2個(gè)請(qǐng)求的返回內(nèi)容又是第3個(gè)請(qǐng)求的參數(shù),只有得到了上一個(gè)請(qǐng)求里面的有用信息,才能發(fā)起下一個(gè)請(qǐng)求。打開異步加載練習(xí)頁3,頁面內(nèi)容如圖7-17所示。7.1.5多次請(qǐng)求的異步加載圖7-17異步加載練習(xí)頁27

通過分析Chrome開發(fā)者工具的請(qǐng)求,不難發(fā)現(xiàn)這一條信息是通過向http:///ajax_3_postbackend這個(gè)地址發(fā)送POST請(qǐng)求得到的,如圖7-18所示。圖7-18通過Chrome開發(fā)者工具找到頁面信息的來源通過分析Chrome開發(fā)者工具的請(qǐng)求,不難28

其中,返回的JSON格式的字符串經(jīng)過Python解析,可以得到頁面上的文字,如圖7-19所示。圖7-19使用Python解析發(fā)現(xiàn)請(qǐng)求返回的內(nèi)容確實(shí)是頁面內(nèi)容其中,返回的JSON格式的字符串經(jīng)過Pyt29

在“Headers”選項(xiàng)卡查看這個(gè)POST請(qǐng)求的具體參數(shù),在body里面發(fā)現(xiàn)兩個(gè)奇怪的參數(shù)secret1和secret2,如圖7-20所示。圖7-20分析請(qǐng)求的body信息發(fā)現(xiàn)兩個(gè)奇怪參數(shù)secret1和secret2在“Headers”選項(xiàng)卡查看這個(gè)POST請(qǐng)30

到目前為止,一切看起來都和7.1.3小節(jié)中的POST請(qǐng)求一樣。但是在7.1.3小節(jié)里面提交的參數(shù)是可以隨便修改的,那么在這里如果隨便修改會(huì)怎么樣呢?嘗試修改secret1和secret2,發(fā)現(xiàn)POST請(qǐng)求無法得到想要的結(jié)果,如圖7-21所示。圖7-21修改secret1或者secret2發(fā)現(xiàn)不能得到想要的結(jié)果到目前為止,一切看起來都和7.1.3小節(jié)中的POS31

打開這個(gè)練習(xí)頁的源代碼,在源代碼中可以找到secret_2,如圖7-22所示。雖然在POST參數(shù)中,名字是secret2,而源代碼中的名字是secret_2,不過從值可以看出這就是同一個(gè)參數(shù)。

圖7-22在源代碼中找到secret_2打開這個(gè)練習(xí)頁的源代碼,在源代碼中可以找到32

源代碼里面沒有secret1,因此就要考慮這個(gè)參數(shù)是不是來自于另一個(gè)異步請(qǐng)求。

繼續(xù)在開發(fā)者工具中查看其他請(qǐng)求,可以成功找到secret1,如圖7-23所示。注意,它的名字變?yōu)榱恕癱ode”,但是從值可以看出這就是secret1。不少網(wǎng)站也會(huì)使用這種改名字的方式來迷惑爬蟲開發(fā)者。源代碼里面沒有secret1,因此就要考慮這33圖7-23在另一個(gè)異步請(qǐng)求里面發(fā)現(xiàn)了secret1圖7-23在另一個(gè)異步請(qǐng)求里面發(fā)現(xiàn)了secret134

這一條請(qǐng)求就是一個(gè)不帶任何參數(shù)的GET請(qǐng)求,請(qǐng)求的頭部信息如圖7-24所示。

對(duì)于這種多次請(qǐng)求才能得到數(shù)據(jù)的情況,解決辦法就是逐一請(qǐng)求,得到返回結(jié)果以后再發(fā)起下一個(gè)請(qǐng)求。具體到這個(gè)例子中,那就是先從源代碼里面獲得secret2,再通過GET請(qǐng)求得到secret1,最后使用secret1和secret2來獲取頁面上顯示的內(nèi)容。

使用Python來實(shí)現(xiàn)這個(gè)過程,代碼和運(yùn)行結(jié)果如圖7-25所示。這一條請(qǐng)求就是一個(gè)不帶任何參數(shù)的GET請(qǐng)求35

圖7-24獲得secret1的請(qǐng)求的頭部信息

圖7-25使用Python模擬多次異步請(qǐng)求并獲得頁面上的值圖7-24獲得secret1的請(qǐng)求的頭部信息367.1.6基于異步加載的簡單登錄網(wǎng)站的登錄方式有很多種,其中有一種比較簡單的方式,就是使用AJAX發(fā)送請(qǐng)求來進(jìn)行登錄。請(qǐng)打開AJAX第4個(gè)練習(xí)頁http://exercise./exercise_ajax_4.html,這個(gè)頁面實(shí)現(xiàn)了簡單的登錄功能。頁面打開以后的效果如圖7-26所示。根據(jù)輸入框中的提示,使用用戶名“kingname”和密碼“genius”進(jìn)行登錄,可以看到登錄成功以后彈出圖7-27所示的提示框。7.1.6基于異步加載的簡單登錄網(wǎng)站的登錄方式37

圖7-26使用異步加載實(shí)現(xiàn)的登錄頁面

圖7-27登錄成功后彈出的提示框圖7-26使用異步加載實(shí)現(xiàn)的登錄頁面38

對(duì)于這種簡單的登錄功能,可以使用抓取異步加載網(wǎng)頁的方式來進(jìn)行處理。在Chrome開發(fā)者工具中可以發(fā)現(xiàn),當(dāng)單擊“登錄”按鈕時(shí),網(wǎng)頁向后臺(tái)發(fā)送了一條請(qǐng)求,如圖7-28所示。圖7-28登錄過程實(shí)際上是一個(gè)異步的請(qǐng)求對(duì)于這種簡單的登錄功能,可以使用抓取異步加39

這條請(qǐng)求返回的內(nèi)容就是“通關(guān)口令”。再來看看這個(gè)請(qǐng)求發(fā)送了哪些數(shù)據(jù),如圖7-29所示。圖7-29登錄請(qǐng)求發(fā)送的數(shù)據(jù)這條請(qǐng)求返回的內(nèi)容就是“通關(guān)口令”。再來看看40

這就是使用POST方式的最簡單的AJAX請(qǐng)求。使用獲取POST方式的AJAX請(qǐng)求的代碼,就能成功獲取到登錄以后返回的內(nèi)容,如圖7-30所示。圖7-30使用AJAX請(qǐng)求獲得登錄返回的內(nèi)容這就是使用POST方式的最簡單的AJAX請(qǐng)求417.2請(qǐng)求頭(Headers)7.2.1請(qǐng)求頭的作用使用計(jì)算機(jī)網(wǎng)頁版外賣網(wǎng)站的讀者應(yīng)該會(huì)發(fā)現(xiàn)這樣一個(gè)現(xiàn)象:第一次登錄外賣網(wǎng)頁的時(shí)候會(huì)讓你選擇當(dāng)前所在的商業(yè)圈,一旦選定好之后關(guān)閉瀏覽器再打開,網(wǎng)頁就會(huì)自動(dòng)定位到先前選擇的商業(yè)圈。又比如,例如攜程的網(wǎng)站,使用計(jì)算機(jī)瀏覽器打開的時(shí)候,頁面看起來非常復(fù)雜多樣,如圖7-31所示。但同一個(gè)網(wǎng)址,使用手機(jī)瀏覽器打開時(shí),網(wǎng)址會(huì)自動(dòng)發(fā)生改變,而且得到的頁面竟然完全不同,如圖7-32所示。7.2請(qǐng)求頭(Headers)7.2.1請(qǐng)求頭的作用42

圖7-31計(jì)算機(jī)網(wǎng)頁版攜程首頁

圖7-32手機(jī)版攜程首頁圖7-31計(jì)算機(jī)網(wǎng)頁版攜程首頁43

網(wǎng)站怎么知道現(xiàn)在是計(jì)算機(jī)瀏覽器還是手機(jī)瀏覽器在訪問這個(gè)頁面呢?網(wǎng)站怎么能記住地理位置呢?這就要?dú)w功于Headers了。

Headers稱為請(qǐng)求頭,瀏覽器可以將一些信息通過Headers傳遞給服務(wù)器,服務(wù)器也可以將一些信息通過Headers傳遞給瀏覽器。電商網(wǎng)站常常應(yīng)用的Cookies就是Headers里面的一個(gè)部分。網(wǎng)站怎么知道現(xiàn)在是計(jì)算機(jī)瀏覽器還是手機(jī)瀏覽447.2.2偽造請(qǐng)求頭打開練習(xí)頁/exercise_headers.html,使用Chrome的開發(fā)者工具監(jiān)控這個(gè)頁面的網(wǎng)頁請(qǐng)求,可以看到圖7-33所示的內(nèi)容。圖7-33請(qǐng)求頭練習(xí)頁7.2.2偽造請(qǐng)求頭打開練習(xí)頁http://45

頁面看起來像是發(fā)起了一個(gè)普通的GET方式的異步請(qǐng)求給/exercise_headers_backend。使用requests嘗試獲取這個(gè)網(wǎng)址的返回信息,結(jié)果如圖7-34所示。圖7-34使用requests訪問請(qǐng)求頭練習(xí)頁面失敗頁面看起來像是發(fā)起了一個(gè)普通的GET方式的46

使用瀏覽器訪問網(wǎng)站的時(shí)候,網(wǎng)站可以看到一個(gè)名稱為Headers(請(qǐng)求頭)的東西,它的內(nèi)容如圖7-35所示。圖7-35使用瀏覽器訪問網(wǎng)站后臺(tái)顯示的Headers信息使用瀏覽器訪問網(wǎng)站的時(shí)候,網(wǎng)站可以看到一47

如果使用requests訪問,請(qǐng)求頭的內(nèi)容如圖7-36所示。圖7-36使用requests訪問網(wǎng)站,后臺(tái)顯示的Headers信息如果使用requests訪問,請(qǐng)求頭的內(nèi)容48

為了解決這個(gè)問題,就需要給爬蟲“換頭”。把瀏覽器的頭安裝到爬蟲的身上,這樣網(wǎng)站就不知道誰是誰了。要換頭,首先就需要知道瀏覽器的頭是什么樣的。因此需要在Chrome瀏覽器開發(fā)者工具的“Network”選項(xiàng)卡的RequestHeaders里面觀察這一次請(qǐng)求的請(qǐng)求頭,如圖7-37所示。為了解決這個(gè)問題,就需要給爬蟲“換頭”。把49圖7-37瀏覽器發(fā)起的請(qǐng)求的頭部信息圖7-37瀏覽器發(fā)起的請(qǐng)求的頭部信息50

在requests里面,設(shè)置請(qǐng)求頭的參數(shù)名稱為“headers”,它的值是一個(gè)字典。帶有請(qǐng)求頭的請(qǐng)求,使用requests的發(fā)送格式為:html=requests.get(url,headers=字典).content.decode()html=requests.post(url,json=xxx,headers=字典).content.decode()

代碼中的字典就對(duì)應(yīng)了瀏覽器中的請(qǐng)求頭。在爬蟲里面創(chuàng)建一個(gè)字典,將Chrome的請(qǐng)求頭的內(nèi)容復(fù)制進(jìn)去,并調(diào)整好格式,發(fā)起一個(gè)帶有Chrome請(qǐng)求頭的爬蟲請(qǐng)求,可以發(fā)現(xiàn)請(qǐng)求獲得成功,如圖7-38所示。在requests里面,設(shè)置請(qǐng)求頭的參數(shù)名51圖7-38更換了Chrome頭部以后爬蟲訪問成功圖7-38更換了Chrome頭部以后爬蟲訪問成功52

雖然對(duì)于某些網(wǎng)站,在請(qǐng)求頭里面只需要設(shè)置User-Agent就可以正常訪問了,但是為了保險(xiǎn)起見,還是建議把所有項(xiàng)目都帶上,這樣可以讓爬蟲更“像”瀏覽器。例如本練習(xí),如果僅僅設(shè)置User-Agent的話,會(huì)得到圖7-39所示的返回信息。圖7-39僅僅修改User-Agent是不能騙過練習(xí)網(wǎng)站的雖然對(duì)于某些網(wǎng)站,在請(qǐng)求頭里面只需要設(shè)置U537.3模擬瀏覽器

有一些網(wǎng)站在發(fā)起AJAX請(qǐng)求的時(shí)候,會(huì)帶上特殊的字符串用于身份驗(yàn)證。這種字符串稱為Token。為了簡單起見,請(qǐng)打開練習(xí)頁面,這個(gè)頁面在發(fā)起AJAX請(qǐng)求的時(shí)候會(huì)在Headers中帶上一個(gè)參數(shù)ReqTime;在POST發(fā)送的數(shù)據(jù)中會(huì)有一個(gè)參數(shù)sum,如圖7-40所示。7.3模擬瀏覽器有一些網(wǎng)站在發(fā)起AJ54圖7-40較為復(fù)雜的異步加載練習(xí)頁面圖7-40較為復(fù)雜的異步加載練習(xí)頁面55

多次刷新頁面,可以發(fā)現(xiàn)ReqTime和sum一直在變化。如果requests只固定使用某個(gè)ReqTime與sum的組合來發(fā)起請(qǐng)求,就會(huì)出現(xiàn)圖7-41所示的返回信息。

不難看出ReqTime是精確到毫秒的時(shí)間戳,即使使用Python生成了一個(gè)時(shí)間戳,也不能得到網(wǎng)頁上面的內(nèi)容,如圖7-42所示。多次刷新頁面,可以發(fā)現(xiàn)ReqTime和sum56

圖7-41如果使用固定的參數(shù)就會(huì)導(dǎo)致爬蟲爬不到數(shù)據(jù)

圖7-42僅僅修改時(shí)間戳是不能讓爬蟲成功的圖7-41如果使用固定的參數(shù)就會(huì)導(dǎo)致爬蟲爬不到數(shù)據(jù)577.3.1Selenium介紹雖然在網(wǎng)頁的源代碼中無法看到被異步加載的內(nèi)容,但是在Chrome的開發(fā)者工具的“Elements”選項(xiàng)卡下卻可以看到網(wǎng)頁上的內(nèi)容,如圖7-43所示。圖7-43在開發(fā)者工具的“Elements”選項(xiàng)卡下可以看到被加載的內(nèi)容7.3.1Selenium介紹雖然在網(wǎng)頁的源代587.3.2Selenium安裝使用pip安裝Selenium:pipinstallselenium安裝情況如圖7-44所示。圖7-44安裝Selenium7.3.2Selenium安裝使用pip安裝59

下載ChromeDriver,根據(jù)自己的系統(tǒng)選擇合適的版本,如圖7-45所示。圖7-45根據(jù)自己的系統(tǒng)選擇合適的版本下載ChromeDriver,根據(jù)自己的系統(tǒng)607.3.3Selenium的使用1.獲取源代碼將chromedriver與代碼放在同一個(gè)文件夾中以方便代碼直接調(diào)用。初始化Selenium只需要兩行代碼,導(dǎo)入Selenium庫,再指定WebDriver,如圖7-46所示。圖7-46初始化Selenium7.3.3Selenium的使用1.獲取源代碼圖7-4661

第3行代碼指定了Selenium使用ChromeDriver來操作Chrome解析網(wǎng)頁,括號(hào)里的參數(shù)就是ChromeDriver可執(zhí)行文件的地址。

如果要使用PhantomJS,只需要修改第3行代碼即可:driver=webdriver.PhantomJS('./phantomjs')

同樣,需要將PhantomJS的可執(zhí)行文件與代碼放在一起。

需要特別提醒的是,如果chromedriver與代碼不在一起,可以通過絕對(duì)路徑來指定,例如:driver=webdriver.Chrome('/usr/bin/chromedriver')第3行代碼指定了Selenium使用Chr62

使用Windows的讀者在寫這個(gè)參數(shù)的時(shí)候,要注意反斜杠的問題?!癨”這個(gè)符號(hào)叫作反斜杠,在Windows中作為路徑的分隔符。但是由于轉(zhuǎn)義字符也是反斜杠,所以如果把Windows下面的代碼寫為下面這樣就會(huì)出問題。driver=webdriver.Chrome('C:\server\chromedriver.exe')

因此,使用Windows的讀者可在路徑字符串左引號(hào)的左邊加一個(gè)“r”符號(hào),將代碼寫為:driver=webdriver.Chrome(r'C:\server\chromedriver.exe')

這樣Python就能正確處理反斜杠的問題。使用Windows的讀者在寫這個(gè)參數(shù)的時(shí)候,63

初始化完成以后,就可以使用Selenium打開網(wǎng)頁了。要打開一個(gè)網(wǎng)頁只需要一行代碼:driver.get('/exercise_advanced_ajax.html')

代碼運(yùn)行以后會(huì)自動(dòng)打開一個(gè)Chrome窗口,并在窗口里面自動(dòng)進(jìn)入這個(gè)網(wǎng)址對(duì)應(yīng)的頁面。一旦被異步加載的內(nèi)容已經(jīng)出現(xiàn)在了這個(gè)自動(dòng)打開的Chrome窗口中,那么此時(shí)使用下列代碼:html=driver.page_source

就能得到在Chrome開發(fā)者工具中出現(xiàn)的HTML代碼,如圖7-47所示。初始化完成以后,就可以使用Selenium打64圖7-47在ChromeDriver加載頁面完成以后可以得到加載以后的源代碼圖7-47在ChromeDriver加載頁面完成以后可以652.等待信息出現(xiàn)

圖7-47所示的代碼第6行設(shè)置了一個(gè)5s的延遲,這是由于Selenium并不會(huì)等待網(wǎng)頁加載完成再執(zhí)行后面的代碼。它只是向ChromeDriver發(fā)送了一個(gè)命令,讓ChromeDriver打開某個(gè)網(wǎng)頁。

至于網(wǎng)頁要開多久,Selenium并不關(guān)心。由于被異步加載的內(nèi)容會(huì)延遲出現(xiàn),因此需要等待它出現(xiàn)以后再開始抓取。2.等待信息出現(xiàn)663.在網(wǎng)頁中獲取元素

在網(wǎng)頁中尋找需要的內(nèi)容,可以使用類似于BeautifulSoup4的語法:element=driver.find_element_by_id("passwd-id")#如果有多個(gè)符合條件的,返回第1個(gè)element=driver.find_element_by_name("passwd")#如果有多個(gè)符合條件的,返回第1個(gè)element_list=driver.find_elements_by_id("passwd-id")#以列表形式返回所有的符合條件的elementelement_list=driver.find_elements_by_name("passwd")#以列表形式返回所有的符合條件的element3.在網(wǎng)頁中獲取元素67

也可以使用XPath:element=driver.find_element_by_xpath("http://input[@id='passwd-id']")#如果有多個(gè)符合條件的,返回第1個(gè)element=driver.find_elements_by_xpath("http://div[@id='passwd-id']")#以列表形式返回所有的符合條件的element也可以使用XPath:68

對(duì)于練習(xí)網(wǎng)站,使用XPath獲取網(wǎng)頁的內(nèi)容,運(yùn)行結(jié)果如圖7-48所示。圖7-48使用Selenium和ChromeDriver獲得練習(xí)網(wǎng)站的內(nèi)容對(duì)于練習(xí)網(wǎng)站,使用XPath獲取網(wǎng)頁的內(nèi)容,697.4階段案例在樂視網(wǎng)上尋找一個(gè)視頻,爬取視頻的評(píng)論信息。7.4.1需求分析目標(biāo)網(wǎng)站:。目標(biāo)內(nèi)容:爬取視頻評(píng)論。涉及的知識(shí)點(diǎn):(1)分析網(wǎng)站的異步加載請(qǐng)求。(2)使用requests發(fā)送請(qǐng)求。7.4階段案例在樂視網(wǎng)上尋找一個(gè)視頻,爬取707.4.2核心代碼構(gòu)建在樂視網(wǎng)上打開一個(gè)視頻,可以看到其部會(huì)評(píng)論頁面如圖7-49所示。圖7-49樂視網(wǎng)部分視頻評(píng)論頁面7.4.2核心代碼構(gòu)建在樂視網(wǎng)上打開一個(gè)視頻71

通過使用Chrome的開發(fā)者工具分析頁面的異步加載請(qǐng)求,可以發(fā)現(xiàn)評(píng)論所在的請(qǐng)求如圖7-50所示。

可以使用Python來模擬這個(gè)請(qǐng)求,從而獲取視頻的評(píng)論信息。

在請(qǐng)求的URL里面有兩個(gè)參數(shù):vid和pid。這兩個(gè)參數(shù)在網(wǎng)頁的源代碼里面都可以找到,如圖7-51所示。

通過使用Chrome的開發(fā)者工具分析頁面的72圖7-50使用Chrome開發(fā)者工具觀察評(píng)論的異步加載請(qǐng)求圖7-50使用Chrome開發(fā)者工具觀察評(píng)論的異步加載請(qǐng)73圖7-51在網(wǎng)頁源代碼里面尋找pid和vid圖7-51在網(wǎng)頁源代碼里面尋找pid和vid74

爬蟲首先訪問視頻頁面,通過正則表達(dá)式獲取vid和pid,并將結(jié)果保存到“necessary_info”這個(gè)類屬性對(duì)應(yīng)的字典中。核心代碼如下:defget_necessary_id(self):source=self.get_source(self.url,self.HEADERS)vid=re.search('vid:(\d+)',source).group(1)pid=re.search('pid:(\d+)',source).group(1)self.necessary_info['xid']=vidself.necessary_info['pid']=pid爬蟲首先訪問視頻頁面,通過正則表達(dá)式獲取v75

訪問評(píng)論的接口,用Python發(fā)起請(qǐng)求,獲得評(píng)論數(shù)據(jù)。核心代碼如下:defget_comment(self):url=self.COMMENT_URL.format(xid=self.necessary_info['xid'],pid=self.necessary_info['pid'])source=self.get_source(url,self.HEADERS)source_json=source[source.find('{"'):-1]

訪問評(píng)論的接口,用Python發(fā)起請(qǐng)求,獲76comment_dict=json.loads(source_json)comments=comment_dict['data']forcommentincomments:print(f'發(fā)帖人:{comment["user"]["username"]},評(píng)論內(nèi)容:{comment["content"]}')comment_dict=json.loads(sour77

代碼中,提前定義的self.COMMENT_URL和self.HEADERS如圖7-52所示。圖7-52在代碼中提前定義好self.COMMENT_URL和self.HEADERS代碼中,提前定義的self.COMMENT_787.4.3調(diào)試與運(yùn)行在爬蟲中,帶上通過Chrome瀏覽器從評(píng)論頁面復(fù)制而來的Headers再發(fā)起請(qǐng)求,可以減少爬蟲被網(wǎng)站封鎖的概率。

爬蟲的運(yùn)行結(jié)果如圖7-53所示。圖7-53樂視網(wǎng)視頻評(píng)論爬蟲運(yùn)行結(jié)果7.4.3調(diào)試與運(yùn)行在爬蟲中,帶上通過Chro797.5本章小結(jié)

本章主要介紹了使用爬蟲獲取異步加載網(wǎng)頁的各種方法。對(duì)于普通的異步加載,可以使用requests直接發(fā)送AJAX請(qǐng)求來獲取被加載的內(nèi)容。

發(fā)送的請(qǐng)求中可能包含一些特殊的值,這些值來自網(wǎng)頁源代碼或者另一個(gè)AJAX請(qǐng)求。7.5本章小結(jié)本章主要介紹了使用爬蟲獲80

在發(fā)送請(qǐng)求時(shí)需要注意,應(yīng)保持requests提交的請(qǐng)求頭與瀏覽器的請(qǐng)求頭一致,這樣才能更好地騙過網(wǎng)站服務(wù)器達(dá)到獲取數(shù)據(jù)的目的。

對(duì)于比較復(fù)雜的異步加載,現(xiàn)階段可以先使用Selenium和ChromeDriver來直接加載網(wǎng)頁,然后就能從被加載的網(wǎng)頁中直接獲取到需要的內(nèi)容。在發(fā)送請(qǐng)求時(shí)需要注意,應(yīng)保持requests817.6動(dòng)手實(shí)踐

尋找并爬取一個(gè)使用異步加載技術(shù)的網(wǎng)站。7.6動(dòng)手實(shí)踐尋找并爬取一個(gè)使用異步82

PythonCrawlerDevelopment極客學(xué)院J互聯(lián)網(wǎng)+職業(yè)技能系列

Python爬蟲開發(fā)從入門到實(shí)戰(zhàn)(微課版)人民郵電出版社謝乾坤

PythonCrawlerDevelopmen83第7章異步加載與請(qǐng)求頭

如果讀者在本世紀(jì)初就接觸過互聯(lián)網(wǎng),那么應(yīng)該會(huì)記得,那個(gè)時(shí)候每單擊一個(gè)鏈接,瀏覽器就會(huì)短暫地“白屏”一兩秒,然后才會(huì)進(jìn)入一個(gè)新的頁面。不同的頁面,網(wǎng)址也是不一樣的。

隨著技術(shù)的不斷進(jìn)步,現(xiàn)在不少網(wǎng)站已經(jīng)引入了異步加載技術(shù),單擊新的鏈接以后,幾乎看不到“白屏”的現(xiàn)象了。而且更神奇的是,單擊了鏈接,網(wǎng)頁的內(nèi)容已經(jīng)發(fā)生了改變,但是網(wǎng)址竟然沒有變。第7章異步加載與請(qǐng)求頭如果讀者在本世紀(jì)初84

通過這一章的學(xué)習(xí),你將會(huì)掌握如下知識(shí)。(1)抓取異步加載的數(shù)據(jù)。(2)偽造HTTP請(qǐng)求頭。(3)模擬瀏覽器獲取網(wǎng)站數(shù)據(jù)。

通過這一章的學(xué)習(xí),你將會(huì)掌握如下知識(shí)857.1

異步加載7.1.1AJAX技術(shù)介紹AJAX是AsynchronousJavaScriptAndXML的首字母縮寫,意為異步JavaScript與XML。

使用AJAX技術(shù),可以在不刷新網(wǎng)頁的情況下更新網(wǎng)頁數(shù)據(jù)。使用AJAX技術(shù)的網(wǎng)頁,一般會(huì)使用HTML編寫網(wǎng)頁的框架。

在打開網(wǎng)頁的時(shí)候,首先加載的是這個(gè)框架。剩下的部分將會(huì)在框架加載完成以后再通過JavaScript從后臺(tái)加載。7.1異步加載7.1.1AJAX技術(shù)介紹86如何判斷一個(gè)網(wǎng)頁有沒有使用AJAX技術(shù)呢?請(qǐng)?jiān)L問http:///exercise_ajax_1.html,這個(gè)頁面用瀏覽器訪問的結(jié)果如圖7-1所示。但是如果檢查它的源代碼,會(huì)發(fā)現(xiàn)源代碼里面并沒有網(wǎng)頁上面顯示的這兩段文字,如圖7-2所示。如何判斷一個(gè)網(wǎng)頁有沒有使用AJAX技術(shù)呢?請(qǐng)?jiān)L問87

圖7-1異步加載練習(xí)頁面1

圖7-2使用異步加載技術(shù)的網(wǎng)頁,數(shù)據(jù)不在源代碼中圖7-1異步加載練習(xí)頁面1887.1.2JSON介紹與應(yīng)用JSON的全稱是JavaScriptObjectNotation,是一種輕量級(jí)的數(shù)據(jù)交換格式。網(wǎng)絡(luò)之間使用HTTP方式傳遞數(shù)據(jù)的時(shí)候,絕大多數(shù)情況下傳遞的都是字符串。

因此,當(dāng)需要把Python里面的數(shù)據(jù)發(fā)送給網(wǎng)頁或者其他編程語言的時(shí)候,可以先將Python的數(shù)據(jù)轉(zhuǎn)化為JSON格式的字符串,然后將字符串傳遞給其他語言,其他語言再將JSON格式的字符串轉(zhuǎn)換為它自己的數(shù)據(jù)格式。7.1.2JSON介紹與應(yīng)用JSON的全稱是89

為了直觀地觀察一個(gè)JSON格式的字符串,先在Python中初始化一個(gè)字典:person={'basic_info':{'name':'kingname','age':24,'sex':'male','merry':False},

為了直觀地觀察一個(gè)JSON格式的字符串,先90'work_info':{'salary':99999,'position':'engineer','department':None}}'work_info':{'salary':99999,91

不僅是字典,Python中的列表或者包含字典的列表,也可以轉(zhuǎn)換為JSON格式的字符串,如圖7-3所示。

如果要把JSON格式的字符串轉(zhuǎn)換為Python的字典或者列表,只需要使用一行代碼即可:person_dict=json.loads(person_json_indent)

這里得到的person_dict就是一個(gè)字典,可以像使用普通字典一樣來使用它,如圖7-4所示。不僅是字典,Python中的列表或者包含字92

圖7-3將包含字典的列表轉(zhuǎn)換為JSON格式的字符串

圖7-4把JSON格式的字符串轉(zhuǎn)換為字典圖7-3將包含字典的列表轉(zhuǎn)換為JSON格式的字符串937.1.3異步GET與POST請(qǐng)求

使用異步加載技術(shù)的網(wǎng)站,被加載的內(nèi)容是不能在源代碼中找到的。對(duì)于這種情況,應(yīng)該如何抓取被加載的內(nèi)容呢?

為了解決這個(gè)問題,就需要使用GoogleChrome瀏覽器的開發(fā)者模式。在網(wǎng)頁上單擊右鍵,選擇“檢查”命令,然后定位到“Network”選項(xiàng)卡,如圖7-5所示。

圖7-5打開GoogleChrome開發(fā)者工具7.1.3異步GET與POST請(qǐng)求使用94

接下來需要刷新網(wǎng)頁。在Windows下,按F5鍵或者單擊地址欄左邊的“刷新”按鈕,在MacOS下,按Shift+Command+R組合鍵或者單擊地址欄左邊的“刷新”按鈕。刷新以后,可以看到“Network”選項(xiàng)卡下面出現(xiàn)了一些內(nèi)容,如圖7-6所示。

圖7-6刷新網(wǎng)頁以后,“Network”選項(xiàng)卡下出現(xiàn)的內(nèi)容接下來需要刷新網(wǎng)頁。在Windows下,按F95

單擊“Network”選項(xiàng)卡下面出現(xiàn)的“ajax_1_backend”和“ajax_1_postbackend”,并定位到“Response”選項(xiàng)卡,可以看到這里出現(xiàn)了網(wǎng)頁上面的內(nèi)容,如圖7-7和圖7-8所示。圖7-7被異步加載的數(shù)據(jù)之一,使用GET方式

單擊“Network”選項(xiàng)卡下面出現(xiàn)的“a96圖7-8被異步加載的數(shù)據(jù)之二,使用POST方式圖7-8被異步加載的數(shù)據(jù)之二,使用POST方式97

再選擇“Headers”選項(xiàng)卡,可以看到這個(gè)請(qǐng)求使用GET方式,發(fā)送到/ajax_1_backend,其頭部信息如圖7-9所示。

于是,嘗試使用requests發(fā)送這個(gè)請(qǐng)求,即可成功獲取到網(wǎng)頁上的第1條內(nèi)容,如圖7-10所示。再選擇“Headers”選項(xiàng)卡,可以看到這個(gè)98

圖7-9使用GET方式的異步請(qǐng)求的頭部信息

圖7-10使用requests獲得被異步加載的信息圖7-9使用GET方式的異步請(qǐng)求的頭部信息99

對(duì)于網(wǎng)頁中的第2條內(nèi)容,查看“Headers”選項(xiàng)卡,可以看到,這是使用POST方式向http:///ajax_1_postbackend發(fā)送請(qǐng)求,并以JSON格式提交數(shù)據(jù),其頭部信息如圖7-11所示。

使用requests發(fā)送這個(gè)請(qǐng)求,也成功地獲取了網(wǎng)頁上面的第2條信息。通過修改請(qǐng)求的數(shù)據(jù)內(nèi)容,還能夠修改網(wǎng)頁的返回內(nèi)容,如圖7-12所示。對(duì)于網(wǎng)頁中的第2條內(nèi)容,查看“Headers100圖7-11使用POST方式的異步請(qǐng)求的頭部信息圖7-12使用requests模擬發(fā)送POST請(qǐng)求獲取第2條異步加載內(nèi)容圖7-11使用POST方式的異步請(qǐng)求的頭部信息圖7-121017.1.4特殊的異步加載7.1.3小節(jié)中介紹的是最常見、最簡單的異步加載情況,但并非所有的異步加載都會(huì)向后臺(tái)發(fā)送請(qǐng)求。打開AJAX的第2個(gè)練習(xí)頁面,可以看到頁面上有圖7-13所示的內(nèi)容。

圖7-13異步加載練習(xí)頁面27.1.4特殊的異步加載7.1.3小節(jié)中介紹102分析Chrome開發(fā)者工具的“Network”選項(xiàng)卡下面的內(nèi)容,可以看到整個(gè)頁面的打開過程并沒有嘗試請(qǐng)求后臺(tái)的行為。其中的exercise_ajax_2.html就是這個(gè)頁面自身,而jquery-3.2.1.min.js是jQuery的庫,都不是對(duì)后臺(tái)的請(qǐng)求。打開網(wǎng)頁源代碼可以看到,確實(shí)沒有“天王蓋地虎”這幾個(gè)漢字,如圖7-14所示。圖7-14網(wǎng)頁源代碼中確實(shí)沒有網(wǎng)頁中的內(nèi)容分析Chrome開發(fā)者工具的“Network103

那么這個(gè)頁面上的漢字到底是從哪里加載進(jìn)來的?這種情況稱為偽裝成異步加載的后端渲染。數(shù)據(jù)就在源代碼里,但卻不直接顯示出來。注意,源代碼最下面的JavaScript代碼,其中有一段:{"code":"\u884c\u52a8\u4ee3\u53f7\uff1a\u5929\u738b\u76d6\u5730\u864e"}其外形看起來有點(diǎn)像JSON格式的字符串。嘗試使用Python去解析,發(fā)現(xiàn)可以得到網(wǎng)頁上面的內(nèi)容,如圖7-15所示。那么這個(gè)頁面上的漢字到底是從哪里加載進(jìn)來的?這種情104圖7-15解析JSON字符串得到網(wǎng)頁上顯示的內(nèi)容圖7-15解析JSON字符串得到網(wǎng)頁上顯示的內(nèi)容105

這種假的異步加載頁面,其處理思路一般是使用正則表達(dá)式從頁面中把數(shù)據(jù)提取出來,然后直接解析。對(duì)于異步加載練習(xí)頁面2,完整的處理代碼為:importjsonimportrequestsimportre

這種假的異步加載頁面,其處理思路一般是使用106url='/exercise_ajax_2.html'html=requests.get(url).content.decode()code_json=re.search("secret='(.*?)'",html,re.S).group(1)code_dict=json.loads(code_json)print(code_dict['code'])

運(yùn)行后的結(jié)果如圖7-16所示。url='http://exercise.kingnam107圖7-16獲取假異步加載的數(shù)據(jù)圖7-16獲取假異步加載的數(shù)據(jù)1087.1.5多次請(qǐng)求的異步加載圖7-17異步加載練習(xí)頁面3還有一些網(wǎng)頁,顯示在頁面上的內(nèi)容要經(jīng)過多次異步請(qǐng)求才能得到。第1個(gè)AJAX請(qǐng)求返回的是第2個(gè)請(qǐng)求的參數(shù),第2個(gè)請(qǐng)求的返回內(nèi)容又是第3個(gè)請(qǐng)求的參數(shù),只有得到了上一個(gè)請(qǐng)求里面的有用信息,才能發(fā)起下一個(gè)請(qǐng)求。打開異步加載練習(xí)頁3,頁面內(nèi)容如圖7-17所示。7.1.5多次請(qǐng)求的異步加載圖7-17異步加載練習(xí)頁109

通過分析Chrome開發(fā)者工具的請(qǐng)求,不難發(fā)現(xiàn)這一條信息是通過向http:///ajax_3_postbackend這個(gè)地址發(fā)送POST請(qǐng)求得到的,如圖7-18所示。圖7-18通過Chrome開發(fā)者工具找到頁面信息的來源通過分析Chrome開發(fā)者工具的請(qǐng)求,不難110

其中,返回的JSON格式的字符串經(jīng)過Python解析,可以得到頁面上的文字,如圖7-19所示。圖7-19使用Python解析發(fā)現(xiàn)請(qǐng)求返回的內(nèi)容確實(shí)是頁面內(nèi)容其中,返回的JSON格式的字符串經(jīng)過Pyt111

在“Headers”選項(xiàng)卡查看這個(gè)POST請(qǐng)求的具體參數(shù),在body里面發(fā)現(xiàn)兩個(gè)奇怪的參數(shù)secret1和secret2,如圖7-20所示。圖7-20分析請(qǐng)求的body信息發(fā)現(xiàn)兩個(gè)奇怪參數(shù)secret1和secret2在“Headers”選項(xiàng)卡查看這個(gè)POST請(qǐng)112

到目前為止,一切看起來都和7.1.3小節(jié)中的POST請(qǐng)求一樣。但是在7.1.3小節(jié)里面提交的參數(shù)是可以隨便修改的,那么在這里如果隨便修改會(huì)怎么樣呢?嘗試修改secret1和secret2,發(fā)現(xiàn)POST請(qǐng)求無法得到想要的結(jié)果,如圖7-21所示。圖7-21修改secret1或者secret2發(fā)現(xiàn)不能得到想要的結(jié)果到目前為止,一切看起來都和7.1.3小節(jié)中的POS113

打開這個(gè)練習(xí)頁的源代碼,在源代碼中可以找到secret_2,如圖7-22所示。雖然在POST參數(shù)中,名字是secret2,而源代碼中的名字是secret_2,不過從值可以看出這就是同一個(gè)參數(shù)。

圖7-22在源代碼中找到secret_2打開這個(gè)練習(xí)頁的源代碼,在源代碼中可以找到114

源代碼里面沒有secret1,因此就要考慮這個(gè)參數(shù)是不是來自于另一個(gè)異步請(qǐng)求。

繼續(xù)在開發(fā)者工具中查看其他請(qǐng)求,可以成功找到secret1,如圖7-23所示。注意,它的名字變?yōu)榱恕癱ode”,但是從值可以看出這就是secret1。不少網(wǎng)站也會(huì)使用這種改名字的方式來迷惑爬蟲開發(fā)者。源代碼里面沒有secret1,因此就要考慮這115圖7-23在另一個(gè)異步請(qǐng)求里面發(fā)現(xiàn)了secret1圖7-23在另一個(gè)異步請(qǐng)求里面發(fā)現(xiàn)了secret1116

這一條請(qǐng)求就是一個(gè)不帶任何參數(shù)的GET請(qǐng)求,請(qǐng)求的頭部信息如圖7-24所示。

對(duì)于這種多次請(qǐng)求才能得到數(shù)據(jù)的情況,解決辦法就是逐一請(qǐng)求,得到返回結(jié)果以后再發(fā)起下一個(gè)請(qǐng)求。具體到這個(gè)例子中,那就是先從源代碼里面獲得secret2,再通過GET請(qǐng)求得到secret1,最后使用secret1和secret2來獲取頁面上顯示的內(nèi)容。

使用Python來實(shí)現(xiàn)這個(gè)過程,代碼和運(yùn)行結(jié)果如圖7-25所示。這一條請(qǐng)求就是一個(gè)不帶任何參數(shù)的GET請(qǐng)求117

圖7-24獲得secret1的請(qǐng)求的頭部信息

圖7-25使用Python模擬多次異步請(qǐng)求并獲得頁面上的值圖7-24獲得secret1的請(qǐng)求的頭部信息1187.1.6基于異步加載的簡單登錄網(wǎng)站的登錄方式有很多種,其中有一種比較簡單的方式,就是使用AJAX發(fā)送請(qǐng)求來進(jìn)行登錄。請(qǐng)打開AJAX第4個(gè)練習(xí)頁http://exercise./exercise_ajax_4.html,這個(gè)頁面實(shí)現(xiàn)了簡單的登錄功能。頁面打開以后的效果如圖7-26所示。根據(jù)輸入框中的提示,使用用戶名“kingname”和密碼“genius”進(jìn)行登錄,可以看到登錄成功以后彈出圖7-27所示的提示框。7.1.6基于異步加載的簡單登錄網(wǎng)站的登錄方式119

圖7-26使用異步加載實(shí)現(xiàn)的登錄頁面

圖7-27登錄成功后彈出的提示框圖7-26使用異步加載實(shí)現(xiàn)的登錄頁面120

對(duì)于這種簡單的登錄功能,可以使用抓取異步加載網(wǎng)頁的方式來進(jìn)行處理。在Chrome開發(fā)者工具中可以發(fā)現(xiàn),當(dāng)單擊“登錄”按鈕時(shí),網(wǎng)頁向后臺(tái)發(fā)送了一條請(qǐng)求,如圖7-28所示。圖7-28登錄過程實(shí)際上是一個(gè)異步的請(qǐng)求對(duì)于這種簡單的登錄功能,可以使用抓取異步加121

這條請(qǐng)求返回的內(nèi)容就是“通關(guān)口令”。再來看看這個(gè)請(qǐng)求發(fā)送了哪些數(shù)據(jù),如圖7-29所示。圖7-29登錄請(qǐng)求發(fā)送的數(shù)據(jù)這條請(qǐng)求返回的內(nèi)容就是“通關(guān)口令”。再來看看122

這就是使用POST方式的最簡單的AJAX請(qǐng)求。使用獲取POST方式的AJAX請(qǐng)求的代碼,就能成功獲取到登錄以后返回的內(nèi)容,如圖7-30所示。圖7-30使用AJAX請(qǐng)求獲得登錄返回的內(nèi)容這就是使用POST方式的最簡單的AJAX請(qǐng)求1237.2請(qǐng)求頭(Headers)7.2.1請(qǐng)求頭的作用使用計(jì)算機(jī)網(wǎng)頁版外賣網(wǎng)站的讀者應(yīng)該會(huì)發(fā)現(xiàn)這樣一個(gè)現(xiàn)象:第一次登錄外賣網(wǎng)頁的時(shí)候會(huì)讓你選擇當(dāng)前所在的商業(yè)圈,一旦選定好之后關(guān)閉瀏覽器再打開,網(wǎng)頁就會(huì)自動(dòng)定位到先前選擇的商業(yè)圈。又比如,例如攜程的網(wǎng)站,使用計(jì)算機(jī)瀏覽器打開的時(shí)候,頁面看起來非常復(fù)雜多樣,如圖7-31所示。但同一個(gè)網(wǎng)址,使用手機(jī)瀏覽器打開時(shí),網(wǎng)址會(huì)自動(dòng)發(fā)生改變,而且得到的頁面竟然完全不同,如圖7-32所示。7.2請(qǐng)求頭(Headers)7.2.1請(qǐng)求頭的作用124

圖7-31計(jì)算機(jī)網(wǎng)頁版攜程首頁

圖7-32手機(jī)版攜程首頁圖7-31計(jì)算機(jī)網(wǎng)頁版攜程首頁125

網(wǎng)站怎么知道現(xiàn)在是計(jì)算機(jī)瀏覽器還是手機(jī)瀏覽器在訪問這個(gè)頁面呢?網(wǎng)站怎么能記住地理位置呢?這就要?dú)w功于Headers了。

Headers稱為請(qǐng)求頭,瀏覽器可以將一些信息通過Headers傳遞給服務(wù)器,服務(wù)器也可以將一些信息通過Headers傳遞給瀏覽器。電商網(wǎng)站常常應(yīng)用的Cookies就是Headers里面的一個(gè)部分。網(wǎng)站怎么知道現(xiàn)在是計(jì)算機(jī)瀏覽器還是手機(jī)瀏覽1267.2.2偽造請(qǐng)求頭打開練習(xí)頁/exercise_headers.html,使用Chrome的開發(fā)者工具監(jiān)控這個(gè)頁面的網(wǎng)頁請(qǐng)求,可以看到圖7-33所示的內(nèi)容。圖7-33請(qǐng)求頭練習(xí)頁7.2.2偽造請(qǐng)求頭打開練習(xí)頁http://127

頁面看起來像是發(fā)起了一個(gè)普通的GET方式的異步請(qǐng)求給/exercise_headers_backend。使用requests嘗試獲取這個(gè)網(wǎng)址的返回信息,結(jié)果如圖7-34所示。圖7-34使用requests訪問請(qǐng)求頭練習(xí)頁面失敗頁面看起來像是發(fā)起了一個(gè)普通的GET方式的128

使用瀏覽器訪問網(wǎng)站的時(shí)候,網(wǎng)站可以看到一個(gè)名稱為Headers(請(qǐng)求頭)的東西,它的內(nèi)容如圖7-35所示。圖7-35使用瀏覽器訪問網(wǎng)站后臺(tái)顯示的Headers信息使用瀏覽器訪問網(wǎng)站的時(shí)候,網(wǎng)站可以看到一129

如果使用requests訪問,請(qǐng)求頭的內(nèi)容如圖7-36所示。圖7-36使用requests訪問網(wǎng)站,后臺(tái)顯示的Headers信息如果使用requests訪問,請(qǐng)求頭的內(nèi)容130

為了解決這個(gè)問題,就需要給爬蟲“換頭”。把瀏覽器的頭安裝到爬蟲的身上,這樣網(wǎng)站就不知道誰是誰了。要換頭,首先就需要知道瀏覽器的頭是什么樣的。因此需要在Chrome瀏覽器開發(fā)者工具的“Network”選項(xiàng)卡的RequestHeaders里面觀察這一次請(qǐng)求的請(qǐng)求頭,如圖7-37所示。為了解決這個(gè)問題,就需要給爬蟲“換頭”。把131圖7-37瀏覽器發(fā)起的請(qǐng)求的頭部信息圖7-37瀏覽器發(fā)起的請(qǐng)求的頭部信息132

在requests里面,設(shè)置請(qǐng)求頭的參數(shù)名稱為“headers”,它的值是一個(gè)字典。帶有請(qǐng)求頭的請(qǐng)求,使用requests的發(fā)送格式為:html=requests.get(url,headers=字典).content.decode()html=requests.post(url,json=xxx,headers=字典).content.decode()

代碼中的字典就對(duì)應(yīng)了瀏覽器中的請(qǐng)求頭。在爬蟲里面創(chuàng)建一個(gè)字典,將Chrome的請(qǐng)求頭的內(nèi)容復(fù)制進(jìn)去,并調(diào)整好格式,發(fā)起一個(gè)帶有Chrome請(qǐng)求頭的爬蟲請(qǐng)求,可以發(fā)現(xiàn)請(qǐng)求獲得成功,如圖7-38所示。在requests里面,設(shè)置請(qǐng)求頭的參數(shù)名133圖7-38更換了Chrome頭部以后爬蟲訪問成功圖7-38更換了Chrome頭部以后爬蟲訪問成功134

雖然對(duì)于某些網(wǎng)站,在請(qǐng)求頭里面只需要設(shè)置User-Agent就可以正常訪問了,但是為了保險(xiǎn)起見,還是建議把所有項(xiàng)目都帶上,這樣可以讓爬蟲更“像”瀏覽器。例如本練習(xí),如果僅僅設(shè)置User-Agent的話,會(huì)得到圖7-39所示的返回信息。圖7-39僅僅修改User-Agent是不能騙過練習(xí)網(wǎng)站的雖然對(duì)于某些網(wǎng)站,在請(qǐng)求頭里面只需要設(shè)置U1357.3模擬瀏覽器

有一些網(wǎng)站在發(fā)起AJAX請(qǐng)求的時(shí)候,會(huì)帶上特殊的字符串用于身份驗(yàn)證。這種字符串稱為Token。為了簡單起見,請(qǐng)打開練習(xí)頁面,這個(gè)頁面在發(fā)起AJAX請(qǐng)求的時(shí)候會(huì)在Headers中帶上一個(gè)參數(shù)ReqTime;在POST發(fā)送的數(shù)據(jù)中會(huì)有一個(gè)參數(shù)sum,如圖7-40所示。7.3模擬瀏覽器有一些網(wǎng)站在發(fā)起AJ136圖7-40較為復(fù)雜的異步加載練習(xí)頁面圖7-40較為復(fù)雜的異步加載練習(xí)頁面137

多次刷新頁面,可以發(fā)現(xiàn)ReqTime和sum一直在變化。如果requests只固定使用某個(gè)ReqTime與sum的組合來發(fā)起請(qǐng)求,就會(huì)出現(xiàn)圖7-41所示的返回信息。

不難看出ReqTime是精確到毫秒的時(shí)間戳,即使使用Python生成了一個(gè)時(shí)間戳,也不能得到網(wǎng)頁上面的內(nèi)容,如圖7-42所示。多次刷新頁面,可以發(fā)現(xiàn)ReqTime和sum138

圖7-41如果使用固定的參數(shù)就會(huì)導(dǎo)致爬蟲爬不到數(shù)據(jù)

圖7-42僅僅修改時(shí)間戳是不能讓爬蟲成功的圖7-41如果使用固定的參數(shù)就會(huì)導(dǎo)致爬蟲爬不到數(shù)據(jù)1397.3.1Selenium介紹雖然在網(wǎng)頁的源代碼中無法看到被異步加載的內(nèi)容,但是在Chrome的開發(fā)者工具的“Elements”選項(xiàng)卡下卻可以看到網(wǎng)頁上的內(nèi)容,如圖7-43所示。圖7-43在開發(fā)者工具的“Elements”選項(xiàng)卡下可以看到被加載的內(nèi)容7.3.1Selenium介紹雖然在網(wǎng)頁的源代1407.3.2Selenium安裝使用pip安裝Selenium:pipinstallselenium安裝情況如圖7-44所示。圖7-44安裝Selenium7.3.2Selenium安裝使用pip安裝141

下載ChromeDriver,根據(jù)自己的系統(tǒng)選擇合適的版本,如圖7-45所示。圖7-45根據(jù)自己的系統(tǒng)選擇合適的版本下載ChromeDriver,根據(jù)自己的系統(tǒng)1427.3.3Selenium的使用1.獲取源代碼將chromedriver與代碼放在同一個(gè)文件夾中以方便代碼直接調(diào)用。初始化Selenium只需要兩行代碼,導(dǎo)入Selenium庫,再指定WebDriver,如圖7-46所示。圖7-46初始化Selenium7.3.3Selenium的使用1.獲取源代碼圖7-46143

第3行代碼指定了Selenium使用ChromeDriver來操作Chrome解析網(wǎng)頁,括號(hào)里的參數(shù)就是ChromeDriver可執(zhí)行文件的地址。

如果要使用PhantomJS,只需要修改第3行代碼即可:driver=webdriver.PhantomJS('./phantomjs')

同樣,需要將PhantomJS的可執(zhí)行文件與代碼放在一起。

需要特別提醒的是,如果chromedriver與代碼不在一起,可以通過絕對(duì)路徑來指定,例如:driver=webdriver.Chrome('/usr/bin/chromedriver')第3行代碼指定了Selenium使用Chr144

使用Windows的讀者在寫這個(gè)參數(shù)的時(shí)候,要注意反斜杠的問題?!癨”這個(gè)符號(hào)叫作反斜杠,在Windows中作為路徑的分隔符。但是由于轉(zhuǎn)義字符也是反斜杠,所以如果把Windows下面的代碼寫為下面這樣就會(huì)出問題。driver=webdriver.Chrome('C:\server\chromedriver.exe')

因此,使用Windows的讀者可在路徑字符串左引號(hào)的左邊加一個(gè)“r”符號(hào),將代碼寫為:driver=webdriver.Chrome(r'C:\server\chromedriver.exe')

這樣Python就能正確處理反斜杠的問題。使用Windows的讀者在寫這個(gè)參數(shù)的時(shí)候,145

初始化完成以后,就可以使用Selenium打開網(wǎng)頁了。要打開一個(gè)網(wǎng)頁只需要一行代碼:driver.get('/exercise_advanced_ajax.html')

代碼運(yùn)行以后會(huì)自動(dòng)打開一個(gè)Chrome窗口,并在窗口里面自動(dòng)進(jìn)入這個(gè)網(wǎng)址對(duì)應(yīng)的頁面。一旦被異步加載的內(nèi)容已經(jīng)出現(xiàn)在了這個(gè)自動(dòng)打開的Chrome窗口中,那么此時(shí)使用下列代碼:html=driver.page_source

就能得到在Chrome開發(fā)者工具中出現(xiàn)的HTML代碼,如圖7-47所示。初始化完成以后,就可以使用Selenium打146圖7-47在ChromeDriver加載頁面完成以后可以得到加載以后的源代碼圖7-47在ChromeDriver加載頁面完成以后可以1472.等待信息出現(xiàn)

圖7-47所示的代碼第6行設(shè)置了一個(gè)5s的延遲,這是由于Selenium并不會(huì)等待網(wǎng)頁加載完成再執(zhí)行后面的代碼。它只是向ChromeDriver發(fā)送了一個(gè)命令,讓ChromeDriver打開某個(gè)網(wǎng)頁。

至于網(wǎng)頁要開多久,Selenium并不關(guān)心。由于被異步加載的內(nèi)容會(huì)延遲出現(xiàn),因此需要等待它出現(xiàn)以后再開始抓取。2.等待信息出現(xiàn)1483.在網(wǎng)頁中獲取元素

在網(wǎng)頁中尋找需要的內(nèi)容,可以使用類似于BeautifulSoup4的語法:element=driver.fin

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論