第7章-異步加載與請求頭_第1頁
第7章-異步加載與請求頭_第2頁
第7章-異步加載與請求頭_第3頁
第7章-異步加載與請求頭_第4頁
第7章-異步加載與請求頭_第5頁
已閱讀5頁,還剩77頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

PythonCrawlerDevelopment極客學院J互聯網+職業(yè)技能系列

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

著第7章異步加載與請求頭

如果讀者在本世紀初就接觸過互聯網,那么應該會記得,那個時候每單擊一個鏈接,瀏覽器就會短暫地“白屏”一兩秒,然后才會進入一個新的頁面。不同的頁面,網址也是不一樣的。

隨著技術的不斷進步,現在不少網站已經引入了異步加載技術,單擊新的鏈接以后,幾乎看不到“白屏”的現象了。而且更神奇的是,單擊了鏈接,網頁的內容已經發(fā)生了改變,但是網址竟然沒有變。

通過這一章的學習,你將會掌握如下知識。(1)抓取異步加載的數據。(2)偽造HTTP請求頭。(3)模擬瀏覽器獲取網站數據。

7.1

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

使用AJAX技術,可以在不刷新網頁的情況下更新網頁數據。使用AJAX技術的網頁,一般會使用HTML編寫網頁的框架。

在打開網頁的時候,首先加載的是這個框架。剩下的部分將會在框架加載完成以后再通過JavaScript從后臺加載。如何判斷一個網頁有沒有使用AJAX技術呢?請訪問http:///exercise_ajax_1.html,這個頁面用瀏覽器訪問的結果如圖7-1所示。但是如果檢查它的源代碼,會發(fā)現源代碼里面并沒有網頁上面顯示的這兩段文字,如圖7-2所示。

圖7-1異步加載練習頁面1

圖7-2使用異步加載技術的網頁,數據不在源代碼中7.1.2JSON介紹與應用JSON的全稱是JavaScriptObjectNotation,是一種輕量級的數據交換格式。網絡之間使用HTTP方式傳遞數據的時候,絕大多數情況下傳遞的都是字符串。

因此,當需要把Python里面的數據發(fā)送給網頁或者其他編程語言的時候,可以先將Python的數據轉化為JSON格式的字符串,然后將字符串傳遞給其他語言,其他語言再將JSON格式的字符串轉換為它自己的數據格式。

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

'work_info':{'salary':99999,'position':'engineer','department':None}}

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

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

這里得到的person_dict就是一個字典,可以像使用普通字典一樣來使用它,如圖7-4所示。

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

圖7-4把JSON格式的字符串轉換為字典7.1.3異步GET與POST請求

使用異步加載技術的網站,被加載的內容是不能在源代碼中找到的。對于這種情況,應該如何抓取被加載的內容呢?

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

圖7-5打開GoogleChrome開發(fā)者工具

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

圖7-6刷新網頁以后,“Network”選項卡下出現的內容

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

圖7-8被異步加載的數據之二,使用POST方式

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

于是,嘗試使用requests發(fā)送這個請求,即可成功獲取到網頁上的第1條內容,如圖7-10所示。

圖7-9使用GET方式的異步請求的頭部信息

圖7-10使用requests獲得被異步加載的信息

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

使用requests發(fā)送這個請求,也成功地獲取了網頁上面的第2條信息。通過修改請求的數據內容,還能夠修改網頁的返回內容,如圖7-12所示。圖7-11使用POST方式的異步請求的頭部信息圖7-12使用requests模擬發(fā)送POST請求獲取第2條異步加載內容7.1.4特殊的異步加載7.1.3小節(jié)中介紹的是最常見、最簡單的異步加載情況,但并非所有的異步加載都會向后臺發(fā)送請求。打開AJAX的第2個練習頁面,可以看到頁面上有圖7-13所示的內容。

圖7-13異步加載練習頁面2分析Chrome開發(fā)者工具的“Network”選項卡下面的內容,可以看到整個頁面的打開過程并沒有嘗試請求后臺的行為。其中的exercise_ajax_2.html就是這個頁面自身,而jquery-3.2.1.min.js是jQuery的庫,都不是對后臺的請求。打開網頁源代碼可以看到,確實沒有“天王蓋地虎”這幾個漢字,如圖7-14所示。圖7-14網頁源代碼中確實沒有網頁中的內容

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

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

url='/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'])

運行后的結果如圖7-16所示。圖7-16獲取假異步加載的數據7.1.5多次請求的異步加載圖7-17異步加載練習頁面3還有一些網頁,顯示在頁面上的內容要經過多次異步請求才能得到。第1個AJAX請求返回的是第2個請求的參數,第2個請求的返回內容又是第3個請求的參數,只有得到了上一個請求里面的有用信息,才能發(fā)起下一個請求。打開異步加載練習頁3,頁面內容如圖7-17所示。

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

其中,返回的JSON格式的字符串經過Python解析,可以得到頁面上的文字,如圖7-19所示。圖7-19使用Python解析發(fā)現請求返回的內容確實是頁面內容

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

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

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

圖7-22在源代碼中找到secret_2

源代碼里面沒有secret1,因此就要考慮這個參數是不是來自于另一個異步請求。

繼續(xù)在開發(fā)者工具中查看其他請求,可以成功找到secret1,如圖7-23所示。注意,它的名字變?yōu)榱恕癱ode”,但是從值可以看出這就是secret1。不少網站也會使用這種改名字的方式來迷惑爬蟲開發(fā)者。圖7-23在另一個異步請求里面發(fā)現了secret1

