使用场景
从 JavaScript 渲染的页面取得精确价格
价格、库存和 ID 来自站点自身的状态,而不是由模型阅读渲染后的文本得出,因此无从编造。
普通抓取看不到的列表
客户端渲染的搜索结果和商品网格,通常在首次响应的 __NEXT_DATA__ 或 RSC 负载中就已经存在。
比 LLM 提取更省
2 credits,而 extract_with_llm 或 extract_structured 为 3 credits,而且不必等待推理步骤。
审查站点对自身发布了什么
found 会报告页面上的每一个状态来源及其大小,往往比可见界面所展示的更多。
Endpoint
/api/v1/tools/extract_embedded_stateParameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
url | string | Required | - | 要读取嵌入状态的页面。 Example: https://example.com/products/widget |
path | string | Optional | - | 只返回一个子树,而不是整个负载。仅支持点号分隔的键和数组索引——这不是 JSONPath,因此没有通配符、过滤器、切片或递归下降。无法解析的路径会返回 400,并列出中断处可用的键,且不消耗 credits。 Example: next_data.props.pageProps |
user_agent | string | Optional | - | 覆盖发送给目标站点的 User-Agent。请求仍以 CrawlForge 的身份签名;签名覆盖的是 authority,而不是该请求头。 Example: MyCompanyBot/1.0 |
respect_robots | boolean | Optional | true | 遵守目标站点的 robots.txt。保持为 `true` 时,对 `CrawlForge` 禁止的路径会在抓取之前以 403 拒绝,并且不扣除 credits。只有在您与目标站点另有协议时才设为 `false`——此时响应会带有 `warnings` 条目,并且该覆盖会记录在您的 API key 上。 Example: true |
timeout | number | Optional | 20000 | 抓取超时(毫秒),取值 1000 到 60000。状态负载常常有数兆字节,因此默认值高于更轻量的提取工具。 Example: 20000 |
请求示例
cURL - 读取完整状态
curl -X POST https://crawlforge.dev/api/v1/tools/extract_embedded_state \
-H "X-API-Key: cf_test_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/products/widget"
}'TypeScript - 用 path 缩小范围
const response = await fetch('https://crawlforge.dev/api/v1/tools/extract_embedded_state', {
method: 'POST',
headers: {
'X-API-Key': process.env.CRAWLFORGE_API_KEY!,
'Content-Type': 'application/json',
},
body: JSON.stringify({
url: 'https://example.com/products/widget',
// Dotted keys and array indexes. Not JSONPath: no wildcards or filters.
path: 'next_data.props.pageProps',
}),
});
const payload = await response.json();
if (!response.ok) {
// A path that does not resolve is a 400 naming the keys that were available.
throw new Error(payload.error.code + ': ' + payload.error.message);
}
const result = payload.data;
// Every state source on the page, largest first, whether or not it was scoped.
for (const source of result.found) {
console.log(source.name, source.variable, source.bytes);
}
// The values are the site's own, so they can be used as they are.
console.log(result.data.product.price);Python - 先发现,再缩小范围
import os
import requests
URL = 'https://crawlforge.dev/api/v1/tools/extract_embedded_state'
HEADERS = {
'X-API-Key': os.environ['CRAWLFORGE_API_KEY'],
'Content-Type': 'application/json',
}
# 1. Discover what the page carries. Nothing is truncated, so this can be big.
discovery = requests.post(
URL,
headers=HEADERS,
json={'url': 'https://example.com/products/widget'},
).json()
for source in discovery['data']['found']:
print(source['name'], source['variable'], source['bytes'])
for warning in discovery['data']['warnings']:
print('warning:', warning)
# 2. Ask again for just the branch you want.
scoped = requests.post(
URL,
headers=HEADERS,
json={
'url': 'https://example.com/products/widget',
'path': 'next_data.props.pageProps.product',
},
)
payload = scoped.json()
if not scoped.ok:
error = payload['error']
raise RuntimeError(f"{error['code']}: {error['message']}")
print(payload['data']['bytes'], 'bytes')
print(payload['data']['data'])响应示例
{ "success": true, "data": { "url": "https://example.com/products/widget", "found": [ { "name": "next_data", "variable": "__NEXT_DATA__", "bytes": 412880 } ], "path": null, "bytes": 412893, "data": { "next_data": { "buildId": "KfC_3GF1zuM", "props": { "pageProps": { "product": { "sku": "WID-9001", "price": 149.99, "currency": "USD", "inStock": true } } } } }, "warnings": [ "Result is 412893 bytes; \"next_data\" alone is 412880. Re-run with path to scope it, e.g. path:\"next_data.props\"." ] }, "credits_used": 2, "credits_remaining": 998, "processing_time": 980}data.found页面上的每一个状态来源,包含读取自的原始对象及其序列化大小data.path所应用的路径;返回完整负载时为 nulldata.bytes所返回内容的序列化大小——给出 path 时即为缩小后的大小data.data状态本身,按来源名称索引data.warnings存在但无法按 JSON 解析的来源,以及建议使用 path 的大小提示错误处理
路径无法解析 (400 Bad Request)
path 在所提取的状态中不存在。消息会指出中断的位置以及该处可用的键,因此拼写错误可以直接修正。不扣除 credits。先不带 path 调用一次,即可看到页面实际携带的内容。
被 robots.txt 阻止 (403 Forbidden)
目标站点的 robots.txt 禁止 CrawlForge 访问此路径。如果您与目标站点另有协议,可设置 respect_robots: false 覆盖——该覆盖会记录在您的 API key 上。此覆盖对 CrawlForge 永久排除名单上的主机无效,无论 respect_robots 取何值都会被拒绝。
响应过大 (413 Payload Too Large)
页面 HTML 超过 25MB 的响应体上限。该上限针对服务端返回的页面本身,而非从中提取的状态。不收取任何费用。
目标未响应 (504 Gateway Timeout)
站点未在 timeout 内响应。状态密集的页面体积很大;在判定为失败之前,请先把 timeout 调高至 60000 的上限。不收取任何费用。