有時候咱們使用瀏覽器查看頁面正常顯示的數據與使用requests抓取頁面獲得的數據不一致,這是由於requests獲取的是原始的HTML文檔,而瀏覽器中的頁面是通過JavaScript處理數據後的結果。這些數據多是經過Ajax加載的,可能包含HTML文檔中,可能通過特定算法計算後生成的。html
Ajax,全稱爲Asynchronous JavaScript and XML,即異步的JavaScript和XML。它是利用JavaScript在保證頁面不被刷新,鏈接不變的狀況下服務器交換數據並更新部分網頁的技術。node
瀏覽網頁的時候,咱們發現不少網頁都有下滑查看更多的選項。好比,就拿新浪微博主頁來講。一直往下滑,看到幾個微博以後就沒有了,而是會出現一個加載的動畫,很快就出現了新的微博內容。這個過程就是Ajax加載的過程,以下圖:ajax
發送Ajax請求到網頁更新的過程能夠簡單的分爲三步:算法
1.發送請求json
2.解析內容瀏覽器
3.渲染頁面服務器
♦ 發送請求異步
var xmlhttp; if (window.XMLHttpRequest) { // IE7,Firefox,Chrome,Safari,opera xmlhttp = new XMLHttpRequest() } else { // IE6,IE5 xmlhttp = new ActiveXObject('Microsoft.XMLHTTP'); } xmlhttp.onreadystatechange = function () { if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { document.getElementById("content").innerHTML = xmlhttp.responseText; } }; xmlhttp.open('POST', '/ajax', true); xmlhttp.send()
這是使用JavaScript對Ajax的底層實現,其實是新建了XMLHttpRequest對象,而後調用onreadystatechange實現設置了監聽,而後使用open()和send()方法向某個鏈接(也就是服務器)發送請求。響應返回時監聽對應的方法便觸發,解析響應內容。ide
♦ 解析內容工具
onreadystatechange對應的屬性觸發後,利用xmlhttp的responseText屬性獲取響應內容。
♦渲染網頁
解析響應完成以後,經過document.getElementById("content").innerHTML這樣的方法對某個元素內部的HTML代碼進行更改,從而渲染網頁。這樣的操做也稱爲DOM操做,即對Document進行操做。
所以,咱們知道了真實的數據都是一次次Ajax請求獲得的,若是想要抓取這些數據,須要知道這些請求究竟是怎麼發送的。以後再使用Python進行模擬發送操做,獲取到其中的結果。
使用Chrome瀏覽器訪問新浪微博首頁,打開開發者工具。切換到Network選項卡,從新刷新頁面,看到很是多的條目。
Ajax請求其實有它特殊的請求類型,叫作xhr。在途中Type對應請求類型中,點擊圖中的XHR能夠過濾出全部的xhr請求。找到其中一個xhr的請求,點擊進去查看詳細內容。其中Request Headers中有一條信息爲X-Requested-With:XMLHttpRequest,這就標記了次請求是Ajax請求。以下圖
使用開發者工具打開Ajax的XHR過濾器,而後一直向下滑動頁面,咱們會看到不斷有Ajax請求發出。選定其中一個請求,分析其參數信息,進入請求詳情。以下圖:
能夠發現,這是一個GET請求,url爲https://weibo.com/a/aj/transform/loadingmoreunlogin? ajwvr=6&category=0&page=3&lefnav=0&cursor=&__rnd=1559115353265。請求的參數有六個:ajwvr,category,page,lefnav,cursor,__rnd。
再看看其餘請求,發現只有page,__rnd這兩個參數在改變。很明顯page是用來控制分頁的,細心觀察__rnd的值爲對應的時間戳。
觀察這個請求的響應內容:
這個內容的格式爲JSON,其中主要的內容在data對應的值裏面。這樣咱們請求一個接口,改變page參數就能夠得到對應數據。
這裏咱們來模擬這戲Ajax請求,將前10頁的數據爬取下來。
# _*_ coding=utf-8 _*_ import requests, time from urllib.parse import urlencode base_url = 'https://weibo.com/a/aj/transform/loadingmoreunlogin?' headers = { 'Host': 'weibo.com', 'Referer': 'https://weibo.com/', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36', 'X-Requested-With': 'XMLHttpRequest', } def get_page(page): """ :param page: :return: """ # 構造__rnd參數 rnd = int(time.time()) # 構造參數字典 params = { 'ajwvr': '6', 'category': '0', 'page': page, 'lefnav': '0', 'cursor': '', '__rnd': rnd } # 拼接參數與url url = base_url + urlencode(params) try: res = requests.get(url, headers=headers) if res.status_code == 200: return res.json() except Exception as e: print('Error:', e.args) def parse(res): weibo = {} if res: weibo['data'] = res.get('data') yield weibo if __name__ == "__main__": for page in range(1, 11): result = get_page(page) weibo_data = parse(result) for data in weibo_data: print(data)
運行結果:
{'data': ' <!--榜單欄位置-->\n <!--/ card-->\r\n<div class="UG_slider" >\r\n <ul action-type="header_slider" node-type="header_slider">\r\n <li>\r\n <a href="/a/hot/7562265474177025_1.html?type=new" target="_blank" suda-uatrack="key=www_unlogin_home&value=focus01">\r\n <img src="https://wx2.sinaimg.cn/crop.0.61.600.337/60718250ly1g3hxko6uxbj20go0b30t9.jpg" class="pic"><div class="pic_intro">頭條新聞今日快訊 | 華爲在美提起訴訟.....}
這樣咱們就經過分析Ajax請求並編寫爬蟲獲取到微博數據,固然代碼還可更優化,還能夠解析具體的標題、內容,這裏只是演示Ajax請求的模擬過程,爬取結果並非重點。