
许多现代网站为了提供更流畅的用户体验,采用异步加载(ajax)技术来动态更新页面内容。这意味着当用户初次访问一个页面时,浏览器接收到的html文档可能只包含部分数据或一个骨架。剩余的数据,例如通过“加载更多”按钮或滚动到底部时显示的内容,是通过javascript向服务器发送额外的请求(通常是xhr或fetch请求)来获取的。
对于这类网站,仅仅使用requests库获取初始HTML内容,再结合BeautifulSoup进行解析,往往只能获取到页面上最初可见的数据。例如,一个列表页面可能只显示前50条记录,而要获取全部数百甚至上千条记录,传统的HTML解析方法就显得力不从心。尝试通过解析JavaScript代码中的字符串或模拟点击行为来获取后续数据,通常既复杂又脆弱,容易因网站前端代码的微小变动而失效。
处理动态加载数据的最有效策略是绕过复杂的JavaScript渲染过程,直接定位并请求数据源的API接口。这些API通常以JSON或XML格式返回结构化的数据,使得数据提取变得异常简单和高效。
如何发现API接口?
发现这些隐藏的API接口通常需要借助浏览器的开发者工具:
在本案例中,通过分析发现,网站的数据并非直接嵌入在HTML中,而是通过一个名为 data.json 的API接口进行分页加载。例如,https://www.racingpost.com/bloodstock/sales/catalogues/5/2023-12-04/data.json 就是这样一个接口。
一旦识别出API接口,接下来的爬取过程就变得直观和高效。
Easily find JSON paths within JSON objects using our intuitive Json Path Finder
30
首先,我们需要向API接口发送一个请求,以获取关于总页数或其他分页信息。这通常通过不带特定页码参数的初始请求来完成。API的响应中会包含一个元数据对象,其中包含了pagination(分页)信息,如totalPages(总页数)。
import requests
# 目标API接口的基础URL
base_url = 'https://www.racingpost.com/bloodstock/sales/catalogues/5/2023-12-04/data.json'
# 发送请求获取分页元数据
try:
page_metadata_response = requests.get(base_url)
page_metadata_response.raise_for_status() # 检查HTTP请求是否成功
page_metadata = page_metadata_response.json()
# 从元数据中提取总页数
total_pages = page_metadata['pagination']['totalPages']
print(f"总页数: {total_pages}")
except requests.exceptions.RequestException as e:
print(f"请求元数据失败: {e}")
total_pages = 0 # 发生错误时,将总页数设为0,避免后续循环
except KeyError as e:
print(f"解析元数据失败,缺少键: {e}")
total_pages = 0获取到总页数后,我们可以通过一个循环,向同一个API接口发送带page参数的请求,逐页获取所有数据。
import requests
import time # 引入time模块用于设置延迟
# 目标API接口的基础URL
base_url = 'https://www.racingpost.com/bloodstock/sales/catalogues/5/2023-12-04/data.json'
all_data_rows = [] # 用于存储所有页面的数据
# 获取总页数(同上一步)
try:
page_metadata_response = requests.get(base_url)
page_metadata_response.raise_for_status()
page_metadata = page_metadata_response.json()
total_pages = page_metadata['pagination']['totalPages']
print(f"总页数: {total_pages}")
except (requests.exceptions.RequestException, KeyError) as e:
print(f"获取总页数失败: {e}")
total_pages = 0
# 遍历每一页,获取数据
if total_pages > 0:
for page in range(1, total_pages + 1):
print(f"正在获取第 {page}/{total_pages} 页数据...")
try:
# 构建带页码参数的请求
response = requests.get(base_url, params={'page': str(page)})
response.raise_for_status() # 检查HTTP请求是否成功
# 解析JSON响应,提取'rows'键下的数据
page_data = response.json()['rows']
all_data_rows.extend(page_data) # 将当前页数据添加到总列表中
# 可以打印每页数据的一部分或其数量,用于调试
# print(f"第 {page} 页获取到 {len(page_data)} 条数据。")
# 设置延迟,避免请求过快被封禁
time.sleep(0.5)
except requests.exceptions.RequestException as e:
print(f"请求第 {page} 页数据失败: {e}")
except KeyError as e:
print(f"解析第 {page} 页数据失败,缺少'rows'键: {e}")
print(f"\n所有页面数据获取完毕,共计 {len(all_data_rows)} 条数据。")
# 此时 all_data_rows 包含了所有页面的数据
# print(all_data_rows[0]) # 打印第一条数据示例
else:
print("无法获取数据,因为未能确定总页数。")
在进行API接口爬取时,除了上述核心逻辑,还需要考虑以下几点以确保爬虫的稳定性和健壮性:
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
}
# 在requests.get中添加headers参数
# requests.get(base_url, params={'page': str(page)}, headers=headers)通过直接利用网站后台的JSON API接口,我们可以更高效、更稳定地获取动态加载的数据,这比传统基于HTML解析和模拟浏览器行为的方法更为优越。这种方法不仅简化了数据提取过程,还大大提高了爬虫的健壮性,使其更能适应网站前端的变化。掌握浏览器开发者工具的使用,是发现这些宝贵API接口的关键技能,也是现代网页数据爬取不可或缺的一环。
以上就是高效爬取动态加载数据的策略:以JSON API为例的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号