BeautifulSoup 网页抓取:从静态 HTML 到动态页面
Expert in Web Scraping Technologies
TL;DR:
- BeautifulSoup 解析 HTML;它不执行 JavaScript。 响应体必须已经包含您想选择的记录。
- Requests 加上 BeautifulSoup 是处理静态页面的最短路径。 在一个 Python 进程中提取、验证、解析、规范化和导出。
- CSS 选择器对于嵌套卡片非常实用。 在每条记录内限定字段选择器的范围,以便值不会在行间漂移。
- 动态页面需要不同的获取路径。 首先检查内部 JSON 端点,然后在需要浏览器执行时使用渲染的 HTML。
- BeautifulSoup 在渲染后仍然有用。 通用抓取 API 可以返回 HTML,而 BeautifulSoup 保持解析和 JSON 输出的所有权。
BeautifulSoup 网络抓取在页面获取和 HTML 解析被视为两个独立任务时效果最佳。Requests 获取一个表示。BeautifulSoup 将该表示转换为可导航的树形结构。无论哪个操作都不能证明依赖于 JavaScript 的内容已经出现。
本教程构建一个静态抓取器,添加有限的分页,演示 JavaScript 页面上的空壳问题,并在管理的渲染步骤后保持相同的 BeautifulSoup 解析器。
静态 HTTP、内部 JSON 还是渲染的 HTML?
在编写选择器之前选择获取路径。
| 页面行为 | 最佳首选路径 | BeautifulSoup 的角色 |
|---|---|---|
| 记录在初始 HTML 中存在 | Requests | 直接解析响应 |
| 页面从公共 JSON 请求加载记录 | 请求该稳定端点 | 仅在需要时解析嵌入的 HTML 字段 |
| 所需内容在 JavaScript 之后出现 | 管理的渲染器或浏览器 | 解析返回的渲染 HTML |
| 工作流程需要点击、表单或会话操作 | 浏览器自动化 | 解析快照或最终 HTML |
查看源代码或检查 Requests 的响应,而不仅仅是浏览器的元素面板。浏览器显示的是执行后 JavaScript 的 DOM;Requests 看到的是服务器的响应。
先决条件
您需要 Python、一个虚拟环境,以及访问公共目标页面的权限。可运行的示例使用 requests 和 beautifulsoup4,并结合 Python 的内置 HTML 解析器。
Beautiful Soup 文档 涵盖了解析器选择和 CSS 选择器。Requests 快速入门 文档描述了响应处理和状态检查。
安装 BeautifulSoup 和 Requests
创建一个隔离环境,并安装脚本导入的确切包:
bash
python3 -m venv .venv
. .venv/bin/activate
python -m pip install beautifulsoup4 requests
在添加网络代码之前确认导入已解决:
bash
python -c "import bs4, requests; print(bs4.__version__, requests.__version__)"
抓取一个静态 HTML 页面
公共的抓取静态页面将每个报价卡片放置在响应的 HTML 中。解析器在每个 .quote 记录内限定文本、作者和标签。
python
import json
from urllib.parse import urljoin
import requests
from bs4 import BeautifulSoup
url = "https://quotes.toscrape.com/page/1/"
response = requests.get(url, timeout=30)
response.raise_for_status()
soup = BeautifulSoup(response.text, "html.parser")
records = []
for card in soup.select(".quote"):
records.append({
"text": card.select_one(".text").get_text(strip=True),
"author": card.select_one(".author").get_text(strip=True),
"tags": [tag.get_text(strip=True) for tag in card.select(".tag")],
"source_url": response.url,
})
next_link = soup.select_one("li.next a")
next_url = urljoin(response.url, next_link["href"]) if next_link else None
print(json.dumps({
"count": len(records),
"first": records[0],
"next_url": next_url,
}, indent=2))
解析器执行三项有用操作:首先选择完整的卡片,保持每个字段在卡片范围内,并保留源 URL。该源 URL 使后续选择器或分页问题可重现。
find_all 与 CSS 选择器
BeautifulSoup 提供基于方法的搜索和 CSS 选择。
find()通过名称或属性返回第一个匹配的标签。find_all()返回所有匹配的标签。select_one()返回第一个 CSS 选择器匹配。select()返回所有 CSS 选择器匹配。
CSS 选择器对于嵌套卡片布局来说简洁明了。基于方法的搜索在匹配一个标签和一个属性时可以更清晰。为项目选择一种风格,并在记录容器下限定子字段。
避免与表现类绑定的冗长选择器链。当页面暴露时,优先考虑耐用属性、语义元素、稳定的 URL 模式或内部 JSON 键。
添加有限的分页
分页需要一个停止条件,即使目标当前有明确的下一个链接。此脚本收集两个静态页面,并在链接消失时提前停止。
python
import json
from urllib.parse import urljoin
import requests
from bs4 import BeautifulSoup
url = "https://quotes.toscrape.com/page/1/"
rows = []
for _ in range(2):
response = requests.get(url, timeout=30)
response.raise_for_status()
soup = BeautifulSoup(response.text, "html.parser")
for card in soup.select(".quote"):
rows.append({
"text": card.select_one(".text").get_text(strip=True),
"author": card.select_one(".author").get_text(strip=True),
"source_url": response.url,
})
next_link = soup.select_one("li.next a")
if not next_link:
break
url = urljoin(response.url, next_link["href"])
print(json.dumps({"count": len(rows), "last": rows[-1]}, indent=2))
在将模式应用于更大的站点之前,使用最大页面数量、访问的 URL 集合和稳定的记录键。保持并行工作精简,并尊重站点政策。
使用 Scrapeless 开始抓取
使用 Scrapeless 提升您的网络抓取和自动化工作流程!
今天注册并获得 $5 的免费信用——无需信用卡。立即在 Scrapeless Dashboard 领取您的免费信用。
为什么 BeautifulSoup 返回一个空的动态页面
JavaScript版本的同一演示在直接请求时返回一个HTML外壳。报价卡在浏览器运行页面脚本后附加,因此soup.select('.quote')在初始响应中找不到记录。
该区分在短诊断中显而易见:
python
import requests
from bs4 import BeautifulSoup
url = "https://quotes.toscrape.com/js/"
response = requests.get(url, timeout=30)
response.raise_for_status()
soup = BeautifulSoup(response.text, "html.parser")
print({
"status": response.status_code,
"title": soup.title.get_text(strip=True),
"quote_cards": len(soup.select(".quote")),
})
状态200意味着服务器返回了成功的HTTP响应;这并不意味着表示中包含业务记录。HTTP语义标准定义了状态行为,而抓取器必须验证页面身份和所需选择器。
在选择渲染器之前,请检查浏览器开发工具中的Fetch/XHR请求。一个稳定的公共JSON响应可能比渲染的DOM小且更易于验证。不要重现依赖于私人凭证、受限端点或您未持有的授权的请求。
BeautifulSoup的止步
BeautifulSoup在解析时停止。它不会运行页面脚本、点击控件、维护浏览器执行环境或解决在HTML到达解析器之前发生的获取问题。
无抓取通用抓取API可以为公共JavaScript页面返回渲染的HTML。BeautifulSoup可以使用与本地HTML相同的选择器解析该返回字符串。
注意:以下云请求需要读者拥有的
SCRAPELESS_API_KEY。文档中的请求形状已被验证;在此环境中未执行凭证受限的调用。
python
import os
import requests
from bs4 import BeautifulSoup
response = requests.post(
"https://api.scrapeless.com/api/v2/unlocker/request",
headers={"x-api-token": os.environ["SCRAPELESS_API_KEY"]},
json={
"actor": "unlocker.webunlocker",
"proxy": {"country": "ANY"},
"input": {
"url": "https://quotes.toscrape.com/js/",
"jsRender": {
"enabled": True,
"waitUntil": "domcontentloaded",
"response": {"type": "html"}
}
}
},
timeout=60
)
response.raise_for_status()
payload = response.json()
if payload.get("code") != 200:
raise RuntimeError("Rendered HTML was not returned")
soup = BeautifulSoup(payload["data"], "html.parser")
records = [
{
"text": card.select_one(".text").get_text(strip=True),
"author": card.select_one(".author").get_text(strip=True),
}
for card in soup.select(".quote")
]
print({"count": len(records), "first": records[0] if records else None})
通用抓取API文档定义了JavaScript渲染和响应选项。将渲染器配置保留在获取层中,以便解析器保持可测试,使用保存的HTML固定装置。
在保存JSON之前验证
解析器应该拒绝错误页面,而不是将空文件导出为成功。验证:
- 最终URL与预期的主机和路线匹配;
- 标题或H1标识所需页面;
- 所需的卡片选择器存在;
- 每条接受的记录包含其业务密钥;
- 可为空字段保持
null而不是偏移相邻值; - 来源URL与每一行一起存储。
机器人排除协议指定自动客户端请求遵循的爬虫指令。这并不取代授权或适用法律。
故障排除BeautifulSoup抓取器
| 症状 | 可能原因 | 检查 |
|---|---|---|
| 状态200时零记录 | JavaScript渲染的内容或错误的选择器 | 检查响应HTML和所需元素 |
| 混乱的文本 | 错误的响应编码 | 比较头部、文档元数据和解码的主体 |
| 字段与错误卡片配对 | 全局字段选择器 | 在每个记录容器下作用域字段查询 |
| 重复的行 | 分页循环重新访问URL | 跟踪已访问的URLs和稳定的记录键 |
| 解析器行为不同 | 不同的解析器后端 | 明确固定所选解析器 |
结论
当响应已经包含数据时,BeautifulSoup网络抓取是可靠的。请求处理静态获取;BeautifulSoup处理解析;有限分页和验证将结果转换为结构化输出。
对于动态页面,首先检查公共内部JSON源。当所需的页面状态需要JavaScript时,通过通用抓取API返回渲染的HTML,并将BeautifulSoup作为解析器。
保持Python解析,将渲染移动到云端
比较Scrapeless定价,研究 Puppeteer下载指南中的浏览器边界,并创建一个Scrapeless账户。加入Discord或Telegram进行实现讨论。
常见问题
问:BeautifulSoup适合进行网络抓取吗?
当另一个组件已经获取了正确的文档时,BeautifulSoup是一个实用的HTML和XML解析器。
问:BeautifulSoup可以抓取动态网站吗?
BeautifulSoup不能执行JavaScript,但可以解析由内部API、受管理的渲染器或浏览器自动化步骤返回的HTML。
问:BeautifulSoup网络抓取合法吗?
只抓取您被授权访问的公共数据,审查网站条款和机器人指令,最小化收集并寻求相关司法管辖区的法律建议。
问:BeautifulSoup抓取器需要代理吗?
小型允许的静态请求可能不需要代理;地理或生产工作负载可能需要适当的代理和明确的流量控制。
问:当DOM发生变化时应该发生什么?
重新检查当前响应,识别持久来源,如属性或JSON键,收紧选择器,并在保存数据之前验证所需字段。
问:BeautifulSoup抓取器应该使用多少并发?
每个主机最多从三个工作线程开始,观察网站政策和响应行为,并在目标或用例需要较少流量时减少并行性。
问:这些示例可以在没有AI代理的情况下运行吗?
可以。这些Python示例直接运行;代理是在相同采集和解析步骤周围的可选协调。
在Scrapeless,我们仅访问公开可用的数据,并严格遵循适用的法律、法规和网站隐私政策。本博客中的内容仅供演示之用,不涉及任何非法或侵权活动。我们对使用本博客或第三方链接中的信息不做任何保证,并免除所有责任。在进行任何抓取活动之前,请咨询您的法律顾问,并审查目标网站的服务条款或获取必要的许可。




