Python3網絡爬蟲實戰-3六、分析Ajax爬取今日頭條街拍美圖

本節咱們以今日頭條爲例來嘗試經過分析 Ajax 請求來抓取網頁數據的方法,咱們此次要抓取的目標是今日頭條的街拍美圖,抓取完成以後將每組圖片分文件夾下載到本地保存下來。git

1. 準備工做

在本節開始以前請確保已經安裝好了 Requests 庫,如沒有安裝能夠參考第一章的安裝說明。github

2. 抓取分析

在抓取以前咱們首先要分析一下抓取的邏輯,首先打開今日頭條的首頁:http://www.toutiao.com/,如圖 6-15 所示:json

Python3網絡爬蟲實戰-3六、分析Ajax爬取今日頭條街拍美圖

圖 6-15 首頁內容
在右上角有一個搜索入口,在這裏咱們嘗試抓取街拍美圖,因此輸入「街拍」二字搜索一下,結果圖 6-16 所示:數組

Python3網絡爬蟲實戰-3六、分析Ajax爬取今日頭條街拍美圖

圖 6-16 搜索結果
這樣咱們就跳轉到了搜索結果頁面。Python資源分享qun 784758214 ,內有安裝包,PDF,學習視頻,這裏是Python學習者的彙集地,零基礎,進階,都歡迎網絡

這時打開開發者工具,查看一下全部網絡請求,咱們首先打開第一個網絡請求,這個請求的 URL 就是當前的連接:http://www.toutiao.com/search...,打開 Preview 選項卡查看 Response Body,若是頁面中的內容是直接請求直接加載出來的,那麼這第一個請求的源代碼中必然包含了頁面結果中的文字,爲了驗證,咱們能夠嘗試嘗試搜索一下搜索結果的標題,好比「路人」二字,如圖 6-17 所示:
Python3網絡爬蟲實戰-3六、分析Ajax爬取今日頭條街拍美圖多線程

圖 6-17 搜索結果
然而發現網頁源代碼中並無包含這兩個字,搜索匹配結果數目爲 0。
因此咱們就能夠初步判斷出這些內容是由 Ajax 加載而後用JavaScript 渲染出來的,因此接下來咱們能夠切換到 XHR過濾選項卡查看一下有沒有 Ajax 請求。app

Python3網絡爬蟲實戰-3六、分析Ajax爬取今日頭條街拍美圖

不出所料,此處出現了一個比較常規的 Ajax 請求,觀察一下它的結果是否包含了頁面中的相關數據。
點擊 data 字段展開,發現這裏有許多條數據,咱們點擊第一條繼續展開,能夠發現有一個 title 字段,它的值正好就是頁面中的第一條數據的標題,再檢查一下其餘的數據也正好是一一對應的,如圖 6-18 所示:
Python3網絡爬蟲實戰-3六、分析Ajax爬取今日頭條街拍美圖ide

圖 6-18 對比結果
那這就肯定了這些數據確實是由 Ajax 加載的。
咱們的目的是要抓取其中的美圖,這裏一組圖就對應上文中的 data 字段中的一條數據,每條數據還有一個image_detail 字段,它是一個列表形式,這其中就包含了組圖的全部圖片列表,如圖 6-19 所示:
Python3網絡爬蟲實戰-3六、分析Ajax爬取今日頭條街拍美圖工具

圖 6-19 圖片列表信息
因此咱們只須要將列表中的 url 字段提取出來並下載下來就行了,每一組圖都創建一個文件夾,文件夾的名稱就命名爲組圖的標題。
接下來咱們就能夠直接用 Python 來模擬這個 Ajax 請求,而後提取出相關美圖連接並下載便可。可是在這以前咱們還須要分析一下 URL 的規律。
切換回 Headers 選項卡,咱們觀察一下它的請求 URL 和 Headers 信息,如圖 6-20 所示:
Python3網絡爬蟲實戰-3六、分析Ajax爬取今日頭條街拍美圖學習

圖 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
Python3網絡爬蟲實戰-3六、分析Ajax爬取今日頭條街拍美圖

圖 6-21 Ajax 請求
在這裏觀察一下後續連接的參數,能夠發現變化的參數只有offset,其餘的都沒有變化,並且第二次請求的 offset 值爲 20,第三次爲 40,第四次爲 60,因此能夠發現規律,這個 offset 值就是偏移量,而進而能夠推斷出 count 參數就是一次性獲取的數據條數,因此咱們能夠用 offset 參數來控制數據分頁,這樣一來,咱們就能夠經過接口批量獲取數據了,而後將數據解析,將圖片下載下來就大功告成了。

3. 實戰演練

咱們剛纔已經分析了一下 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 所示:

Python3網絡爬蟲實戰-3六、分析Ajax爬取今日頭條街拍美圖

圖 6-22 保存結果

4. 本節代碼

本節代碼地址:https://github.com/oldmarkfac...

5. 結語

以上即是抓取今日頭條街拍美圖的過程,經過本節咱們能夠了解 Ajax 分析的流程、Ajax 分頁的模擬以及圖片的下載過程。本節的內容須要熟練掌握,在後面的實戰中咱們還會用到不少次這樣的分析和抓取。

相關文章
相關標籤/搜索