一、前言? ? ? ??
二、什么是Ajax
?三、Ajax的基本原理
3.1 發送請求
3.2 解析內容
3.3 渲染網頁
3.4 總結?
四、Ajax 分析
五、過濾請求-篩選所有Ajax請求
一、前言? ? ? ??
????????當我們在用 requests 抓取頁面的時候,得到的結果可能會和在瀏覽器中看到的不一樣:在瀏覽器中正常顯示的頁面數據,使用 requests 卻沒有得到結果。這是因為 requests 獲取的都是原始 HTML 文檔,而瀏覽器中的頁面則是經過 JavaScript 數據處理后生成的結果。這些數據的來源有多種,可能是通過 Ajax 加載的,可能是包含在 HTML 文檔中的,也可能是經過 JavaScript 和特定算法計算后生成的。
????????數據加載是一種異步加載方式,原始頁面不會包含某些數據,只有在加載完后,才會向服務器請求某個接口獲取數據,然后數據才被處理從而呈現到網頁上,這個過程實際上就是向服務器接口發送了一個 Ajax 請求。
????????網頁的原始 HTML 文檔不會包含任何數據,數據都是通過 Ajax 統一加載后再呈現出來的,這樣在 Web 開發上可以做到前后端分離(在后端專欄中我們就用的ajax實現前后端分離),并且降低服務器直接渲染頁面帶來的壓力。
????????所以如果你遇到這樣的頁面,直接利用 requests 等庫來抓取原始頁面,是無法獲取有效數據的。這時我們需要分析網頁后臺向接口發送的 Ajax 請求,如果可以用 requests 來模擬 Ajax 請求,就可以成功抓取了。
????????所以,本課時我們就來了解什么是 Ajax 以及如何去分析和抓取 Ajax 請求。
二、什么是Ajax
????????Ajax,全稱為 Asynchronous JavaScript and XML,即異步的 JavaScript 和 XML。它不是一門編程語言,而是利用 JavaScript 在保證頁面不被刷新、頁面鏈接不改變的情況下與服務器交換數據并更新部分網頁的技術。
????????傳統的網頁,如果你想更新其內容,那么必須要刷新整個頁面。有了 Ajax,便可以在頁面不被全部刷新的情況下更新其內容。在這個過程中,頁面實際上在后臺與服務器進行了數據交互,獲取到數據之后,再利用 JavaScript 改變網頁,這樣網頁內容就會更新了。
????????可以到 W3School 上體驗幾個 Demo 來感受一下:http://www.w3school.com.cn/ajax/ajax_xmlhttprequest_send.asp。
舉例:
????????瀏覽網頁的時候,我們會發現很多網頁都有下滑查看更多的選項。以cqc老師的微博的主頁為例:https://m.weibo.cn/u/2830678474。我們切換到微博頁面,發現下滑幾個微博后,后面的內容不會直接顯示,而是會出現一個加載動畫,加載完成后下方才會繼續出現新的微博內容,這個過程其實就是 Ajax 加載的過程,如圖所示:
頁面其實并沒有整個刷新,這意味著頁面的鏈接沒有變化,但是網頁中卻多了新內容,也就是后面刷出來的新微博。這就是通過 Ajax 獲取新數據并呈現的過程。
?三、Ajax的基本原理
????????初步了解了 Ajax 之后,我們再來詳細了解它的基本原理。發送 Ajax 請求到網頁更新的過程可以簡單分為以下 3 步:發送請求、解析內容、渲染網頁
????????下面我們分別詳細介紹一下這幾個過程。
3.1 發送請求
?????????JavaScript 可以實現頁面的各種交互功能,Ajax 也不例外,它是由 JavaScript 實現的,發送請求的過程實際上執行了如下代碼:
var xmlhttp;if (window.XMLHttpRequest) {//code for IE7+, Firefox, Chrome, Opera, Safarixmlhttp=new XMLHttpRequest();} else {//code for IE6, IE5xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");}xmlhttp.onreadystatechange=function() {if (xmlhttp.readyState==4 && xmlhttp.status==200) {document.getElementById("myDiv").innerHTML=xmlhttp.responseText;}}xmlhttp.open("POST","/ajax/",true);xmlhttp.send();
????????這是 JavaScript 對 Ajax 最底層的實現,這個過程實際上是? ? ?①新建了 XMLHttpRequest 對象,然后? ? ? ?②調用 onreadystatechange 屬性設置監聽,? ?③最后調用 open() 和 send() 方法向某個鏈接(也就是服務器)發送請求。
????????前面我們用 Python 實現請求發送之后,可以得到響應結果,但這里請求的發送由 JavaScript 來完成。由于設置了監聽,所以當服務器返回響應時,onreadystatechange 對應的方法便會被觸發,我們在這個方法里面解析響應內容即可。
3.2 解析內容
????????得到響應之后,onreadystatechange 屬性對應的方法會被觸發,此時利用 xmlhttp 的 responseText 屬性便可取到響應內容。這類似于 Python 中利用 requests 向服務器發起請求,然后得到響應的過程。
????????返回的內容可能是 HTML,也可能是 JSON,接下來我們只需要在方法中用 JavaScript 進一步處理即可。比如,如果返回的內容是 JSON 的話,我們便可以對它進行解析和轉化。
3.3 渲染網頁
?????????JavaScript 有改變網頁內容的能力,解析完響應內容之后,就可以調用 JavaScript 針對解析完的內容對網頁進行下一步處理。比如,通過 document.getElementById().innerHTML 這樣的操作,對某個元素內的源代碼進行更改,這樣網頁顯示的內容就改變了,這種對 Document 網頁文檔進行如更改、刪除等操作也被稱作 DOM 操作。
????????上例中,document.getElementById("myDiv").innerHTML=xmlhttp.responseText這個操作便將 ID 為 myDiv 的節點內部的 HTML 代碼更改為服務器返回的內容,這樣 myDiv 元素內部便會呈現出服務器返回的新數據,網頁的部分內容看上去就更新了。
3.4 總結?
????????可以看到,發送請求、解析內容和渲染網頁這 3 個步驟其實都是由 JavaScript 完成的。
????????再回想微博的下拉刷新,這其實是 JavaScript 向服務器發送了一個 Ajax 請求,然后獲取新的微博數據,將其解析,并將其渲染在網頁中的過程。
????????因此,真實的數據其實都是通過一次次 Ajax 請求得到的,如果想要抓取這些數據,我們需要知道這些請求到底是怎么發送的,發往哪里,發了哪些參數。如果我們知道了這些,不就可以用 Python 模擬這個發送操作,獲取到其中的結果了嗎?
四、Ajax 分析
????????拖動刷新的內容由 Ajax 加載,而且頁面的 URL 沒有變化,這時我們應該到哪里去查看這些 Ajax 請求呢?
????????這里還需要借助瀏覽器的開發者工具,下面以 Chrome 瀏覽器為例來介紹。
????????用 Chrome 瀏覽器打開微博鏈接 https://m.weibo.cn/u/2830678474,隨后在頁面中點擊鼠標右鍵,從彈出的快捷菜單中選擇“檢查” 選項,此時便會彈出開發者工具,如圖所示:
這里就是頁面加載過程中瀏覽器與服務器之間發送請求和接收響應的所有記錄。
????????Ajax 有其特殊的請求類型,它叫作 xhr。在圖中我們可以發現一個以 getIndex 開頭的請求,其 Type 為 xhr,這就是一個 Ajax 請求。用鼠標點擊這個請求,可以查看這個請求的詳細信息。
????????在右側可以觀察到 Request Headers、URL 和 Response Headers 等信息。Request Headers 中有一個信息為 X-Requested-With:XMLHttpRequest,這就標記了此請求是 Ajax 請求,如圖所示:
??????????隨后我們點擊 Preview,即可看到響應的內容,它是 JSON 格式的。這里 Chrome 為我們自動做了解析,點擊箭頭即可展開和收起相應內容。
????????我們可以觀察到,返回結果是cqc老師微博的個人信息,包括昵稱、簡介、頭像等,這也是用來渲染個人主頁所使用的數據。JavaScript 接收到這些數據之后,再執行相應的渲染方法,整個頁面就渲染出來了。
Response 選項卡,從中可以觀察到真實的返回數據,切回到第一個請求,觀察一下它的 Response 是什么,如圖所示:
????????這就是最原始鏈接?https://m.weibo.cn/u/2830678474?返回的結果,結構非常簡單,只是執行了一些 JavaScript。所以說,我們看到的微博頁面的真實數據并不是最原始的頁面返回的,而是在執行 JavaScript 后再次向后臺發送 Ajax 請求,瀏覽器拿到數據后進一步渲染出來的。
五、過濾請求-篩選所有Ajax請求
????????接下來,我們再利用 Chrome 開發者工具的篩選功能篩選出所有的 Ajax 請求。在請求的上方有一層篩選欄,直接點擊 XHR,此時在下方顯示的所有請求便都是 Ajax 請求了,如圖所示:
????????接下來,不斷滑動頁面,可以看到頁面底部有一條條新的微博被刷出,而開發者工具下方也不斷地出現 Ajax 請求,這樣我們就可以捕獲到所有的 Ajax 請求了。
????????隨意點開一個條目,都可以清楚地看到其 Request URL、Request Headers、Response Headers、Response Body 等內容,此時想要模擬請求和提取就非常簡單了。
下圖所示的內容便是某一頁微博的列表信息:
一段 JSON 結構化數據?:
ok: 1---
表示 API 請求成功,返回了有效數據。
data:
這是包含微博數據的主要對象。
cardlistInfo
?????
該對象包含列表的元信息,如微博數量、分頁參數等。
containerid: "1076032830678474"
微博列表的唯一 ID(可能是用戶 ID)。
v_p: 42
可能是版本或分頁相關的參數。
show_style: 1
控制微博列表的顯示樣式。
total: 2090
表示該用戶的總微博數為 2090 條。
autoLoadMoreIndex: 10
自動加載的分頁索引。
since_id: 5128176110668256
用于分頁的since_id
,下次請求可以用它獲取更早的微博。
cards
(微博數據列表,每條微博有 ID、發布時間、URL 等)
scheme
(微博 APP 內部跳轉鏈接)
????????到現在為止,我們已經可以分析出 Ajax 請求的一些詳細信息了,接下來只需要用程序模擬這些 Ajax 請求,就可以輕松提取我們所需要的信息了。
???????