爬取【ajax+json】非同步載入的網站

水木·圳烜發表於2018-02-04

@匯入類庫

import requests
from lxml import etree
import json
import time

@請求地址和請求頭

# 請求頭,用於偽裝客戶端瀏覽器,可由抓包獲取
header_base = {
    'Connection': 'keep-alive',
    'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36',
    'Upgrade-Insecure-Requests': '1',
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
    'Accept-Encoding': 'gzip, deflate, br',
    'Accept-Language': 'zh-CN,zh;q=0.9',
}

# 首頁 URL
url_str = 'https://www.douyu.com/directory/all'

@發起請求,並獲得html文件的頁面元素樹

# 獲取首頁資訊
response = requests.get(url=url_str, headers=header_base)
# print(response.text)
# print(response.status_code)

# 獲得頁面的元素樹
html = etree.HTML(response.text)

@解析首頁資訊

# 通過xpath提取房間資訊:播主和線上人數
info_list = html.xpath(
    "//ul[@id='live-list-contentbox']//span[@class='dy-name ellipsis fl']/text() \
    | //ul[@id='live-list-contentbox']//span[@class='dy-num fr']/text()"
)

# 播主暱稱
host_names = info_list[0]
print('first_name : ', host_names)

# 列印所有房間資訊
for info in info_list:
    print(info)

@點選首頁底部的分頁頁碼時,位址列不會發生變化,由此我們知道該網站的分頁資訊是通過ajax做非同步載入的
這裡寫圖片描述

# 這裡我們發現當點選第二頁時,瀏覽器的位址列並沒有發生變化
# 無法直接從頁面獲取頁碼,因為頁碼是通過JS生成的
# page_num = html.xpath("//a[@class='shark-pager-item']/text()")
# print('page_num : ', page_num)

@知道了分頁資料的載入地址之後,我們逐頁發起請求,獲取其json資料,並做分析和提取

# 該網站的分頁資料是通過ajax非同步載入的(無法直接從瀏覽器的位址列獲取其頁面的URL)
# 需要藉助抓包工具或頁面控制檯獲得ajax非同步請求所傳送請求的url
page = 1
while True:

    # 不斷爬取下一頁
    page += 1

    # 通過抓包分析獲得的分頁地址
    url_str = 'https://www.douyu.com/gapi/rkc/directory/0_0/' + str(page)
    print(url_str)

    # 請求房間資訊
    response = requests.get(url=url_str, headers=header_base)
    # print(response.text)

    # 從第二頁開始,資料以json格式載入,因此先將文字轉換為json
    info_json = json.loads(response.text)
    # print(type(info_json['data']['rl']))

    # 當請求的頁碼大於最大頁碼時返回的是第一頁資料
    # 我們以此作為迴圈退出條件
    print('========', info_json['data']['rl'][0]['nn'],"==========")
    if host_names == info_json['data']['rl'][0]['nn']:
        break

    # 分析json資訊,迴圈獲取每個直播房間的資訊
    for one_info in info_json['data']['rl']:

        # 獲取主播名稱
        host = one_info['nn']

        # 獲取直播在看人數
        onlines = one_info['ol']

    # 睡一會再接著爬下一頁,以免請求過於頻繁被反爬
    time.sleep(2)

@爬取效果
這裡寫圖片描述

@完整程式碼

import requests
from lxml import etree
import json
import time

# 請求頭,用於偽裝客戶端瀏覽器,可由抓包獲取
header_base = {
    'Connection': 'keep-alive',
    'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36',
    'Upgrade-Insecure-Requests': '1',
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
    'Accept-Encoding': 'gzip, deflate, br',
    'Accept-Language': 'zh-CN,zh;q=0.9',
}

# 首頁 URL
url_str = 'https://www.douyu.com/directory/all'

# 獲取首頁資訊
response = requests.get(url=url_str, headers=header_base)
# print(response.text)
# print(response.status_code)

# 獲得頁面的元素樹
html = etree.HTML(response.text)

# 通過xpath提取房間資訊:播主和線上人數
info_list = html.xpath(
    "//ul[@id='live-list-contentbox']//span[@class='dy-name ellipsis fl']/text() \
    | //ul[@id='live-list-contentbox']//span[@class='dy-num fr']/text()"
)

# 播主暱稱
host_names = info_list[0]
print('first_name : ', host_names)

# 列印所有房間資訊
for info in info_list:
    print(info)

# 這裡我們發現當點選第二頁時,瀏覽器的位址列並沒有發生變化
# 無法直接從頁面獲取頁碼,因為頁碼是通過JS生成的
# page_num = html.xpath("//a[@class='shark-pager-item']/text()")
# print('page_num : ', page_num)

# 該網站的分頁資料是通過ajax非同步載入的(無法直接從瀏覽器的位址列獲取其頁面的URL)
# 需要藉助抓包工具或頁面控制檯獲得ajax非同步請求所傳送請求的url
page = 1
while True:

    # 不斷爬取下一頁
    page += 30

    # 通過抓包分析獲得的分頁地址
    url_str = 'https://www.douyu.com/gapi/rkc/directory/0_0/' + str(page)
    print(url_str)

    # 請求房間資訊
    response = requests.get(url=url_str, headers=header_base)
    # print(response.text)

    # 從第二頁開始,資料以json格式載入,因此先將文字轉換為json
    info_json = json.loads(response.text)
    # print(type(info_json['data']['rl']))

    # 當請求的頁碼大於最大頁碼時返回的是第一頁資料
    # 我們以此作為迴圈退出條件
    print('========', info_json['data']['rl'][0]['nn'],"==========")
    if host_names == info_json['data']['rl'][0]['nn']:
        break

    # 分析json資訊,迴圈獲取每個直播房間的資訊
    for one_info in info_json['data']['rl']:

        # 獲取主播名稱
        host = one_info['nn']

        # 獲取直播在看人數
        onlines = one_info['ol']

    # 睡一會再接著爬下一頁,以免請求過於頻繁被反爬
    time.sleep(2)

print('OVER !!!')

相關文章