动态加载内容爬取,Ajax爬取典例

本问包含内容,Ajax数据获取,线程池简单使用,xlwt模板数据写入exel

有时候我们在用 requests 抓取页面的时候,得到的结果可能和在浏览器中看到的不一样:在浏览器中可以看到正常显示的页面数据,但是使用 requests 得到的结果并没有。这是因为 requests 获取的都是原始的 HTML 文档,而浏览器中的页面则是经过 JavaScript 处理数据后生成的结果,这些数据的来源有多种,可能是通过 Ajax 加载的,可能是包含在 HTML 文档中的,也可能是经过 JavaScript 和特定算法计算后生成的。

目录

1.导入模块

2.获取请求头

3.获取Ajax加载数据

4.解析页面信息

5.将获取到的数据写入exel

6.开辟线程池运行程序

7.相对完整代码


下面我们以新发地为例获取Ajax动态加载的内容

1.导入模块

注意看代码注释

import requests
import xlwt    # 用于将数据储存到exel文件
from concurrent.futures import ThreadPoolExecutor  # 导入线程池给爬虫加速

简单请求我们使用requests就好 

2.获取请求头

在开发者面板找到如下请求头,这是Ajax数据获取所需的请求头

如果想要运行注意根据实际情况修改请求头参数

base_url = "http://xinfadi.com.cn/getPriceData.html"
headers = {
    'Host': 'xinfadi.com.cn',     # 在开发者面板都可以找到的请求头
    'Origin': 'http://xinfadi.com.cn',
    'Referer': 'http://xinfadi.com.cn/priceDetail.html',
    'User-Agent': '',  # 填写自己的User-Agent
    'X-Requested-With': 'XMLHttpRequest'   # 获取Ajax数据需要加入的请求
}

链接可以根据实际情况填入

3.获取Ajax加载数据

# 爬取页面原码(Ajax)
def get_page(url, headers, current):
    print(f"正在爬取第{current}页")
    # post请求需要的参数在Payload可找到
    params = {
            'limit': 20,   # 页面最多显示数据的条数,10或20
            'current': current,  # 页数,第几页
            'pubDateStartTime': '',
            'pubDateEndTime': '',
            'prodPcatid': '',
            'prodCatid': '',
            'prodName': ''
        }
    response = requests.post(url, data=params, headers=headers)  # 根据网页提示使用post请求
    json_source = response.json()["list"]   # 将Ajax数据转化为json格式后提取list条目的内容
    page_parse(json_source, all_info)  # 调用page_parse提取数据

4.解析页面信息

将获取到的信息都保存到字典当中

如果想要运行注意根据实际get_page函数返回的json数据给字典的键(key)赋值

如一级分类是否对应prodCat

# 提取数据
def page_parse(json_source, all_info):    # all_info为列表,包含每件产品的信息
    for item in json_source:    # type(item)为字典
        all_info["一级分类"].append(item.get("prodCat"))   # value对应的每个列表包含有所有产品的数据
        all_info["二级分类"].append(item.get("prodPcat"))
        all_info["产品名"].append(item.get("prodName"))
        all_info["最高价"].append(item.get("highPrice"))
        all_info["最低价"].append(item.get("lowPrice"))
        all_info["平均价"].append(item.get("avgPrice"))
        all_info["规格"].append(item.get("specInfo"))
        all_info["产地"].append(item.get("place"))
        all_info["单位"].append(item.get("unitInfo"))
        all_info["发布日期"].append(item.get("pubDate"))

5.将获取到的数据写入exel

这里细节比较多,不熟悉xlwt模块的可以跳过

# 保存数据
def save_data_exel(all_info):
    book = xlwt.Workbook(encoding="utf-8")   # 打开新工作薄
    sheet = book.add_sheet('新发地', cell_overwrite_ok=True)  # cell_overwrite_ok=Tru时重复写入单元格不会报错
    col = list(all_info.keys())   # col获取all_info的键将作为标题头写入exel
    value = list(all_info.values())   # value则是作为产品内容写入exel
    for i in range(10):           # 标题头写入操作
        sheet.write(0, i, col[i])
    # 根据all_info的格式特点采取按列储存数据
    for j in range(10):   # 列
        for k in range(20*(MAX_PAGE-1)):  # 行。MAX_PAGE无法取0,因为current从1开始,故MAX_PAGE-1
            sheet.write(k+1, j, value[j][k])  # 为了不覆盖第一行的标题行从第1行写入,而不是第0行

    book.save('新发地商品信息.xls')   # 文件名+保存Exel文件

6.开辟线程池运行程序

简单的线程池使用

