新闻中心
解决BeautifulSoup爬取网页表格中动态内容缺失问题

本文旨在解决使用BeautifulSoup爬取网页表格时,因部分数据通过J*aScript动态加载导致内容缺失的问题。通过详细分析Oracle云定价页面的案例,教程将指导读者如何识别并获取隐藏在JSON API中的动态数据,并将其与BeautifulSoup解析的静态HTML内容有效整合,最终构建一个完整、准确的数据集。
在进行网页数据抓取时,开发者常常会遇到BeautifulSoup无法获取到页面上所有可见内容的情况。这通常是因为网页中的某些数据并非直接嵌入在初始HTML中,而是通过J*aScript在页面加载后动态请求并渲染的。本文将以Oracle云定价页面为例,详细阐述如何识别这类动态内容,并通过结合静态HTML解析与动态API数据获取的方法,实现完整、准确的数据抓取。
1. 问题识别:BeautifulSoup与浏览器开发者工具的差异
当使用BeautifulSoup解析网页时,如果发现某些表格单元格(如价格信息)在打印出的HTML中显示为空或不完整,但在浏览器开发者工具中却能看到完整内容,这便是一个典型的动态加载数据信号。
例如,在抓取Oracle云虚拟机构建定价表时,初始的BeautifulSoup代码可能得到如下结果,其中价格相关的
<tr data-partnumber="B93297"> <td><div>Compute – Ampere A1 – OCPU</div></td> <td class="rw-theme-40bg"><div data-minrange="3000" data-type="vcpu"><br/> </div></td> <td><span data-minrange="3000"><span data-model="pay_as_you_go"></span></span></td> <td><div>OCPU per hour</div></td> </tr>
而通过浏览器开发者工具检查同一行,会发现第二和第三列实际包含了价格信息:
<tr data-partnumber="B93297"> <th scope="row"><div>Compute – Ampere A1 – OCPU</div></th> <td class="rw-theme-40bg"><div data-minrange="3000" data-type="vcpu" class="">$0.01<span></span></div></td> <td><span data-minrange="3000" class="">$0.01<span></span></span></td> <td><div>OCPU per hour</div></td> </tr>
这种差异表明,价格数据是在页面加载后通过异步请求获取的。
2. 定位动态数据源
解决此问题的关键在于找到动态加载数据的源头。通常,这可以通过浏览器的开发者工具(Network标签页)来完成。当页面加载时,观察网络请求,寻找返回JSON格式数据的XHR或Fetch请求。
小爱开放平台
小米旗下小爱开放平台
291
查看详情
在Oracle云定价页面的案例中,经过检查可以发现,价格数据是从一个JSON文件加载的:https://www.oracle.com/a/ocom/docs/pricing/cloud-price-list.json。这个JSON文件包含了所有产品的定价信息,并通过partNumber(部件号)与HTML表格中的产品进行关联。
3. 数据抓取与整合策略
一旦确定了动态数据源,接下来的步骤是:
- 使用requests库获取并解析静态HTML页面内容。
- 使用requests库获取并解析动态JSON数据。
- 通过partNumber或其他唯一标识符,将HTML表格中抓取的产品信息与JSON数据中的价格信息进行匹配和整合。
3.1 核心代码实现
以下是实现这一策略的Python代码:
from bs4 import BeautifulSoup
import requests
import re
import pandas as pd
# 1. 获取动态价格数据
# ----------------------------------------------------------------------
json_data_url = 'https://www.oracle.com/a/ocom/docs/pricing/cloud-price-list.json'
try:
json_data = requests.get(json_data_url).json()
except requests.exceptions.RequestException as e:
print(f"Error fetching JSON data: {e}")
json_data = {} # Fallback to empty dict
currency = 'USD' # 假设我们关注美元价格
# 2. 获取静态HTML页面内容
# ----------------------------------------------------------------------
url = 'https://www.oracle.com/uk/cloud/compute/pricing/#compute-vm'
try:
oracle_website = requests.get(url).text
soup = BeautifulSoup(oracle_website, "html.parser")
except requests.exceptions.RequestException as e:
print(f"Error fetching HTML page: {e}")
exit() # Exit if we can't get the page
# 3. 遍历表格并整合数据
# ----------------------------------------------------------------------
rows_data = []
# 定位包含虚拟机定价表的div
virtual_machine_table_div = soup.find("div", class_="rc34w5 rw-neutral-00bg")
if virtual_machine_table_div and virtual_machine_table_div.table:
virtual_machine_table = virtual_machine_table_div.table
for compute_products_tbody in virtual_machine_table.find_all("tbody"):
trs = compute_products_tbody.find_all("tr")
for tr in trs:
part_number = None
# 尝试从tr标签的data-partnumber属性获取
if 'data-partnumber' in tr.attrs:
part_number = tr['data-partnumber']
else:
# 尝试从td内部的div获取data-partnumber
# 注意:原始问题中的第二个td是价格占位符,这里我们找第一个可能包含partNumber的td
# 更稳健的做法是遍历所有td或直接从tr获取
first_td_div = tr.find('td')
if first_td_div:
div_with_part_num = first_td_div.find('div', {'data-partnumber': re.compile('.*')})
if div_with_part_num:
part_number = div_with_part_num['data-partnumber']
# 初始化价格
comp_price = '-'
unit_price = '-'
# 根据part_number从JSON数据中查找价格
if part_number:
if part_number == 'B93297': # 特殊处理 'Compute – Ampere A1 – OCPU'
if 'vcpuRangeItems' in json_data and part_number in json_data['vcpuRangeItems']:
price_info = json_data['vcpuRangeItems'][part_number].get(currency)
if price_info:
comp_price = price_info[-1]['value']
unit_price = price_info[-1]['value']
elif part_number not in json_data['vcpuItems'] and part_number not in json_data['items'] and part_number not in json_data['rangeItems']:
# 如果part_number不在任何已知价格类型中,则价格未知
comp_price = '-'
unit_price = '-'
else:
# 从vcpuItems获取Comparison Price
if 'vcpuItems' in json_data and part_number in json_data['vcpuItems']:
comp_price = json_data['vcpuItems'][part_number].get(currency, '-')
# 从items或rangeItems获取Unit Price
if 'items' in json_data and part_number in json_data['items']:
unit_price = json_data['items'][part_number].get(currency, '-')
elif 'rangeItems' in json_data and part_number in json_data['rangeItems']:
price_info = json_data['rangeItems'][part_number].get(currency)
if price_info:
unit_price = price_info[-1]['value'] # 取最后一个(可能是最高范围或默认值)
elif part_number is None: # 处理没有part_number的产品,如Free tier
product_name_td = tr.find('td')
if product_name_td and "Free" in product_name_td.text:
comp_price = 'Free'
unit_price = 'Free'
# 提取产品名称和单位
tds = tr.find_all('td')
product_name = tds[0].text.strip() if len(tds) > 0 else ''
unit = tds[-1].text.strip() if len(tds) > 0 else '' # 最后一个td是单位
row = {
'partNumber': part_number,
'Product': product_name,
'Comparison Price (/vCPU)': comp_price,
'Unit price': unit_price,
'Unit': unit,
}
rows_data.append(row)
else:
print("Could not find the virtual machine table.")
# 4. 使用pandas生成结构化输出
# ----------------------------------------------------------------------
df = pd.DataFrame(rows_data)
print(df.to_markdown(index=False))
3.2 代码解析与注意事项
- 导入必要的库: BeautifulSoup用于HTML解析,requests用于HTTP请求,re用于正则表达式匹配(在某些情况下定位data-partnumber),pandas用于数据整理和输出。
- 获取JSON数据: 使用requests.get(json_data_url).json()直接获取并解析JSON格式的定价数据。这是解决问题的核心步骤。
- 获取HTML数据: 同样使用requests.get(url).text获取网页内容,并用BeautifulSoup进行解析。
- 定位表格: 通过soup.find("div", class_="rc34w5 rw-neutral-00bg").table准确找到目标定价表格。
-
遍历行与提取partNumber:
- 遍历每个
标签。 - partNumber是连接HTML产品信息和JSON价格数据的关键。它可能存在于
标签的data-partnumber属性中,也可能嵌套在 标签内的元素中。代码中尝试了两种获取方式。 - 价格查找逻辑:
- JSON数据结构可能复杂,包含多种价格类型(如vcpuRangeItems, vcpuItems, items, rangeItems)。需要根据partNumber的特点和JSON结构,编写相应的条件逻辑来查找正确的Comparison Price和Unit Price。
- 例如,B93297在vcpuRangeItems中,而其他产品可能在vcpuItems或items中。
- 对于没有partNumber但产品名称中包含“Free”的行,需要特殊处理,将其价格标记为“Free”。
- 使用json_data.get(key, {})或dict.get(key, default_value)可以避免在键不存在时引发KeyError,提高代码的健壮性。
- 数据整合: 将提取的产品名称、partNumber、计算出的价格和单位信息组织成字典,并添加到列表中。
- Pandas输出: 最后,使用pandas.DataFrame将收集到的数据转换为表格形式,并打印为Markdown格式,便于查看和进一步处理。
4. 总结
当BeautifulSoup无法抓取到网页上可见的全部内容时,很可能是因为这些内容是通过J*aScript动态加载的。解决这类问题的关键在于:
- 利用浏览器开发者工具(尤其是Network标签页)识别动态数据请求及其API地址。
- 直接请求动态数据API(通常返回JSON或XML格式)。
- 将动态数据与静态HTML解析结果进行整合,通常通过一个唯一的标识符(如data-partnumber)进行匹配。
这种方法比使用Selenium等全功能浏览器自动化工具更高效,因为它避免了渲染整个页面和执行J*aScript的开销,直接获取了原始数据。掌握这种“静态+动态”结合的抓取策略,能有效应对现代网页的复杂性,实现更精准和高效的数据抓取。
- partNumber是连接HTML产品信息和JSON价格数据的关键。它可能存在于
- 遍历每个
以上就是解决BeautifulSoup爬取网页表格中动态内容缺失问题的详细内容,更多请关注其它相关文章!
# 是因为
# 广州网站建设路串串
# 沈阳建设国外网站
# 怎么优化科技网站链接
# seo秒排教学
# 广西seo排名公司
# 滨江网络推广网站公司
# 自媒体如何营销引流推广
# 聊城装饰装修网站建设
# 青海seo营销方案
# 湛江seo规则
# 为空
# 关键在于
# 解决问题
# 这类
# oracle
# 数据结构
# 小爱
# 遍历
# 加载
# 正则表达式
# go
# json
# markdown
# js
# html
# java
# python
# javascript
相关栏目:
【
科技资讯46185 】
【
网络学院92790 】
相关推荐:
实现全屏滚动与导航点:专业教程
KFC游戏互动怎么赢取优惠券_KFC线上游戏活动参与与优惠代码赢取教程
mcjs网页版在线存档 mcjs云存档登录入口
python3时间如何用calendar输出?
J*aScript map 迭代中检测空数组元素的有效方法
QQ邮箱官方邮箱登录入口 QQ邮箱网页版快速访问
在J*a中如何开发简易电子商务商品管理系统_商品管理系统项目实战解析
理解J*aScript Promise的微任务队列与执行顺序
AO3官网镜像链接 Archive of Our Own同人文在线浏览
支付宝解绑银行卡步骤_支付宝如何解除绑定银行卡
斑马英语APP如何开启夜间护眼阅读_斑马英语APP夜间模式与低蓝光设置教程
PDO预处理语句中冒号的正确处理:区分SQL函数格式与命名占位符
QQ邮箱登录平台入口 QQ邮箱网页版邮箱官方入口
c++如何使用Meson构建系统_c++比CMake更快的构建工具
J*a递归快速排序中静态变量导致数据累积问题的解决方案
神经网络二分类模型训练异常:高损失与完美验证准确率的排查与修正
C++如何操作注册表_Windows平台下C++读写注册表的API函数详解
Basecamp怎样用留言钉固定重点_Basecamp用留言钉固定重点【重点标记】
J*aScript动态修改指定div内所有a标签样式指南
微信语音通话掉线如何解决 微信语音通话稳定优化方法
CSS子选择器:如何区分并样式化嵌套列表的子层级
在Go Martini框架中高效服务动态生成图像的实践指南
QQ邮箱网页版登录入口 QQ邮箱官方在线使用平台
生成rdflib自定义SPARQL函数:参数匹配与实践指南
蛙漫2台版漫画地址 Manwa2正版网页版链接
LINUX怎么设置定时任务_LINUX crontab配置教程
处理动态列数据:J*a ArrayList的正确初始化与字符累加教程
HTML空白字符处理机制:渲染、DOM与编码实践
b站如何看历史记录_b站观看历史找回方法
海量存储:机器视觉智能化的核心基石
利用Bokeh CustomJS动态控制DataTable列可见性
必由学网页版入口 必由学官方平台直接访问
MAC怎么安装Homebrew包管理器_MAC为开发者和高级用户安装命令行工具
C++如何实现异步操作_C++11使用std::future和std::async进行异步编程
2026春节假期时间安排 2026春节假日查询
C++指针和引用有什么区别_C++内存管理核心概念深度解析
抖音极速版最新版本 抖音极速版官方下载地址
一加手机电池耗电快怎么办_一加手机电池耗电快的解决方法
精准捕获:如何在页面中监听除特定元素外的所有点击事件
React/Next.js中实现列表项的动态选择与移动
高德地图总提示网络异常怎么办 高德地图离线导航设置与网络排查方法
Spring Boot嵌入式服务器与J*a EE:功能支持深度解析
windows10怎么关闭系统提示音_windows10彻底静音设置方法
荣耀Play7TPro怎样在信息App置顶客服对话_iPhone荣耀Play7TPro信息App置顶客服对话【优先查看】
必由学官方网站入口 必由学学生教师共用登录通道
AngularJS $http POST请求数据传递与Go后端接收实践
TypeScript/J*aScript:高效查找数组中首个唯一ID对象
J*aScript DOM操作:高效清空列表元素的策略与实践
Win11怎么开启卓越性能模式 Win11电源选项启用高性能释放硬件潜力【方法】
12306选座怎么选到特殊座位_12306特殊座位选择注意事项


2025-10-27
浏览次数:次
返回列表
3.2 代码解析与注意事项