這裡仍然以微博為例,接下來用Python來模擬這些Ajax請求,把我發過的微博爬取下來。
1. 分析請求
開啟Ajax的XHR過濾器,然後一直滑動頁面以載入新的微博內容。可以看到,會不斷有Ajax請求發出。
選定其中一個請求,分析它的引數資訊。點選該請求,進入詳情頁面,如圖6-11所示。
圖6-11 詳情頁面
可以發現,這是一個GET型別的請求,請求連結為[https://m.weibo.cn/api/container/getIndex?type=uid&value=2830678474&containerid=1076032830678474&page=2)。請求的引數有4個:type
、value
、containerid
和page
。
隨後再看看其他請求,可以發現,它們的type
、value
和containerid
始終如一。type
始終為uid
,value
的值就是頁面連結中的數字,其實這就是使用者的id
。另外,還有containerid
。可以發現,它就是107603加上使用者id
。改變的值就是page
,很明顯這個引數是用來控制分頁的,page=1
代表第一頁,page=2
代表第二頁,以此類推。
2. 分析響應
隨後,觀察這個請求的響應內容,如圖6-12所示。
圖6-12 響應內容
這個內容是JSON格式的,瀏覽器開發者工具自動做了解析以方便我們檢視。可以看到,最關鍵的兩部分資訊就是cardlistInfo
和cards
:前者包含一個比較重要的資訊total
,觀察後可以發現,它其實是微博的總數量,我們可以根據這個數字來估算分頁數;後者則是一個列表,它包含10個元素,展開其中一個看一下,如圖6-13所示。
圖6-13 列表內容
可以發現,這個元素有一個比較重要的欄位mblog
。展開它,可以發現它包含的正是微博的一些資訊,比如attitudes_count
(贊數目)、comments_count
(評論數目)、reposts_count
(轉發數目)、created_at
(釋出時間)、text
(微博正文)等,而且它們都是一些格式化的內容。
這樣我們請求一個介面,就可以得到10條微博,而且請求時只需要改變page
引數即可。
這樣的話,我們只需要簡單做一個迴圈,就可以獲取所有微博了。
3. 實戰演練
這裡我們用程式模擬這些Ajax請求,將我的前10頁微博全部爬取下來。
首先,定義一個方法來獲取每次請求的結果。在請求時,page
是一個可變引數,所以我們將它作為方法的引數傳遞進來,相關程式碼如下:
12345678910111213141516171819202122232425from urllib.parse import urlencodeimport requestsbase_url = 'https://m.weibo.cn/api/container/getIndex?' headers = { 'Host': 'm.weibo.cn', 'Referer': 'https://m.weibo.cn/u/2830678474', 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36', 'X-Requested-With': 'XMLHttpRequest',} def get_page(page): params = { 'type': 'uid', 'value': '2830678474', 'containerid': '1076032830678474', 'page': page } url = base_url + urlencode(params) try: response = requests.get(url, headers=headers) if response.status_code == 200: return response.json() except requests.ConnectionError as e: print('Error', e.args)複製程式碼
首先,這裡定義了base_url
來表示請求的URL的前半部分。接下來,構造引數字典,其中type
、value
和containerid
是固定引數,page
是可變引數。接下來,呼叫urlencode()
方法將引數轉化為URL的GET請求引數,即類似於type=uid&value=2830678474&containerid=1076032830678474&page=2
這樣的形式。隨後,base_url
與引數拼合形成一個新的URL。接著,我們用requests請求這個連結,加入headers
引數。然後判斷響應的狀態碼,如果是200,則直接呼叫json()
方法將內容解析為JSON返回,否則不返回任何資訊。如果出現異常,則捕獲並輸出其異常資訊。
隨後,我們需要定義一個解析方法,用來從結果中提取想要的資訊,比如這次想儲存微博的id
、正文、贊數、評論數和轉發數這幾個內容,那麼可以先遍歷cards
,然後獲取mblog
中的各個資訊,賦值為一個新的字典返回即可:
1234567891011121314from pyquery import PyQuery as pq def parse_page(json): if json: items = json.get('data').get('cards') for item in items: item = item.get('mblog') weibo = {} weibo['id'] = item.get('id') weibo['text'] = pq(item.get('text')).text() weibo['attitudes'] = item.get('attitudes_count') weibo['comments'] = item.get('comments_count') weibo['reposts'] = item.get('reposts_count') yield weibo複製程式碼
這裡我們藉助pyquery將正文中的HTML標籤去掉。
最後,遍歷一下page
,一共10頁,將提取到的結果列印輸出即可:
if __name__ == '__main__': for page in range(1, 11): json = get_page(page) results = parse_page(json) for result in results: print(result)複製程式碼
另外,我們還可以加一個方法將結果儲存到MongoDB資料庫:
123456789from pymongo import MongoClient client = MongoClient()db = client['weibo']collection = db['weibo'] def save_to_mongo(result): if collection.insert(result): print('Saved to Mongo')複製程式碼
這樣所有功能就實現完成了。執行程式後,樣例輸出結果如下:
1234{'id': '4134879836735238', 'text': '驚不驚喜,刺不刺激,意不意外,感不感動', 'attitudes': 3, 'comments': 1, 'reposts': 0}Saved to Mongo{'id': '4143853554221385', 'text': '曾經夢想仗劍走天涯,後來過安檢給收走了。分享單曲 遠走高飛', 'attitudes': 5, 'comments': 1, 'reposts': 0}Saved to Mongo複製程式碼
檢視一下MongoDB,相應的資料也被儲存到MongoDB,如圖6-14所示。
圖6-14 儲存結果
這樣,我們就順利通過分析Ajax並編寫爬蟲爬取下來了微博列表,最後,給出本節的程式碼地址:github.com/Python3WebS…。
本節的目的是為了演示Ajax的模擬請求過程,爬取的結果不是重點。該程式仍有很多可以完善的地方,如頁碼的動態計算、微博檢視全文等,若感興趣,可以嘗試一下。
通過這個例項,我們主要學會了怎樣去分析Ajax請求,怎樣用程式來模擬抓取Ajax請求。瞭解了抓取原理之後,下一節的Ajax實戰演練會更加得心應手。
本資源首發於崔慶才的個人部落格靜覓: Python3網路爬蟲開發實戰教程 | 靜覓
如想了解更多爬蟲資訊,請關注我的個人微信公眾號:進擊的Coder
weixin.qq.com/r/5zsjOyvEZ… (二維碼自動識別)