Python爬蟲學習筆記6:Ajax數據爬取


學習參考:Python3網絡爬蟲開發實戰

 

問題:requests抓取的頁面信息和瀏覽器中看到的不一樣。

原因:requests獲取的都是原始的HTML文檔,瀏覽器中的頁面很多都是經過javascript數據處理后的結果,這些數據可能通過AJax加載的,也可能是通過其他特定算法計算得到的

解決:對於通過Ajax加載的,叫異步加載,這種可以在web開發上做到前后端分離,降低服務器直接渲染頁面帶來的壓力,如果遇到requests無法獲取有效數據,需要進一步分析網頁后台向接口發送的Ajax請求,然后用requests來模擬Ajax請求,就可以成功獲取了

 

6.1 什么是 Ajax

Ajax是利用Javascript在保證頁面不被刷新、頁面鏈接不改變的情況下與服務器交換數據並更新部分網頁的技術。

2. 基本原理

發送請求、解析內容、渲染網頁

6.2 Ajax 分析方法 

1. 查看請求

jax其實有其特殊的請求類型,它叫作 xhr ,Request Headers中有一個信息為 X-Requested-With:XMLHt甲Request,這就標記了此請求是 Ajax請求 

在response里面可以找到真實的數據。

2. 過濾請求 

6.3 Ajax 結果提取

1.分析請求
繼續下拉網頁,會發現很多xhr信息出來,找到其中兩個 

點擊第一個,可以發現,這是一個 GET類型的請求,請求鏈接為 https://m.weibo.cn/api/container/getIndex?type=uid&value=2830678474&containerid=1076032830678474&page=2。 請求的參數有 4 個: type、 value、 containerid 和 page。

點開第二個,請求鏈接為:https://m.weibo.cn/api/container/getIndex?type=uid&value=2830678474&containerid=1076032830678474&page=3 

 可以發現,它們的type、value和containerid始終如一,type始終為uid,value的值就是頁面鏈接中的數字,其實這就是用戶的id,另外containerid就是107603加上用戶id,改變的值就是page,很明顯這個參數是用來 控制分頁的,page=1代表第一頁,page=2 代表第二頁,依次類推。
 

2. 分析響應 

可以看到,最關鍵 的兩部分信息就是 cardlistlnfo 和 cards:前者包含一個 比較重要的信息 total,觀察后可以發現, 它其實是微博的總數量,我們可以根據這個數字來估算分頁數;后者則是一個列表,它包含 10 個元 素 ,展開其中 一個看一下, 

這個元素有一個比較重要的字段 mblog。 展開它,可以發現它包含的正是微博的一些 信息,比如 attitudes count (贊數目)、 comments_count (評論數目)、 reposts_count (轉發數目)、 created at (發布時間)、 text (微博正文)等,而且它們都是-些格式化的內容 。

 

3. 實戰演練 

用程序模擬這些 Ajax請求,將前 10頁微博全部爬取下來。

import requests
from urllib.parse import urlencode
from pyquery import PyQuery as pq

base_url = 'https://m.weibo.cn/api/container/getIndex?' # 這里要換成對應Ajax請求中的鏈接

headers = {
    'Host':'m.weibo.cn',
    'Referer':'https://m.weibo.cn/u/2830678474',
    'X-Requested-With':'XMLHttpRequest',
    'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36'
}  #  不同於簡單的requests,只需要傳入客戶端信息就好了

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() # 解析內容為json返回
    except requests.ConnectionError as e:
        print('Error',e.args) #輸出異常信息


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() # 這里面的文字內容又進行了一層解析,將正文里面的html標簽去掉
            weibo['attitudes'] = item.get('attitudes_count')
            weibo['comments'] = item.get('comments_count')
            weibo['reposts'] = item.get('reposts_count')
            yield weibo

if __name__ =='__main__': # 本腳本窗口下的程序名字就叫做main,且這樣寫在別的程序調用該腳本文件的函數時不會受到影響
    for page in range(1,11):
        json = get_page(page)
        results = parse_page(json)
        for result in results:
            print(result)

 結果:

 

6.4 分析 Ajax爬取今日頭條街拍美圖

抓取今日頭條的街拍美圖,抓取完成之后,將每組圖片分文件夾下載到本地並保存下來。

import requests
from urllib.parse import urlencode
import os
from hashlib import md5
from multiprocessing.pool import Pool

def get_page(offset):
params = {
'aid':'24',
'app_name':'web_search',
'offset':offset,
'format':'json',
'keyword':'街拍',
'autoload':'true',
'count':'20',
'en_qc':'1',
'cur_tab':'1',
'from':'search_tab',
'pd':'synthesis',
'timestamp':'1562248846140'
}
header = {
'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36',
'x-requested-with':'XMLHttpRequest',
'referer':'https://www.toutiao.com/search/?keyword=%E8%A1%97%E6%8B%8D',
'cookie':'tt_webid=6709993811802818062; WEATHER_CITY=%E5%8C%97%E4%BA%AC; tt_webid=6709993811802818062; UM_distinctid=16bbfdeff8f460-0e02d1b4d98e6d-37607e02-1fa400-16bbfdeff90492; CNZZDATA1259612802=558541297-1562289443-https%253A%252F%252Fwww.google.com%252F%7C1562289443; __tasessionId=aorag2kb71562292191190; csrftoken=e4aae62081d10a9bb97fb5cd48e5cfa7; s_v_web_id=03c096aa5abb1e5a2f9edc5b4be5e8f3'
}
url = 'https://www.toutiao.com/api/search/content/?'+urlencode(params)
try:
response = requests.get(url,headers=header)
if response.status_code == 200:
return response.json()
except requests.ConnectionError as e:
print('Error',e.args)
# return None

def get_images(json):
if json.get('data'):
items = json.get('data')
for item in items:
title = item.get('title')
images = item.get('image_list')
for image in images:
yield {
'image':image.get('url'),
'title':title # 沒明白這個title 是怎么來的
}

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') # md5的哈希值
print(file_path)
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 as e:
print("Failed to Save Image")

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()

main(20)

 結果:

 

 


免責聲明!

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



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