
本文详解为何 CSS 选择器返回 None,并提供健壮的 BeautifulSoup 解析方案,解决因缺失 标签导致的元素定位失败问题,确保燃油价格自动抓取稳定可靠。
本文详解为何 css 选择器返回 `none`,并提供健壮的 beautifulsoup 解析方案,解决因缺失 `
` 标签导致的元素定位失败问题,确保燃油价格自动抓取稳定可靠。在使用 BeautifulSoup(特别是 select_one())从网页中提取结构化数据时,一个常见却极易被忽视的问题是:HTML 文档实际结构与开发者工具中显示的“美化后结构”不一致。你遇到的 "Initial number: None" 错误,根本原因正是 CSS 选择器过度依赖了浏览器开发者工具自动补全的 <tbody> 标签——而目标网站 <code>https://nbeub.ca/index.php?page=current-petroleum-prices-2 的原始 HTML 中,<tr> 元素是<strong>直接嵌套在 <code><table> 内的,并未显式声明 <code><tbody>。因此,原选择器:<pre class="brush:php;toolbar:false;">body > table > tbody > tr:nth-child(5) > td > ...</pre><p>会在解析时逐级匹配失败,最终返回 <code>None。
✅ 正确做法是改用更鲁棒的后代选择器(descendant selector),省略对 <tbody> 的强制要求。只需将所有 <code>> tbody > 替换为空格(即 ` `),让选择器匹配任意层级的后代元素:
import requests
from bs4 import BeautifulSoup
import time
url = 'https://nbeub.ca/index.php?page=current-petroleum-prices-2'
def fetch_number():
try:
response = requests.get(url, timeout=10)
response.raise_for_status() # 检查 HTTP 错误状态码
soup = BeautifulSoup(response.text, 'html.parser')
# ✅ 修正后的选择器:移除所有 > tbody >,使用空格表示后代关系
element = soup.select_one(
'body > table tr:nth-child(5) > td > table tr > td > table tr > td:nth-child(3) > table tr:nth-child(3) > td:nth-child(2)'
)
# 安全提取文本:去除空白、处理 None
if element and element.get_text(strip=True):
return element.get_text(strip=True)
else:
return "N/A"
except Exception as e:
print(f"⚠️ 抓取异常: {e}")
return "ERROR"
def monitor():
last_number = fetch_number()
print(f"Initial number: {last_number}")
# ⚠️ 注意:原代码中 time.sleep(2592000) 是 30 天,开发调试建议先设为 10–60 秒
while True:
time.sleep(60) # 调试阶段建议缩短间隔
current_number = fetch_number()
if current_number != last_number and current_number not in ["N/A", "ERROR"]:
print(f"✅ Number updated: {current_number}")
last_number = current_number
else:
print("ℹ️ No change or invalid value.")
if __name__ == "__main__":
monitor()? 关键优化说明:
立即学习“前端免费学习笔记(深入)”;
-
选择器健壮性提升:使用
table tr(后代)替代table > tbody > tr(子元素),兼容无<tbody> 的真实 HTML; <li> <strong>异常防护</strong>:添加 <code>try/except和response.raise_for_status(),避免网络错误导致程序崩溃; -
文本安全提取:调用
.get_text(strip=True)避免空格、换行干扰,并显式检查element是否存在; - 调试友好:初始轮询间隔改为 60 秒,便于快速验证逻辑;生产环境再按需调整至数小时或数天;
-
可维护性增强:若未来页面结构调整,建议结合
soup.find_all('table')层级遍历 + 关键文本定位(如查找含"Regular Unleaded"的行),比硬编码nth-child更可持续。
? 额外建议:该网站未设置反爬头,但长期运行仍建议添加 headers={'User-Agent': 'Mozilla/5.0...'};同时注意遵守 robots.txt(访问 https://nbeub.ca/robots.txt 查看限制),并尊重网站更新频率,避免高频请求。
通过以上调整,你的燃油价格解析器将稳定输出真实数值,真正为计算器提供动态、准确的油价数据支撑。



















