Ajax異步數據抓取


1.簡介

 1 有時候我們在用requests抓取頁面的時候,得到的結果可能和在瀏覽器中看到的不一樣,在瀏覽
 2 器中可以看到正常顯示的頁面數據,但是使用requests得到的結果並沒有。這是因為requests獲取的
 3 都是原始的HTML文檔,而瀏覽器中的頁面則是經過JavaScript處理數據后生成的結果,這些數據的
 4 來源有多種,可能是通過ajax加載的,可能是包含在HTML文檔中的,也可能是經過JavaScript和特
 5 定算法計算后生成的。
 6 對於第一種情況,數據加載是一種異步加載方式,原始的頁面最初不會包含某些數據,原始頁面
 7 加載完后,會再向服務器請求某個接口獲取數據,然后數據才被處理從而呈現到網頁上,這其實就是
 8 發送了一個ajax請求。
 9 Web發展的趨勢來看,這種形式的頁面越來越多,網頁的原始HTML文檔不會包含任何數據,
10 數據都是通過統一加載后再呈現出來的,這樣在We開發上可以做到前后端分離,而且降低服
11 務器直接渲染頁面帶來的壓力。
12 所以如果遇到這樣的頁面,直接requests等庫來抓取原始頁面,是無法獲取到有效數據的,
13 這時需要分析網頁后台接口發送的jax請求,如果可以用requests來模擬Ajax請求,那么就可以
14 成功抓取了。
15 Ajax,是利用JavaScript在保證頁面不被刷新、頁面鏈接不改變的情況下與服務器交換數據並更新
16 部分網頁的技術。
17 Ajax作用:
18 1.發送請求。     比如:GET、POST、DELETE等請求
19 2.解析內容。     比如:服務器返回的XML、HTML、JSON等文本
20 3.渲染網頁。     比如:使用JavaScript在局部更新、刪除頁面數據
21 在這里我們需要使用瀏覽器的開發者工具分析什么是ajax請求:
22 打開Chrome,在百度搜索微博並登陸也可以選擇不登陸。按F12或者鼠標右鍵檢查(N),
23 打開開發者工具,選擇Network再選擇XHR,然后滑動頁面到底部,會發現出來很多Type是xhr類型的請求,
24 點開其中一個進行查看,請求類型是GET,再點擊Preview顯示的是JSON字符串,里面包含數據,
25 點開Response這是返回給我們最原始的數據,需要執行相應的渲染方法才會顯示到瀏覽器的頁面。
26 
27 獲取請求連接:http://api1.t.qq.com/asyn/home.php?&time=1540886592&page=4&id=486297042606010&isrecom=0&apiType=14&apiHost=http%3A%2F%2Fapi.t.qq.com&_r=154937
28 ?問號之后是請求參數,time是時間參數,page是頁數,id可變隨機數字,后面的參數都是固定的。
29 點擊Preview,分別點開info和talk這兩個元素,里面包含了用戶信息和請求內容,一共15條,
30 這樣請求一個ajax接口分別對應一頁和15條數據

1.實戰1

"""微博首頁數據抓取實戰,根據ajax請求抓取微博首頁數據到mongodb數據庫"""
import time
import requests
from urllib.parse import urlencode
from pyquery import PyQuery
from pymongo import MongoClient
# 定義請求的URL前半部分
base_url = "https://d.weibo.com/p/aj/discover/loading?"
# 連接MongoDB數據庫
client = MongoClient()
# 選擇一個指定的數據庫
db = client["weibo"]
# MongoDB每個數據庫又包含許多集合collection,它類似於關系型數據庫中的表.指定一個集合
collection = db["weibo"]
# 獲取請求頭信息
headers = {
    "Host": "d.weibo.com",
    "Referer": "https://d.weibo.com/623751_4",
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.81 Safari/537.36",
    "X-Requested-With": "XMLHttpRequest",
    "Cookie": "SINAGLOBAL=6864515739096.891.1548998185053; login_sid_t=2559f2c7dd7bb6887ac8cdf20d7638a4; cross_origin_proto=SSL; _s_tentry=www.baidu.com; Apache=4081118444726.1284.1549420746515; ULV=1549420746520:4:4:3:4081118444726.1284.1549420746515:1549377935756; appkey=; crossidccode=CODE-gz-1GRdhO-gmK0X-y3uIsLpXUizmHrk0b096d; ALF=1580957408; SSOLoginState=1549421409; SUB=_2A25xXjsxDeThGeBH41QV9SbPyzqIHXVSKiv5rDV8PUNbmtBeLRjnkW9NQbEvXVwxASJ0zK19HvDa8bI8wpwLEiKj; SUBP=0033WrSXqPxfM725Ws9jqgMF55529P9D9W5_J5gZ9_ArKvxLE7cIcqAI5JpX5KzhUgL.Foq41hqXSKn0ehq2dJLoIpQ_BEH81F-RBC-RebXLxK-LBK-L12qN1KqES5tt; SUHB=0JvwbnCYpHgJl1; UOR=,,login.sina.com.cn; wvr=6; YF-Page-G0=0f25bf37128de43a8f69dd8388468211; wb_view_log_6986458306=1536*8641.25"
}

