
Ethan Collins
Pattern Recognition Specialist

ReCaptchaV2TaskProxyLess、websiteURL和websiteKey,然后在结果就绪后返回gRecaptchaResponse。needs_review状态可防止循环和误导性库存警报。库存数据采集看似简单,但当零售页面根据位置改变行为、需要选择门店或在CAPTCHA处暂停授权浏览器会话时,就会变得复杂。可靠的采集器必须保留产品和门店上下文,通过同一会话解决检查点,并证明页面返回了真实的库存状态。CapSolver 可以作为该受控恢复步骤中的CAPTCHA基础设施。
本指南聚焦于具体的零售运营场景:监控公开产品可用性,用于批准的补货计划、商品质量保证(QA)或面向客户的库存通知。它不假设成功的CAPTCHA任务意味着库存请求成功。只有当目标应用程序提供已识别的库存结果,并且采集器记录了足够的证据以区分“缺货”和“未知”时,工作流才会结束。
库存数据采集应返回一个小型、稳定的记录,供下游系统信任。一个有用记录包含零售商、规范产品标识符、请求的门店或邮政区域、观察到的可用性、采集时间以及证据来源。价格可能包含在内,但不应替代显式的库存状态。
{
"retailer": "authorized-demo-store",
"product_id": "SKU-4821",
"store_id": "STORE-017",
"postal_area": "10001",
"availability": "in_stock",
"quantity_hint": "limited",
"observed_at": "2026-08-13T09:15:00Z",
"source": "product-page",
"verification": "inventory-label-and-store-id",
"captcha_recovery": "completed"
}
输入是产品URL加上允许的门店上下文。该操作选择或确认位置,检测验证检查点,授权后解决它,读取库存状态并标准化结果。输出是上述记录或终端状态,如not_found、out_of_stock、needs_review或policy_denied。永远不要将超时、挑战循环、登录墙或格式错误的响应转换为out_of_stock;这种错误会生成虚假的业务信号。
CapSolver CAPTCHA求解常见问题解释了服务边界,而浏览器自动化指南提供了维护页面状态的更广泛背景。在此用户案例中,浏览器负责导航和证据,CapSolver负责记录的CAPTCHA任务,而您的应用程序负责授权、重试策略和最终验证。
零售采集器通常在库存出现前执行多个有状态操作:加载产品页面、接受区域设置、选择门店、打开取货面板或调用由页面发起的公共库存端点。CAPTCHA可能在第一个页面渲染前或这些操作之一后出现。如果采集器将其视为通用HTML,选择器将失败,任务可能会写入空或错误的记录。
正确的响应是状态转换,而不是盲重试。采集器从collecting转移到captcha_required,冻结当前产品和位置上下文,仅收集记录的挑战字段,并调用提供者适配器。就绪的提供者响应将工作流转移到apply_solution;应用接受后将其转移到verify_inventory。被拒绝的结果、重复的检查点、意外的主机或耗尽的截止时间将将其转移到needs_review。
此设计保持了三种不同的真相:
只有第三个真相允许任务发布库存记录。当零售商使用缓存内容、在区域域之间重定向或在初始HTML加载后异步更新库存时,这种分离尤为重要。
使用六个职责明确的组件。
范围注册表列出批准的主机名、采集目的、产品路径、门店区域、计划和联系负责人。它还应记录排除项:认证账户区域、员工门户、结账、支付、个人资料,以及站点所有者或您的协议放置在范围外的任何路径。不匹配注册表的请求在浏览器打开前停止。
浏览器建立区域、门店选择、Cookie和导航状态。在一个浏览器上下文中保持一个产品检查。在不同门店间重用无关Cookie可能导致位置漂移;在CAPTCHA恢复期间丢弃上下文可能使解决方案失效。仅保留任务所需的最小数据,并根据保留策略清除它。
检测器查找显式小部件元素、已知响应字段、挑战脚本或验证路由。它还区分CAPTCHA与普通问题,如404、同意对话框、不可用门店或应用错误。CapSolver术语表有助于在日志和运行手册中保持挑战术语的一致性。
适配器接收包含挑战类型、页面URL、站点密钥和关联ID的窄请求。它从秘密存储中读取API密钥,创建一个任务,用截止时间轮询该任务,验证结果模式,并返回标准化的成功或失败。它不决定哪些站点被允许,也不写入库存数据。
提取器将页面或公共响应映射到稳定的库存模式。它应优先使用持久产品标识符、门店ID、结构化数据和显式可用性文本,而不是脆弱的视觉位置。如果页面包含多个履行模式,应分别记录取货、运输和本地配送,而不是将它们合并为一个布尔值。
证据层存储非敏感诊断事实:关联ID、允许的主机名、产品ID、门店ID、挑战类型、提供者任务ID、经过时间、最终状态以及用于验证的选择器或响应字段。它应删除令牌、Cookie、API密钥、地址和任何客户数据。仅在有意义的状态变化时发出警报,并在单个瞬态结果可能导致操作噪音时要求两次观察。
在实现CAPTCHA恢复之前,确认您有权自动化选定的零售页面,并且采集目的已记录。尊重合同限制、适用法律、适用于您使用的robots指令以及合理的请求速率。机器人排除协议 描述了标准化的爬虫指令;它是更广泛的授权审查中的一种信号,而不是访问授权。
使用以下先决条件:
requests和Playwright。秘密应来自管理环境或秘密存储。OWASP秘密管理指南 支持将凭证与应用程序代码、日志和构建工件分离。不要在文章、提示、截图或故障排除票中放置真实客户端密钥、Cookie、求解令牌或客户地址。
检测器应在每次可能触发验证页面的操作后运行:初始导航、门店更改、取货面板打开、分页和库存刷新。检测应返回结构化证据,而不是简单布尔值。
from dataclasses import dataclass
@dataclass(frozen=True)
class CaptchaEvidence:
kind: str
website_url: str
website_key: str
async def detect_recaptcha_v2(page) -> CaptchaEvidence | None:
frame = page.locator('iframe[src*="recaptcha"]')
textarea = page.locator('textarea[name="g-recaptcha-response"]')
if await frame.count() == 0 and await textarea.count() == 0:
return None
key = await page.locator('[data-sitekey]').first.get_attribute('data-sitekey')
if not key:
raise RuntimeError("reCAPTCHA detected without a readable site key")
return CaptchaEvidence(
kind="recaptcha_v2",
website_url=page.url,
website_key=key,
)
输入是已打开的Playwright页面。该操作检查两个显式小部件信号并读取页面提供的站点密钥。输出是None或类型化证据对象。当挑战可见但无法确认密钥时,函数会报错;猜测密钥或重用其他页面的密钥会使恢复不可靠。
在调用求解器之前,验证page.url仍属于允许的零售主机,并且当前运行仍持有预期的产品和门店标识符。如果重定向到账户页面、结账、支付流程或意外域名,停止并标记为policy_denied。技术能力并不提供访问私有、受限、敏感或未经授权数据的权限。
官方CapSolver reCAPTCHA v2任务指南记录了ReCaptchaV2TaskProxyLess、websiteURL和websiteKey。createTask API 返回taskId;getTaskResult API 返回终端结果。成功的reCAPTCHA v2结果包括solution.gRecaptchaResponse。
import os
import time
import requests
CAPSOLVER_API = "https://api.capsolver.com"
def solve_recaptcha_v2(website_url: str, website_key: str) -> str:
client_key = os.environ["CAPSOLVER_API_KEY"]
created = requests.post(
f"{CAPSOLVER_API}/createTask",
json={
"clientKey": client_key,
"task": {
"type": "ReCaptchaV2TaskProxyLess",
"websiteURL": website_url,
"websiteKey": website_key,
},
},
timeout=30,
).json()
if created.get("errorId") or not created.get("taskId"):
raise RuntimeError(created.get("errorDescription", "createTask failed"))
task_id = created["taskId"]
deadline = time.monotonic() + 120
while time.monotonic() < deadline:
result = requests.post(
f"{CAPSOLVER_API}/getTaskResult",
json={"clientKey": client_key, "taskId": task_id},
timeout=30,
).json()
if result.get("status") == "ready":
token = result.get("solution", {}).get("gRecaptchaResponse")
if not token:
raise RuntimeError("ready result did not contain gRecaptchaResponse")
return token
if result.get("status") == "failed" or result.get("errorId"):
raise RuntimeError(result.get("errorDescription", "CAPTCHA task failed"))
time.sleep(3)
raise TimeoutError("CAPTCHA task exceeded the 120-second deadline")
函数输入是验证的页面URL和站点密钥。该操作创建一个任务并仅轮询其taskId。输出是记录的令牌字符串。停止条件是明确的:任务创建错误、失败状态、格式错误的就绪响应、网络超时或120秒截止时间。传输重试可能会重复同一任务结果的HTTP请求,但不得静默创建一系列新可计费任务。
使用您的CapSolver优惠码
立即提升您的自动化预算!
在充值CapSolver账户时使用优惠码 CAP26,每次充值可获得额外 5% 的奖励 —— 无限制。
现在在您的 CapSolver仪表板 中兑换
令牌应用是页面特定的。在授权集成或受控测试页面上,将令牌写入记录的响应字段并调用页面的预期回调或提交路径。不要将令牌复制到新浏览器、另一个产品页面或不同门店上下文中。
async def apply_recaptcha_token(page, token: str) -> None:
applied = await page.evaluate(
"""
(token) => {
const fields = [...document.querySelectorAll(
'textarea[name="g-recaptcha-response"]'
)];
if (fields.length === 0) return false;
for (const field of fields) {
field.value = token;
field.innerHTML = token;
field.dispatchEvent(new Event('change', { bubbles: true }));
}
return true;
}
""",
token,
)
如果未应用:
raise RuntimeError("reCAPTCHA响应字段在应用前消失")
此示例仅限于当前页面可见的响应字段。某些实现还要求有记录的回调。检查您拥有或有权测试的页面并连接到其实际集成合同。永远不要虚构回调名称。应用后,等待小部件或验证路由改变状态,然后在库存操作完成后继续。
库存验证应将观察到的状态与请求的产品和商店绑定。如果商店标签静默更改或产品页面重定向到变体,则仅显示“可用”的选择器是不够的。
from datetime import datetime, timezone
async def read_inventory(page, expected_sku: str, expected_store: str) -> dict:
sku = (await page.locator('[data-product-sku]').first.get_attribute('data-product-sku'))
store = (await page.locator('[data-store-id]').first.get_attribute('data-store-id'))
label = (await page.locator('[data-inventory-status]').first.inner_text()).strip()
if sku != expected_sku or store != expected_store:
raise RuntimeError("在收集过程中产品或商店上下文发生了变化")
normalized = {
"In stock": "in_stock",
"Limited stock": "limited",
"Out of stock": "out_of_stock",
}.get(label)
if not normalized:
raise RuntimeError(f"无法识别的库存标签: {label!r}")
return {
"product_id": sku,
"store_id": store,
"availability": normalized,
"observed_at": datetime.now(timezone.utc).isoformat(),
"verification": "product-store-status",
}
选择器是您控制或有权自动化的页面的占位符。输入是当前页面和预期标识符。该操作在规范化标签之前检查身份。输出是一个验证的库存记录。如果缺少元素、标识符更改或状态不熟悉,该函数将停止。这些停止可以防止常见故障:将更改的页面布局转换为虚假的缺货事件。
对于生产环境,当可用时添加第二个证据渠道。例如包括结构化产品对象、页面发起的XHR响应、提货面板商店标签或根据协议允许的购物车资格检查。收集器应要求身份和可用性证据之间的一致性,而不是简单地重复请求以增加信心。
可预测的状态机使工作流可观察并防止递归恢复。
作用域
-> 打开产品
-> 确认门店
-> 检测检查点
-> 无挑战: 读取库存
-> reCAPTCHA v2: 创建一个任务
-> 准备就绪: 在同一会话中应用
-> 失败/超时: 需要审核
-> 不支持/模糊: 需要审核
-> 重新播放库存操作一次
-> 验证产品 + 门店 + 可用性
-> 有效: 写入记录
-> 重复挑战: 需要审核
-> 上下文更改: 策略拒绝
该任务应通过每个状态携带一个关联ID。记录转换时间和结果,但永远不要记录已解决的令牌或客户端密钥。CapSolver错误和故障排除常见问题解答可以帮助操作员区分提供商故障与浏览器和应用程序故障。
症状包括缺少站点密钥、意外的验证页面或适配器无法识别的小部件家族。捕获已脱敏的截图和顶级主机,然后停止。不要发送猜测的参数或将每个iframe都视为reCAPTCHA。
非零的errorId、失败状态、格式错误的就绪结果或轮询超时是提供商边界故障。保留taskId、记录的错误描述、经过时间以及关联ID。只有在错误类别明确为瞬态且剩余任务预算允许时才重试。
响应字段可能因页面导航、重新渲染或更改门店上下文而消失。不要自动将令牌应用到替换页面。重新运行作用域和上下文检查,然后从干净状态重新启动单个产品检查,或将其路由到审核。
提供商可能在页面拒绝解决方案时返回就绪任务,因为会话、页面时间、小部件元数据或回调路径不匹配。将此归类为应用故障。一次受控重放就足够。如果CAPTCHA重复,请停止而不是开始循环。
如果页面加载但库存证据缺失或冲突,请记录unknown,而不是out_of_stock。当零售商或模板的模糊性超过阈值时发出警报,因为这通常表明标记更改而非真实库存事件。
HTTP语义规范有助于区分传输状态和应用含义。200 OK仅描述HTTP响应;它不能证明已选择某个位置、接受CAPTCHA或返回库存。
良好的库存自动化在最小化收集的同时最大化信心。根据业务需求和来源允许的频率进行轮询。缓存稳定的商品元数据。在批准的窗口内使用随机延迟安排门店检查,而不是突发的并行请求。在源支持的情况下使用条件获取,并在挑战率或应用错误率意外上升时停止运行。
分别跟踪这些指标:
不要仅优化求解器完成率。高就绪率但低应用接受率表明集成问题。高接受率但上升的未知库存率表明提取器或页面模板问题。这些指标应引导操作员找到拥有故障的层级。
当数据馈送警报时,去重重复观察并定义稳定性规则。例如,仅在正常收集间隔内两次有效检查后通知out_of_stock,而in_stock转换可能需要一次新的成功观察。将规则保留在配置中,以便业务团队可以审计为何发送了警报。
在您拥有或明确授权自动化的页面上测试工作流。使用固定装置进行普通库存状态测试,并使用受控的CAPTCHA集成进行恢复测试。
createTask失败并确认未写入库存记录。processing结果直到超时并确认仅创建了一个任务。gRecaptchaResponse并确认模式验证失败。needs_review。unknown,而不是out_of_stock。选择CAPTCHA求解API的指南提供了额外的评估标准,但此用例的接受测试仍为业务特定:系统必须在有限恢复后返回正确的商品、正确的门店和正确的可用性。
当将CAPTCHA处理视为经过验证的零售工作流中的受控状态时,商店库存数据收集变得可靠。保留商品和门店上下文,创建一个记录的任务,在同一授权会话中应用解决方案,重新播放库存操作一次,并在身份和可用性检查通过后发布数据。CapSolver提供CAPTCHA任务基础设施;您的收集器仍需负责作用域、速率限制、数据质量和停止条件。
Q: 商店库存数据收集的最小输出是什么?
最小可信输出包含规范的产品ID、门店或区域ID、标准化的可用性、观察时间以及用于验证状态的证据。空白页面或失败的检查点绝不能转换为缺货。
Q: 为什么在CAPTCHA恢复期间必须保留相同的浏览器会话?
相同的会话保留了与检查点相关的页面、cookies、产品选择、门店选择和小部件生命周期。将结果转移到其他上下文可能导致拒绝或将观察结果附加到错误的门店。
Q: 库存作业应进行多少次CAPTCHA重试?
使用小的显式预算:一个任务和一次受控的应用重放是实际的默认值。重复的挑战应进入needs_review,以便在不昂贵或破坏性循环的情况下检查集成。
Q: 就绪的CapSolver任务是否证明已收集库存?
不。就绪任务仅证明提供商返回了解决方案。浏览器必须接受它,并且应用程序仍必须返回已识别的产品、门店和库存状态。
Q: 此工作流能否从私人零售商门户收集库存?
只有在门户所有者明确授权该自动化且工作流符合适用协议和法律时才可能。技术能力并不授予访问私人、受限、敏感或未经授权数据的权限。