if __name__ == '__main__':
    base_url = "http://xinfadi.com.cn/getPriceData.html"
    headers = {
        'Host': 'xinfadi.com.cn',     # 在开发者面板都可以找到的请求头
        'Origin': 'http://xinfadi.com.cn',
        'Referer': 'http://xinfadi.com.cn/priceDetail.html',
        'User-Agent': '',  # 填写自己的User-Agent
        'X-Requested-With': 'XMLHttpRequest'   # 获取Ajax数据需要加入的请求
    }
    all_info = {        # all_info包含获取到的所有产品数据
        "一级分类": [],
        "二级分类": [],
        "产品名": [],
        "最高价": [],
        "最低价": [],
        "平均价": [],
        "规格": [],
        "产地": [],
        "单位": [],
        "发布日期": []
    }

    with ThreadPoolExecutor(50) as T:    # 创建包含50个线程的线程池
        for i in range(1, MAX_PAGE):   # post的current参数从1开始不能取0
            # 不能写成T.submit(get_page, base_url=base_url, headers=headers, current=i)不然获取不到数据
            T.submit(get_page, base_url, headers, i)  # 将函数调用的任务交给线程池来做
    save_data_exel(all_info)
    print("数据储存完成")

7.相对完整代码

为什么是相对完整呢?里面User-Agent还需要自行填入

import requests
import xlwt    # 用于将数据储存到exel文件
from concurrent.futures import ThreadPoolExecutor  # 导入线程池给爬虫加速

MAX_PAGE = 200  # 爬取最大页数>=1,因为range(1,1)为None


# 爬取页面原码(Ajax)
def get_page(url, headers, current):
    print(f"正在爬取第{current}页")
    # post请求需要的参数在Payload可找到
    params = {
            'limit': 20,   # 页面最多显示数据的条数,10或20
            'current': current,  # 页数,第几页
            'pubDateStartTime': '',
            'pubDateEndTime': '',
            'prodPcatid': '',
            'prodCatid': '',
            'prodName': ''
        }
    response = requests.post(url, data=params, headers=headers)  # 根据网页提示使用post请求
    json_source = response.json()["list"]   # 将Ajax数据转化为json格式后提取list条目的内容
    page_parse(json_source, all_info)  # 调用page_parse提取数据


# 提取数据
def page_parse(json_source, all_info):    # all_info为列表,包含每件产品的信息
    for item in json_source:    # type(item)为字典
        all_info["一级分类"].append(item.get("prodCat"))    # value对应的每个列表包含有所有产品的数据
        all_info["二级分类"].append(item.get("prodPcat"))
        all_info["产品名"].append(item.get("prodName"))
        all_info["最高价"].append(item.get("highPrice"))
        all_info["最低价"].append(item.get("lowPrice"))
        all_info["平均价"].append(item.get("avgPrice"))
        all_info["规格"].append(item.get("specInfo"))
        all_info["产地"].append(item.get("place"))
        all_info["单位"].append(item.get("unitInfo"))
        all_info["发布日期"].append(item.get("pubDate"))



# 保存数据
def save_data_exel(all_info):
    book = xlwt.Workbook(encoding="utf-8")   # 打开新工作薄
    sheet = book.add_sheet('新发地', cell_overwrite_ok=True)  # cell_overwrite_ok=Tru时重复写入单元格不会报错
    col = list(all_info.keys())   # col获取all_info的键将作为标题头写入exel
    value = list(all_info.values())   # value则是作为产品内容写入exel
    for i in range(10):           # 标题头写入操作
        sheet.write(0, i, col[i])
    # 根据all_info的格式特点采取按列储存数据
    for j in range(10):   # 列
        for k in range(20*(MAX_PAGE-1)):  # 行。MAX_PAGE无法取0,因为current从1开始,故MAX_PAGE-1
            sheet.write(k+1, j, value[j][k])  # 为了不覆盖第一行的标题行从第1行写入,而不是第0行

    book.save('新发地商品信息.xls')   # 文件名+保存Exel文件


if __name__ == '__main__':
    base_url = "http://xinfadi.com.cn/getPriceData.html"
    headers = {
        'Host': 'xinfadi.com.cn',     # 在开发者面板都可以找到的请求头
        'Origin': 'http://xinfadi.com.cn',
        'Referer': 'http://xinfadi.com.cn/priceDetail.html',
        'User-Agent': '',  # 填写自己的User-Agent
        'X-Requested-With': 'XMLHttpRequest'   # 获取Ajax数据需要加入的请求
    }
    all_info = {        # all_info包含获取到的所有产品数据
        "一级分类": [],  # value对应的每个列表包含有所有产品的数据
        "二级分类": [],
        "产品名": [],
        "最高价": [],
        "最低价": [],
        "平均价": [],
        "规格": [],
        "产地": [],
        "单位": [],
        "发布日期": []
    }

    with ThreadPoolExecutor(50) as T:    # 创建包含50个线程的线程池
        for i in range(1, MAX_PAGE):   # post的current参数从1开始不能取0
            # 不能写成T.submit(get_page, base_url=base_url, headers=headers, current=i)不然获取不到数据
            T.submit(get_page, base_url, headers, i)  # 将函数调用的任务交给线程池来做
    save_data_exel(all_info)
    print("数据储存完成")

下期见

  • 4
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

亖夕

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值