您现在的位置是:首页 > 正文

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

2024-04-01 00:36:20阅读 2

本问包含内容,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("数据储存完成")

下期见

网站文章

  • 再学C语言28:输入和输出——重定向和文件

    再学C语言28:输入和输出——重定向和文件

    输入重定向;输出重定向;组合重定向

    2024-04-01 00:36:12
  • JAVA-项目打Jar包和打war包

    打jar包 打war包

    2024-04-01 00:35:46
  • 多位P8大佬联合打造的Java面试八股文,堪称《Java驾考宝典》

    多位P8大佬联合打造的Java面试八股文,堪称《Java驾考宝典》

    金三银四已经过去啦,问一下大家都收到了多少offer呢?都找到自己满意的工作了不,我猜快的人可能已经成功上岸了,慢的人可能还在准备当中,虽然春招差不多过去了,但是各大公司还在继续招聘,所以呢还没有拿到...

    2024-04-01 00:35:38
  • Feign-请求不同注册中心的服务

    Feign-请求不同注册中心的服务

    2019独角兽企业重金招聘Python工程师标准>>> ...

    2024-04-01 00:35:30
  • 2.7 协程设计原理

    2.7 协程设计原理

    /协程的状态 typedef enum _co_status {CO_NEW , //新建 CO_READY , // 就绪 CO_WAIT , //等待 CO_SLEEP , //睡眠 CO_EXI...

    2024-04-01 00:35:05
  • 区块链基础

    区块链基础

    区块链的数据结构是由包含交易信息的区块按照从远及近的顺序有序链接起来的。它可以被存储为平面文件(flat file),或是存储在一个简单数据库中。比特币核心客户端使用Google 的LevelDB 数据库存储区块链元数据。区块被从远及近有序地链接在这个链条里,每个区块都指向前一个区块。区块链经常被视为一个垂直的栈,第一个区块作为栈底的首区块,随后每个区块都被放置在之前的区块之上...

    2024-04-01 00:34:56
  • create connection SQLException, url: jdbc:mysql:///account, errorCode 1045, state 28000

    create connection SQLException, url: jdbc:mysql:///account, errorCode 1045, state 28000 错误原因: 配置文件中 ...

    2024-04-01 00:34:50
  • 【React】Context

    【React】Context

    Context

    2024-04-01 00:34:44
  • 读书笔记:数据结构与算法分析(java语言描述)——引论

    读书笔记:数据结构与算法分析(java语言描述)——引论1.3 递归简述 递归的四条基本法则: 1. 基准情形 2. 不断推进 3. 设计法则 4. 合成效益法则 本Markdown编辑器使用StackEdit修改而来,用它写博客,将会带来全新的体验哦: Markdown和扩展Markdown简洁的语法 代码块高亮 图片链接和图片上传 LaTex数学公式 UML序

    2024-04-01 00:34:16
  • 数的拆分(DFS的应用)

    数的拆分(DFS的应用)

    import java.util.Scanner;public class Main{ static int n,m,k,sum=0; static int []res=new int [100];//保存结果的数组 public static void main(String[] args) { Scanner scanner=new Scanner(Syste...

    2024-04-01 00:34:11