這一條請求就是一個不帶任何參數的GET請求,請求的頭部信息如圖7-24所示。

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

使用Python來實現這個過程,代碼和運行結果如圖7-25所示。

圖7-24獲得secret1的請求的頭部信息

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

圖7-26使用異步加載實現的登錄頁面

圖7-27登錄成功后彈出的提示框

對于這種簡單的登錄功能,可以使用抓取異步加載網頁的方式來進行處理。在Chrome開發(fā)者工具中可以發(fā)現,當單擊“登錄”按鈕時,網頁向后臺發(fā)送了一條請求,如圖7-28所示。圖7-28登錄過程實際上是一個異步的請求

這條請求返回的內容就是“通關口令”。再來看看這個請求發(fā)送了哪些數據,如圖7-29所示。圖7-29登錄請求發(fā)送的數據

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

圖7-31計算機網頁版攜程首頁

圖7-32手機版攜程首頁

網站怎么知道現在是計算機瀏覽器還是手機瀏覽器在訪問這個頁面呢?網站怎么能記住地理位置呢?這就要歸功于Headers了。

Headers稱為請求頭,瀏覽器可以將一些信息通過Headers傳遞給服務器,服務器也可以將一些信息通過Headers傳遞給瀏覽器。電商網站常常應用的Cookies就是Headers里面的一個部分。7.2.2偽造請求頭打開練習頁/exercise_headers.html,使用Chrome的開發(fā)者工具監(jiān)控這個頁面的網頁請求,可以看到圖7-33所示的內容。圖7-33請求頭練習頁

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

使用瀏覽器訪問網站的時候,網站可以看到一個名稱為Headers(請求頭)的東西,它的內容如圖7-35所示。圖7-35使用瀏覽器訪問網站后臺顯示的Headers信息

如果使用requests訪問,請求頭的內容如圖7-36所示。圖7-36使用requests訪問網站,后臺顯示的Headers信息

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

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

代碼中的字典就對應了瀏覽器中的請求頭。在爬蟲里面創(chuàng)建一個字典,將Chrome的請求頭的內容復制進去,并調整好格式,發(fā)起一個帶有Chrome請求頭的爬蟲請求,可以發(fā)現請求獲得成功,如圖7-38所示。圖7-38更換了Chrome頭部以后爬蟲訪問成功

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

有一些網站在發(fā)起AJAX請求的時候,會帶上特殊的字符串用于身份驗證。這種字符串稱為Token。為了簡單起見,請打開練習頁面,這個頁面在發(fā)起AJAX請求的時候會在Headers中帶上一個參數ReqTime;在POST發(fā)送的數據中會有一個參數sum,如圖7-40所示。圖7-40較為復雜的異步加載練習頁面

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

不難看出ReqTime是精確到毫秒的時間戳,即使使用Python生成了一個時間戳,也不能得到網頁上面的內容,如圖7-42所示。

圖7-41如果使用固定的參數就會導致爬蟲爬不到數據

圖7-42僅僅修改時間戳是不能讓爬蟲成功的7.3.1Selenium介紹雖然在網頁的源代碼中無法看到被異步加載的內容,但是在Chrome的開發(fā)者工具的“Elements”選項卡下卻可以看到網頁上的內容,如圖7-43所示。圖7-43在開發(fā)者工具的“Elements”選項卡下可以看到被加載的內容7.3.2Selenium安裝使用pip安裝Selenium:pipinstallselenium安裝情況如圖7-44所示。圖7-44安裝Selenium

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

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

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

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

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

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

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

這樣Python就能正確處理反斜杠的問題。

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

代碼運行以后會自動打開一個Chrome窗口,并在窗口里面自動進入這個網址對應的頁面。一旦被異步加載的內容已經出現在了這個自動打開的Chrome窗口中,那么此時使用下列代碼:html=driver.page_source

就能得到在Chrome開發(fā)者工具中出現的HTML代碼,如圖7-47所示。圖7-47在ChromeDriver加載頁面完成以后可以得到加載以后的源代碼2.等待信息出現

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

至于網頁要開多久,Selenium并不關心。由于被異步加載的內容會延遲出現,因此需要等待它出現以后再開始抓取。3.在網頁中獲取元素

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

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

對于練習網站,使用XPath獲取網頁的內容,運行結果如圖7-48所示。圖7-48使用Selenium和ChromeDriver獲得練習網站的內容7.4階段案例在樂視網上尋找一個視頻,爬取視頻的評論信息。7.4.1需求分析目標網站:。目標內容:爬取視頻評論。涉及的知識點:(1)分析網站的異步加載請求。(2)使用requests發(fā)送請求。7.4.2核心代碼構建在樂視網上打開一個視頻,可以看到其部會評論頁面如圖7-49所示。圖7-49樂視網部分視頻評論頁面

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

可以使用Python來模擬這個請求,從而獲取視頻的評論信息。

在請求的URL里面有兩個參數:vid和pid。這兩個參數在網頁的源代碼里面都可以找到,如圖7-51所示。

圖7-50使用Chrome開發(fā)者工具觀察評論的異步加載請求圖7-51在網頁源代碼里面尋找pid和vid

爬蟲首先訪問視頻頁面,通過正則表達式獲取vid和pid,并將結果保存到“necessary_info”這個類屬性對應的字典中。核心代碼如下:defget_necessary_id(self):source=self.get_source(self.url,self.HEADERS)vid=re.search('v

溫馨提示

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

最新文檔

評論

0/150

提交評論