本節咱們以今日頭條爲例來嘗試經過分析 Ajax 請求來抓取網頁數據的方法,咱們此次要抓取的目標是今日頭條的街拍美圖,抓取完成以後將每組圖片分文件夾下載到本地保存下來。git
在本節開始以前請確保已經安裝好了 Requests 庫,如沒有安裝能夠參考第一章的安裝說明。github
在抓取以前咱們首先要分析一下抓取的邏輯,首先打開今日頭條的首頁:http://www.toutiao.com/,如圖 6-15 所示:json
圖 6-15 首頁內容
在右上角有一個搜索入口,在這裏咱們嘗試抓取街拍美圖,因此輸入「街拍」二字搜索一下,結果圖 6-16 所示:數組
圖 6-16 搜索結果
這樣咱們就跳轉到了搜索結果頁面。Python資源分享qun 784758214 ,內有安裝包,PDF,學習視頻,這裏是Python學習者的彙集地,零基礎,進階,都歡迎網絡
這時打開開發者工具,查看一下全部網絡請求,咱們首先打開第一個網絡請求,這個請求的 URL 就是當前的連接:http://www.toutiao.com/search...,打開 Preview 選項卡查看 Response Body,若是頁面中的內容是直接請求直接加載出來的,那麼這第一個請求的源代碼中必然包含了頁面結果中的文字,爲了驗證,咱們能夠嘗試嘗試搜索一下搜索結果的標題,好比「路人」二字,如圖 6-17 所示:
多線程
圖 6-17 搜索結果
然而發現網頁源代碼中並無包含這兩個字,搜索匹配結果數目爲 0。
因此咱們就能夠初步判斷出這些內容是由 Ajax 加載而後用JavaScript 渲染出來的,因此接下來咱們能夠切換到 XHR過濾選項卡查看一下有沒有 Ajax 請求。app
不出所料,此處出現了一個比較常規的 Ajax 請求,觀察一下它的結果是否包含了頁面中的相關數據。
點擊 data 字段展開,發現這裏有許多條數據,咱們點擊第一條繼續展開,能夠發現有一個 title 字段,它的值正好就是頁面中的第一條數據的標題,再檢查一下其餘的數據也正好是一一對應的,如圖 6-18 所示:
ide
圖 6-18 對比結果
那這就肯定了這些數據確實是由 Ajax 加載的。
咱們的目的是要抓取其中的美圖,這裏一組圖就對應上文中的 data 字段中的一條數據,每條數據還有一個image_detail 字段,它是一個列表形式,這其中就包含了組圖的全部圖片列表,如圖 6-19 所示:
工具
圖 6-19 圖片列表信息
因此咱們只須要將列表中的 url 字段提取出來並下載下來就行了,每一組圖都創建一個文件夾,文件夾的名稱就命名爲組圖的標題。
接下來咱們就能夠直接用 Python 來模擬這個 Ajax 請求,而後提取出相關美圖連接並下載便可。可是在這以前咱們還須要分析一下 URL 的規律。
切換回 Headers 選項卡,咱們觀察一下它的請求 URL 和 Headers 信息,如圖 6-20 所示:
學習
圖 6-20 請求信息
能夠看到這是一個 GET 請求,請求 URL 的參數有 offset、format、keyword、autoload、count、cur_tab,咱們須要找出這些參數的規律才方便用程序構造出來。
接下來咱們能夠滑動頁面,多加載一些新的結果,在加載的同時能夠發現 Network 中又出現了許多 Ajax 請求,如圖 6-21 所示:
evernotecid://D603D29C-DFBA-4C04-85E9-CCA3C33763F6/appyinxiangcom/23852268/ENResource/p193
圖 6-21 Ajax 請求
在這裏觀察一下後續連接的參數,能夠發現變化的參數只有offset,其餘的都沒有變化,並且第二次請求的 offset 值爲 20,第三次爲 40,第四次爲 60,因此能夠發現規律,這個 offset 值就是偏移量,而進而能夠推斷出 count 參數就是一次性獲取的數據條數,因此咱們能夠用 offset 參數來控制數據分頁,這樣一來,咱們就能夠經過接口批量獲取數據了,而後將數據解析,將圖片下載下來就大功告成了。
咱們剛纔已經分析了一下 Ajax 請求的邏輯,下面咱們就用程序來實現美圖下載吧。
首先咱們實現一個方法用於加載單個 Ajax 請求的結果,叫作 get_page(),其中惟一變化的參數就是 offset,因此咱們將 offset 看成參數傳遞,方法實現以下:
import requests from urllib.parse import urlencode def get_page(offset): params = { 'offset': offset, 'format': 'json', 'keyword': '街拍', 'autoload': 'true', 'count': '20', 'cur_tab': '1', } url = 'http://www.toutiao.com/search_content/?' + urlencode(params) try: response = requests.get(url) if response.status_code == 200: return response.json() except requests.ConnectionError: return None
在這裏咱們用 urlencode() 方法構造了請求的 GET 參數,而後用 Requests 請求這個連接,若是返回狀態碼爲 200,則調用 response 的 json() 方法將結果轉爲 Json 格式,而後返回。
接下來咱們再實現一個解析方法,提取每條數據的 image_detail 字段中的每一張圖片連接,將圖片連接和圖片所屬的標題一併返回,構造一個生成器,代碼以下:
def get_images(json): if json.get('data'): for item in json.get('data'): title = item.get('title') images = item.get('image_detail') for image in images: yield { 'image': image.get('url'), 'title': title } Python資源分享qun 784758214 ,內有安裝包,PDF,學習視頻,這裏是Python學習者的彙集地,零基礎,進階,都歡迎
接下來咱們實現一個保存圖片的方法,item 就是剛纔get_images() 方法返回的一個字典,在方法中咱們首先根據 item 的 title 來建立文件夾,而後請求這個圖片連接,獲取圖片的二進制數據,以二進制的形式寫入文件,圖片的名稱能夠使用其內容的 MD5 值,這樣能夠去除重複。
import os from hashlib import md5 def save_image(item): if not os.path.exists(item.get('title')): os.mkdir(item.get('title')) try: response = requests.get(item.get('image')) if response.status_code == 200: file_path = '{0}/{1}.{2}'.format(item.get('title'), md5(response.content).hexdigest(), 'jpg') if not os.path.exists(file_path): with open(file_path, 'wb') as f: f.write(response.content) else: print('Already Downloaded', file_path) except requests.ConnectionError: print('Failed to Save Image')
最後咱們只須要構造一個 offset 數組,遍歷 offset,提取圖片連接,並將其下載便可。
from multiprocessing.pool import Pool def main(offset): json = get_page(offset) for item in get_images(json): print(item) save_image(item) GROUP_START = 1 GROUP_END = 20 if __name__ == '__main__': pool = Pool() groups = ([x * 20 for x in range(GROUP_START, GROUP_END + 1)]) pool.map(main, groups) pool.close() pool.join() Python資源分享qun 784758214 ,內有安裝包,PDF,學習視頻,這裏是Python學習者的彙集地,零基礎,進階,都歡迎
在這裏定義了分頁的起始和終止頁數,分別爲 GROUP_START 和 GROUP_END,還利用了多線程的線程池,調用其 map() 方法實現多線程下載。
這樣整個程序都就完成了,運行以後能夠發現街拍美圖都分文件夾保存下來了,如圖 6-22 所示:
圖 6-22 保存結果
本節代碼地址:https://github.com/oldmarkfac...
以上即是抓取今日頭條街拍美圖的過程,經過本節咱們能夠了解 Ajax 分析的流程、Ajax 分頁的模擬以及圖片的下載過程。本節的內容須要熟練掌握,在後面的實戰中咱們還會用到不少次這樣的分析和抓取。