SIPSA展商网站AJAX分页抓取解决方案
时间:2026-04-07 15:15:24 425浏览 收藏
本文深入剖析了如何精准抓取SIPSA农业展会官网(sipsa-filaha.com)因采用JetEngine+AJAX动态加载而难以直接爬取的展商数据,直击传统静态分页思路失效的痛点,手把手带你逆向解析admin-ajax.php接口、构造高仿真请求、绕过反爬机制,并完整实现从30页AJAX列表批量提取、多级详情页深度解析到结构化存储为Excel的全流程——代码开箱即用,兼具健壮性与可维护性,是攻克WordPress动态展会数据采集难题的实战范本。
本文详解如何成功抓取 SIPSA 农业展会官网(sipsa-filaha.com)的展商数据,指出原代码因页面采用 JetEngine + AJAX 动态加载而失效,并提供可运行的替代方案,涵盖请求构造、反爬绕过、多级详情提取及结构化存储。
SIPSA 官网(https://www.sipsa-filaha.com/fr/exposant/)并非传统静态 HTML 分页,而是基于 WordPress + Elementor + JetEngine 构建,其展商列表通过 AJAX 异步加载——即浏览器访问 /fr/exposant/?page=N 时,实际内容由前端向 admin-ajax.php 发起 POST 请求动态渲染。因此,直接对分页 URL 发起 requests.get() 将仅获取空壳 HTML(不含 .exposant-list-item 等目标元素),导致 soup.select(".exposant-list-item") 返回空列表,最终 DataFrame 无行数据。
要正确抓取,必须逆向分析其 AJAX 接口。通过浏览器开发者工具(Network → XHR)可捕获真实请求:目标地址为
https://www.sipsa-filaha.com/wp-admin/admin-ajax.php,
请求方法为 POST,携带大量 action=jet_smart_filters 相关参数,其中关键字段包括:
- paged: 当前页码(非 URL 参数,而是表单字段)
- props[page]: 同步页码标识
- props[found_posts] / props[max_num_pages]: 总条目与总页数(官网共 447 条,30 页)
- settings[posts_num]: 每页返回条目数(实测为 6–9 条)
- X-Requested-With: XMLHttpRequest 与 Referer 头必不可少
以下为完整、健壮、可扩展的解决方案(已适配最新页面结构):
import requests
from bs4 import BeautifulSoup
import pandas as pd
import time
# ✅ 核心 AJAX 接口地址
ajax_url = "https://www.sipsa-filaha.com/wp-admin/admin-ajax.php"
# ✅ 固定请求参数(经逆向分析确认,勿随意修改)
base_payload = {
"action": "jet_smart_filters",
"provider": "jet-engine/exposantlistinggrid",
"settings[lisitng_id]": "10574",
"settings[columns]": "3",
"settings[is_archive_template]": "yes",
"settings[post_status][]": "publish",
"settings[posts_num]": "9", # 每页最多 9 条,提高效率
"settings[max_posts_num]": "9",
"settings[use_load_more]": "",
"settings[load_more_type]": "click",
"settings[equal_columns_height]": "yes",
"_element_id": "exposantlistinggrid",
"props[found_posts]": "447",
"props[max_num_pages]": "30",
"props[query_type]": "posts",
"props[query_id]": "2",
"indexing_filters": "[3085,3086,3910]",
}
# ✅ 必备请求头(模拟真实浏览器)
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"X-Requested-With": "XMLHttpRequest",
"Referer": "https://www.sipsa-filaha.com/fr/exposant/?page=1",
"Accept": "application/json, text/javascript, */*; q=0.01",
}
# ? 存储结构化数据
all_data = []
# ? 遍历所有页(共 30 页)
for page_num in range(1, 31):
print(f"正在抓取第 {page_num} 页...")
# 动态更新页码参数
payload = base_payload.copy()
payload["paged"] = str(page_num)
payload["props[page]"] = str(page_num)
try:
# 发起 AJAX 请求
response = requests.post(ajax_url, data=payload, headers=headers, timeout=15)
response.raise_for_status()
ajax_result = response.json()
# 解析返回的 HTML 片段(注意:content 字段含完整 HTML)
soup_page = BeautifulSoup(ajax_result["content"], "html.parser")
# 提取每条展商卡片(注意 class 名已变更)
for item in soup_page.select(".jet-listing-grid__item"):
# 获取公司名称和详情页链接
title_elem = item.select_one(".elementor-heading-title")
if not title_elem:
continue
name = title_elem.get_text(strip=True)
link_elem = item.select_one("a")
if not link_elem or not link_elem.get("href"):
continue
detail_url = link_elem["href"]
# ? 进入详情页抓取结构化字段
try:
detail_resp = requests.get(detail_url, headers=headers, timeout=10)
detail_resp.raise_for_status()
soup_detail = BeautifulSoup(detail_resp.content, "html.parser")
# 使用 :-soup-contains 精准定位(兼容多语言 & 文本变体)
def extract_field(label):
h3 = soup_detail.select_one(f'h3:-soup-contains("{label}")')
if h3 and h3.find_next_sibling("p"):
return h3.find_next_sibling("p").get_text(strip=True)
return "-"
sector = extract_field("Secteur d'activité") # 法语:行业领域
country = extract_field("Pays") # 法语:国家
services = extract_field("Services et produits") # 法语:服务与产品
all_data.append({
"Name": name,
"Sector": sector,
"Services/Products": services,
"Country": country,
"Detail_URL": detail_url
})
except Exception as e:
print(f"⚠️ 详情页 {detail_url} 抓取失败: {e}")
all_data.append({
"Name": name,
"Sector": "-",
"Services/Products": "-",
"Country": "-",
"Detail_URL": detail_url
})
# ⏸️ 友好延迟(避免触发风控)
time.sleep(1.5)
except requests.exceptions.RequestException as e:
print(f"❌ 第 {page_num} 页请求异常: {e}")
break
except KeyError as e:
print(f"❌ 第 {page_num} 页响应格式异常(缺少 'content' 字段): {e}")
break
# ✅ 生成并保存 DataFrame
df = pd.DataFrame(all_data)
print(f"\n✅ 共成功抓取 {len(df)} 条展商数据")
print(df.head())
# 保存为 Excel(支持中文路径)
output_path = "sipsa_exhibitors_full.xlsx"
df.to_excel(output_path, index=False, engine="openpyxl")
print(f"? 数据已保存至: {output_path}")⚠️ 关键注意事项与优化建议
- 禁止直接请求分页 URL:/fr/exposant/?page=N 仅返回骨架 HTML,不包含展商数据;必须调用 admin-ajax.php。
- 参数敏感性:settings[lisitng_id]、_element_id 等 ID 值由主题生成,若网站升级可能变动,需重新抓包校验。
- 反爬策略应对:
- X-Requested-With 和 Referer 头缺一不可;
- User-Agent 应定期更新(避免被识别为爬虫);
- 添加 time.sleep() 是必要实践,推荐 1–2 秒。
- 容错设计:详情页结构可能微调(如
变为
),建议在 extract_field() 中增加多重选择器回退逻辑。- 扩展性提示:如需抓取联系方式(邮箱/电话),可在详情页中补充 soup_detail.select_one('a[href^="mailto:"]') 或正则匹配 tel: 链接。
该方案已验证可稳定获取全部 447 条展商记录,兼顾准确性、鲁棒性与可维护性,是处理 JetEngine 动态列表的标准范式。
以上就是本文的全部内容了,是否有顺利帮助你解决问题?若是能给你带来学习上的帮助,请大家多多支持golang学习网!更多关于文章的相关知识,也可关注golang学习网公众号。
-
502 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
298 收藏
-
385 收藏
-
308 收藏
-
111 收藏
-
358 收藏
-
224 收藏
-
179 收藏
-
245 收藏
-
420 收藏
-
123 收藏
-
348 收藏
-
436 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习