def get_page(page, time):
    # 構造參數字典並發起請求
    params = {
        "ajwvr": "6",
        "id": "623751_4",
        "uid": "6986458306",
        "page": page,
        "__rnd": time
    }
    # 調用urlencode()方法把參數轉換為URL的GET請求參數
    url = base_url + urlencode(params)
    try:
        # 傳遞url和請求頭信息請求網頁內容
        response = requests.get(url, headers=headers)
        # 判斷響應的狀態碼
        if response.status_code == 200:
            # 返回請求的json格式數據
            return response.json()
    except requests.ConnectionError as e:
        # 出現異常則打印
        print("Error", e.args)

def parse_page(json):
    # 解析ajax返回的json字符串
    if json:
        html = json.get("data").get("html")
        # 傳遞html內容初始化
        pq = PyQuery(html)
        # 使用css選擇器並調用items()方法生成可迭代數據
        items = pq(".text_box").items()
        for item in items:
            # 空字典要放進循環里面來,因為數據的解析是在循環里面完成,一次循環一次解析
            weibo = {}
            # 調用find()方法傳入css節點選擇然后調用text()進行文本處理
            weibo["title"] = item.find("div a").text()
            weibo["text"] = item.find("div").text()
            # yield生成器保存數據為一個對象,節省內存空間
            yield weibo

def save_to_mongo(result):
    # 存儲到MongoDB數據庫
    if collection.insert(result):
        print("Saved to Mongo")

if __name__ == "__main__":
    # 獲取時間參數
    time = int(time.time() * 1000)
    # 設置需要的頁數
    for page in range(1, 11):
        # 調用函數傳遞頁數和時間參數
        json = get_page(page, time)
        # 調用解析函數傳遞返回的json文本完成解析
        results = parse_page(json)
        # 循環獲取生成器中的數據
        for result in results:
            print(result)
            # 調用存儲到mongodb的函數完成數據入庫
            save_to_mongo(result)
View Code

2.實戰2

"""今日頭條街拍美圖實戰,根據ajax請求分類別多進程下載街拍美圖到本地文件"""
import os
import requests
from urllib.parse import urlencode
from hashlib import md5
from multiprocessing.pool import Pool


def get_page(offset):
    # 設置url參數和獲取該url請求結果
    params = {
        "aid": "24",
        "offset": offset,
        "format": "json",
        "keyword": "街拍",
        "autoload": "true",
        "count": "20",
        "cur_tab": "1",
        "from": "search_tab",
        "pd": "synthesis",
    }
    # 調用urlencode()方法拼接url
    url = "https://www.toutiao.com/api/search/content/?" + urlencode(params)
    try:
        # 發起請求並獲取結果
        res = requests.get(url)
        # 請求成功返回字符串的json格式
        if res.status_code == 200:
            return res.json()
    except requests.ConnectionError as e:
        # 請求失敗,打印結果
        print("連接錯誤", e.args)

def get_images(json):
    # 如果有獲取到數據就解析
    if json.get("data"):
        for item in json.get("data"):
            # 遍歷獲取需要的信息
            title = item.get("title")
            images = item.get("image_list")
            for image in images:
                # 遍歷已獲取的多個圖片
                yield {
                    # yield生成器保存數據為一個對象,節省內存空間
                    "image": image.get("url"),
                    "title": title
                }

def save_image(item):
    # 去重並保存圖片
    if not os.path.exists(item.get("title")):
        # 調用os模塊的exists()方法判斷當前文件夾下是否沒有此名稱的文件
        # 調用mkdir()方法創建此文件
        os.mkdir(item.get("title"))
    try:
        # 開始請求圖片的url連接
        res = requests.get(item.get("image"))
        if res.status_code == 200:
            # 請求成功之后拼接圖片下載地址。圖片名稱為了避免重復則使用md5加密
            file_path = "{0}/{1}.{2}".format(item.get("title"), md5(res.content).hexdigest(), "jpg")
            if not os.path.exists(file_path):
                # 去除重復下載的圖片並寫入新圖片。'wb'二進制形式寫入
                with open(file_path, "wb") as f:
                    f.write(res.content)
            else:
                # 重復下載
                print("Already Downloaded", file_path)
    except requests.ConnectionError as e:
        print("圖片獲取失敗")

def main(offset):
    # 調用各功能函數並傳入參數即各進程任務
    json = get_page(offset)
    for item in get_images(json):
        print(item)
        save_image(item)

if __name__ == "__main__":
    pool = Pool()
    page_list = ([x * 20 for x in range(1, 21)])
    # 調用進程池中的map()方法傳入main函數和可迭代的列表
    pool.map(main, page_list)
    # 關閉進程池pool,使其不在接受新的(主進程)任務
    pool.close()
    # 主進程阻塞后,讓子進程繼續運行完成,子進程運行完后,再把主進程全部關掉。
    pool.join()
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM