
本文详解如何使用 beautifulsoup 准确提取 scraping club 练习页面中所有商品的名称和价格,解决因 html 结构变化导致仅获取首项的常见问题,并提供健壮、可复用的 css 选择器方案。
本文详解如何使用 beautifulsoup 准确提取 scraping club 练习页面中所有商品的名称和价格,解决因 html 结构变化导致仅获取首项的常见问题,并提供健壮、可复用的 css 选择器方案。
你遇到的问题非常典型:原始代码中使用了 soup.find_all('div', class_='grid grid-cols-1 gap-4 sm:grid-cols-3'),但该 class 实际作用于整个商品列表容器(即父级
正确的思路是:定位每个商品的最小独立包装单元。观察当前页面 HTML 结构可知,每个商品均包裹在
(商品名)和 (价格)子元素。此时最可靠、语义清晰的选择器是 CSS 的 :has() 伪类:div:has(>h4),它精准匹配“直接子元素含 的 ”,完美对应每个商品卡片。以下是优化后的完整实现:
import requests
from bs4 import BeautifulSoup
url = "https://scrapingclub.com/exercise/list_basic/?page=1"
response = requests.get(url)
response.raise_for_status() # 确保请求成功,抛出异常便于调试
soup = BeautifulSoup(response.content, "lxml")
# 使用 :has() 选择器精准定位每个商品卡片(推荐方式)
for i, card in enumerate(soup.select("div:has(>h4)"), 1):
try:
name = card.h4.text.strip()
price = card.h5.text.strip()
print(f"{i:^5} {name:<p>✅ <strong>关键改进说明:</strong> </p>
- soup.select("div:has(>h4)") 替代过时的 class 匹配,不受 CSS 类名更新影响,稳定性高;
- enumerate(..., 1) 直接生成从 1 开始的序号,简洁且不易出错;
- 添加 try-except 异常处理,避免因个别商品结构异常导致整个爬虫中断;
- 使用 response.content(而非 .text)配合 lxml 解析器,更准确处理编码与 HTML 解析。
⚠️ 注意事项:
- :has() 是较新的 CSS 选择器,在极旧版 BeautifulSoup(如
- 该页面共 10 件商品,若需翻页抓取多页,请在循环外嵌套页码逻辑(如 for page in range(1, 4):),并动态构造 URL;
- 生产环境务必添加 time.sleep() 和 User-Agent 头,遵守 robots.txt 并尊重网站反爬策略。
掌握基于语义结构(而非脆弱的 class 名)的选择器思维,是应对网页频繁改版的核心能力。此方法不仅适用于本练习,更是构建鲁棒爬虫的基础实践。
的 ”,完美对应每个商品卡片。以下是优化后的完整实现:
import requests
from bs4 import BeautifulSoup
url = "https://scrapingclub.com/exercise/list_basic/?page=1"
response = requests.get(url)
response.raise_for_status() # 确保请求成功,抛出异常便于调试
soup = BeautifulSoup(response.content, "lxml")
# 使用 :has() 选择器精准定位每个商品卡片(推荐方式)
for i, card in enumerate(soup.select("div:has(>h4)"), 1):
try:
name = card.h4.text.strip()
price = card.h5.text.strip()
print(f"{i:^5} {name:<p>✅ <strong>关键改进说明:</strong> </p>
- soup.select("div:has(>h4)") 替代过时的 class 匹配,不受 CSS 类名更新影响,稳定性高;
- enumerate(..., 1) 直接生成从 1 开始的序号,简洁且不易出错;
- 添加 try-except 异常处理,避免因个别商品结构异常导致整个爬虫中断;
- 使用 response.content(而非 .text)配合 lxml 解析器,更准确处理编码与 HTML 解析。
⚠️ 注意事项:
- :has() 是较新的 CSS 选择器,在极旧版 BeautifulSoup(如
- 该页面共 10 件商品,若需翻页抓取多页,请在循环外嵌套页码逻辑(如 for page in range(1, 4):),并动态构造 URL;
- 生产环境务必添加 time.sleep() 和 User-Agent 头,遵守 robots.txt 并尊重网站反爬策略。
掌握基于语义结构(而非脆弱的 class 名)的选择器思维,是应对网页频繁改版的核心能力。此方法不仅适用于本练习,更是构建鲁棒爬虫的基础实践。
以下是优化后的完整实现:
import requests
from bs4 import BeautifulSoup
url = "https://scrapingclub.com/exercise/list_basic/?page=1"
response = requests.get(url)
response.raise_for_status() # 确保请求成功,抛出异常便于调试
soup = BeautifulSoup(response.content, "lxml")
# 使用 :has() 选择器精准定位每个商品卡片(推荐方式)
for i, card in enumerate(soup.select("div:has(>h4)"), 1):
try:
name = card.h4.text.strip()
price = card.h5.text.strip()
print(f"{i:^5} {name:<p>✅ <strong>关键改进说明:</strong> </p>
- soup.select("div:has(>h4)") 替代过时的 class 匹配,不受 CSS 类名更新影响,稳定性高;
- enumerate(..., 1) 直接生成从 1 开始的序号,简洁且不易出错;
- 添加 try-except 异常处理,避免因个别商品结构异常导致整个爬虫中断;
- 使用 response.content(而非 .text)配合 lxml 解析器,更准确处理编码与 HTML 解析。
⚠️ 注意事项:
- :has() 是较新的 CSS 选择器,在极旧版 BeautifulSoup(如
- 该页面共 10 件商品,若需翻页抓取多页,请在循环外嵌套页码逻辑(如 for page in range(1, 4):),并动态构造 URL;
- 生产环境务必添加 time.sleep() 和 User-Agent 头,遵守 robots.txt 并尊重网站反爬策略。
掌握基于语义结构(而非脆弱的 class 名)的选择器思维,是应对网页频繁改版的核心能力。此方法不仅适用于本练习,更是构建鲁棒爬虫的基础实践。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!










