ChatGPT网页抓取:实用指南
Senior Web Scraping Engineer
TL;DR:
- ChatGPT 不会抓取网页 — 它分析您提供的文本。 该模型没有浏览器,没有 JavaScript 引擎,也没有绕过访问挑战的方式,因此每个有效的“ChatGPT 网络抓取”设置有两个层次:某种工具抓取页面,模型提取字段。
- 提取层的确非常好。 使用 OpenAI Python SDK 的结构化输出,您可以定义 Pydantic 模式,
chat.completions.parse返回经过验证的对象 — 当页面布局发生变化时无需维护选择器。 - 限制是可衡量的。 在 JavaScript 渲染的演示页面上进行普通的 HTTP GET 请求返回 0 个引用元素;通过启用
js_render的 Scrapeless Universal Scraping API 抓取同一 URL 返回所有 10 个。这一差距正是模型无法自行跨越的。 - 模式设计胜过提示的巧妙。 可空字段、显式类型和每次调用一个页面产生您可以信任的提取;模糊的提示则产生自信的虚构。
- 知道您手中拿着哪个工具。 “ChatGPT 网络抓取”(本指南 — 将模型作为解析器)与 ChatGPT 抓取器相对,后者捕获 ChatGPT 自己的答案作为数据。
- 免费开始。 本指南中的抓取层在免费计划下运行 — 在 app.scrapeless.com 创建您的 API 密钥。
ChatGPT 可以抓取网站吗?
不能。ChatGPT 是一种语言模型:它读取和生成文本,无法完成抓取器的抓取层所做的事情 — 发起请求,执行 JavaScript,保持会话或回答反机器人挑战。将页面文本粘贴到它中,它以非常出色的方式提取字段;将其指向 URL,您依赖于当天包裹模型的任何抓取工具,这在渲染或受保护的页面上会悄然失败。
因此,ChatGPT 网络抓取的实际架构始终是相同的两个层。抓取层检索页面的忠实副本。提取层 — 带有模式的 OpenAI API — 将该副本转换为结构化记录。本指南首先构建提取层,因为这是 ChatGPT 获得其位置的地方,然后演示抓取层的失败模式及其通过一个活的、可重复的示例的修复。
在代码之前有一个澄清,因为这个关键词确实模棱两可:本指南将 ChatGPT 用作抓取器的大脑。相反的工作 — 捕获 ChatGPT 自己的答案及其引用作为数据 — 是完全不同的工具,涵盖在 ChatGPT 抓取器 API 指南 和更广泛的 LLM 抓取器解释 中。
安装
两个软件包涵盖了这两个层 — 用于提取的 OpenAI SDK 和用于 HTTP 的 requests。这里的版本是本指南编写时所使用的版本 (openai 2.34.0):
bash
pip install "openai==2.34.0" requests
配置
两个层都从环境中进行身份验证,因此不会有密钥存储在源代码中:
bash
export OPENAI_API_KEY="sk-your_openai_key"
export SCRAPELESS_API_KEY="sk_your_scrapeless_key"
基本实现:优先考虑模式提取
当前的 OpenAI SDK 通过一次调用实现结构化提取:将记录定义为 Pydantic 模型,将其作为 response_format 传递,chat.completions.parse 返回其实例。模式带来可靠性。模型受到您声明的字段名称和类型的限制 — 同样的合同风格 JSON Schema 规范 将其正规化 — 因此输出质量不再依赖于您在提示中的恳求程度。
注意:此块需要一个带有 API 额度的
OPENAI_API_KEY— 本指南不假定的唯一先决条件,因此提取调用的输出未被捕获,其结果形态将在下面描述。接下来的抓取层真实运行,仅需要一个 Scrapeless 密钥。
python
# extract.py — ChatGPT 作为提取层(需要 OPENAI_API_KEY)
from openai import OpenAI
from pydantic import BaseModel
class Quote(BaseModel):
text: str
author: str
tags: list[str]
class QuotePage(BaseModel):
quotes: list[Quote]
client = OpenAI() # 从环境中读取 OPENAI_API_KEY
page_html = open("page.html", encoding="utf-8").read()
completion = client.chat.completions.parse(
model="gpt-4.1-mini-2025-04-14",
messages=[
{
"role": "system",
"content": "提取页面上的每个引用。"
"使用 null 表示无 — 完全省略引用,而不是虚构字段。",
},
{"role": "user", "content": page_html},
],
response_format=QuotePage,
)
page = completion.choices[0].message.parsed
print(f"提取了 {len(page.quotes)} 条引用")
在解析的页面上,这将返回一个 `QuotePage`,其 `.quotes` 列表包含一个经过验证的 `Quote` 记录 — 经过类型化的 Python 对象,而不是你仍然需要 `json.loads` 和防御的字符串。当前的参数和架构规则在 <a href="https://developers.openai.com/api/docs/guides/structured-outputs" rel="nofollow"><strong>OpenAI 结构化输出指南</strong></a> 中。
## 高级模式
三个习惯将可靠的提取器与演示区分开:
- **让缺失可表示。** 如果真实页面上某个字段可以缺失,将其类型设置为 `str | None`,并在系统消息中说明。仅包含必需字符串的架构会强迫模型填补空白,而它会填补。
- **给模型提供更少的页面。** HTML 结构会消耗令牌并引发干扰。如果你可以隔离内容容器 — 或以 markdown 而不是 HTML 获取页面 — 提取将变得更便宜且更准确。
- **每次调用保持一个页面。** 将十个页面批量处理为一个提示可以节省请求,但会影响准确性:记录在页面边界之间流失。循环应该在 Python 中,而不是在提示中。
还有第二种更旧的使用 ChatGPT 进行抓取的方法:让它为你 *编写* 基于选择器的脚本,然后在每个页面上运行该脚本。这仍然是对稳定布局进行高容量工作的正确选择 — 生成的代码在第一页后免费运行 — 但你需要像审核任何不是自己编写的代码一样审核它,它继承了下一节中的所有抓取层限制。
## 诚实的限制:ChatGPT 无法抓取页面
以上所有假设 `page.html` 存在且是可信的。这个假设是 ChatGPT 仅抓取破裂的地方,而这一破裂是可重现的。一个简单的 HTTP GET 返回服务器发送的字节 — 根据 <a href="https://datatracker.ietf.org/doc/html/rfc9110" rel="nofollow"><strong>HTTP 语义规范</strong></a>,这是资源的一个表示,而不是浏览器将从中构建的页面。在 JavaScript 渲染的页面上,这些是非常不同的文档:
```python
# plain_fetch.py — 简单 GET 在 JS 渲染页面上实际上看到的内容
import requests
url = "https://quotes.toscrape.com/js/"
resp = requests.get(url, timeout=60)
html = resp.text
print(f"状态 {resp.status_code} | {len(html):,} 字符")
print("HTML 中的引用元素:", html.count('<span class="text"'))
运行打印 状态 200 — 完全成功的请求 — 并且 0 个引用元素,因为在这个演示页面上,引用仅存在于脚本变量中,直到 JavaScript 引擎构建 DOM。将这个 HTML 交给上面的提取器,最好的模型提取不出任何内容,或者更糟糕的是,进行猜测。
解决方法是先渲染再提取。 通用抓取 API 在一次 POST 中获取相同的 URL,在服务器端执行页面,并启用 js_render,返回读者看到的 DOM — 包括解锁和代理路由,不需要在本地运行:
python
# rendered_fetch.py — 相同的 URL,在提取前在服务器端渲染
import os
import requests
resp = requests.post(
"https://api.scrapeless.com/api/v1/unlocker/request",
headers={
"Content-Type": "application/json",
"x-api-token": os.environ["SCRAPELESS_API_KEY"],
},
json={
"actor": "unlocker.webunlocker",
"input": {"url": "https://quotes.toscrape.com/js/", "method": "GET", "js_render": True},
},
timeout=120,
)
resp.raise_for_status()
html = resp.json().get("data", "")
print(f"渲染的页面: {len(html):,} 字符")
print("HTML 中的引用元素:", html.count('<span class="text"'))
with open("page.html", "w", encoding="utf-8") as f:
f.write(html)
相同的 URL,运行打印 10 个引用元素,渲染 HTML 的字符数为 8,940 — 这是提取层一直需要的文件。两个打印并列展示了整个论点:在简单提取中为 0 个引用元素,在渲染提取中为 10 个。将这两个脚本串联在一起,你就得到了有效的模式:通过 Scrapeless 渲染,通过 ChatGPT 提取。
在免费计划中获取你的 API 密钥:app.scrapeless.com
疑难解答
- 模型返回文本而不是 JSON。 你在简单的
create调用上 — 切换到chat.completions.parse并使用response_format模型,该模型在 API 层限制输出,而不是礼貌地请求。 - 字段回来填充但应为空。 在架构中将字段设置为可选,并在系统消息中陈述空值规则。失败在于合同,而不是模型的状态。
- 某些页面的提取正确,而其他页面则为空。 比较你实际获取的内容,而不是浏览器向你展示的内容——按照纯提取脚本的方式计算所提取的HTML中的已知元素。零匹配意味着渲染或访问问题,你应在提取层(
js_render或其他出口国家)修复,而不是在提示中修复。 - 随着容量的增加,令牌成本上升。 在调用之前将页面剥离到其内容容器,或者从Markdown而不是原始HTML中提取。对于稳定的高容量布局,让模型编写一次选择器脚本,只有在布局变化时才消耗令牌。
结论
ChatGPT改变了抓取中哪一部分是困难的。提取——过去意味着脆弱选择器的部分——现在是一个架构和一个SDK调用。获取——模型无法做到的部分——仍然决定是否有真实内容可以提取,以上的0与10的演示就是这种边界在实践中所呈现的样子。单独构建这两个层次,验证提取之前的获取,然后结合起来就是一个能在重设计中生存下来的抓取器。
准备好为您的提取器提供真实页面?
本指南中的获取层是通用抓取API上每页一个POST请求——计划和请求量请参见定价页面,而开发者文档涵盖了unlocker.webunlocker接受的每个参数。在app.scrapeless.com的免费计划中创建一个密钥,并自己重新运行这两个获取脚本。
常见问题
问:ChatGPT可以直接抓取网站吗?
不可以。模型无法发出HTTP请求,无法执行JavaScript,也无法通过反机器人检查——它只从其他内容提取文本。消费者聊天产品有时会将模型与浏览工具结合使用,但该工具规模较小,且被许多网站阻止,这就是为什么生产设置将模型与专用获取层配对的原因。
问:使用ChatGPT抓取是合法的吗?
提取层并不会改变收集层的规则。仅抓取公共页面,遵守网站条款和机器人排除协议中定义的机器人指令,保持请求量有限,并根据适用的隐私法处理任何个人数据——这与任何抓取器的义务相同,加上您模型提供者的使用条款。
问:在何种情况下传统选择器抓取器仍然是更好的选择?
在高容量的稳定布局上。每个页面的LLM调用会消耗令牌;编写后的选择器脚本不会产生任何费用。实际的分配是:在布局变化或经常变化的地方使用模型,而在一个布局重复数千次的地方使用生成后审查的选择器代码。
问:这与“ChatGPT抓取器”有什么不同?
方向。本指南将模型指向网页——ChatGPT是解析器。ChatGPT抓取器则是将抓取器指向ChatGPT——它捕获助手的回答、引用和产品结果作为结构化数据。Scrapeless将其作为一个参与者提供;引言中链接的ChatGPT抓取器API指南对此进行了说明。
问:我应该使用哪个OpenAI模型进行提取?
从一个小的当前模型开始,只有在提取质量需要时才向上升级。结构化输出限制了响应形状,无论模型大小,因此较小的层次能够很好地处理干净输入上的定义良好的架构——节省下来的费用用来获取更多页面。
在Scrapeless,我们仅访问公开可用的数据,并严格遵循适用的法律、法规和网站隐私政策。本博客中的内容仅供演示之用,不涉及任何非法或侵权活动。我们对使用本博客或第三方链接中的信息不做任何保证,并免除所有责任。在进行任何抓取活动之前,请咨询您的法律顾问,并审查目标网站的服务条款或获取必要的许可。



