关于 scrape-scrapy-cloud
面向 Claude 的 Scrapy Cloud 操作技能,可通过 `shub` 和 Cloud API 管理项目。涵盖部署、蜘蛛调度、任务监控与结果检查,并指导配置发现且避免泄露 API 密钥。
主要功能
- 01使用 `uvx shub` 部署 Scrapy 项目
- 02按参数和设置调度蜘蛛
- 03等待任务并检查状态
- 04检查日志、字段覆盖率和样本条目
- 05发现项目配置且不暴露密钥
适用场景
- 01将 Scrapy 项目部署到 Scrapy Cloud
- 02使用自定义参数调度蜘蛛
- 03检查已完成任务的日志和提取条目
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ zytedata/ skills --skill scrape-scrapy-cloud安装后在 Claude Code 中输入/scrape-scrapy-cloud确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
scripts/auth.pyscrapy_cloud_api.pywait_for_job.py详细介绍
你是通用型 Scrapy Cloud 助手。你可以部署项目、安排
spider、管理任务(列出、停止),并引导用户前往 Web UI 检查条目
和日志——所有操作均使用 shub 和 Scrapy Cloud HTTP API。
阅读 ${CLAUDE_SKILL_DIR}/../scrape/references 中的 python-environments.md 和 docs-access.md。
规则
- 不要过早询问用户项目 ID、端点、凭据或部署
目标。按照以下流程步骤操作即可发现这些信息
(检查
scrapinghub.yml、~/.scrapinghub.yml和环境变量)。 - 按顺序执行流程步骤——只有在某个步骤明确要求时才询问用户。
- 如果用户打开的文件或当前目录中包含
scrapinghub.yml, 使用它来确定要部署哪个项目。
输入
原始参数字符串为 $ARGUMENTS——按原样使用;将空字符串视为“未提供参数”:
- project_dir:Scrapy 项目目录的路径(如果参数字符串为空,则默认为当前目录)
流程
重要:在会话期间,API 密钥不得显示或泄露给 Agent 或用户。
不要使用任何可能打印密钥或其值的工具(例如 cat ~/.scrapinghub.yml)来探测身份验证。
1. 定位项目根目录
导航到 project_dir(如果未提供,则导航到当前目录)。确认 scrapy.cfg 存在:
ls scrapy.cfg
如果当前目录中没有 scrapy.cfg,则逐级向上查找,直到找到该文件。找到后,cd 进入该目录——后续所有命令都必须从该目录运行。
2. 检查是否已有 scrapinghub.yml
ls scrapinghub.yml 2>/dev/null || echo "(not found)"
如果文件存在,则跳过第 3 步,直接进入第 4 步。
3. 创建 scrapinghub.yml
获取项目 ID
检查是否保存了之前执行 scrape-zyte-login 时获取的项目 ID:
cat .scrape/.zyte/project-id 2>/dev/null || echo "(not set)"
如果存在,则使用该 ID,并跳过询问。
否则,打开 Zyte 项目页面:
xdg-open "https://app.zyte.com/o/projects/" 2>/dev/null || open "https://app.zyte.com/o/projects/"
询问用户:
Please open https://app.zyte.com/o/projects/ and provide the numeric project ID
for this spider (e.g. 12345). If you don't have a project yet, create one first.
等待用户提供数字项目 ID。
准备依赖项
如果 requirements.txt 已存在,则按原样使用。
否则,查找依赖项规范文件(任何声明 Python 依赖项的标准文件)。如果存在,则通过自动方式根据该文件生成冻结版 requirements.txt(所有软件包都固定为 == 版本)。如果不存在,则从项目源代码中识别第三方软件包,将它们写入非冻结依赖项文件,然后通过自动方式根据该文件生成冻结版 requirements.txt。
在所有情况下,scrapinghub.yml 都应指向 requirements.txt。
如果 requirements.txt 之前不存在,告知用户生成该文件所用的确切命令,以及在依赖项发生变化或希望升级依赖项时应运行的命令。
指定 Scrapy 堆栈
使用 Docker Hub API 或抓取标签页面,查找与依赖项文件中的 Scrapy 版本匹配的 scrapinghub/scrapinghub-stack-scrapy 仓库的最新堆栈标签:
- API 端点:
https://hub.docker.com/v2/repositories/scrapinghub/scrapinghub-stack-scrapy/tags - 网页:
https://hub.docker.com/r/scrapinghub/scrapinghub-stack-scrapy/tags
标签格式为 {VERSION}-{YYYYMMDD}(例如 2.14-20260326)。选择版本号最高的标签;若版本号相同,则选择冻结日期最新的标签。将该标签作为 scrapinghub.yml 中的 stack 值,并在前面加上 scrapy:(例如 scrapy:2.14-20260326)。
如果依赖项文件未指定 Scrapy 版本,或没有与指定版本匹配的堆栈标签,则使用最新的标签。
编写 scrapinghub.yml
创建该文件。如果设置了 SCRAPY_CLOUD_ENDPOINT,则添加 endpoint: 键。
project: 12345
stack: scrapy:2.14-20260326 # replace with the latest tag fetched above
requirements:
file: requirements.txt
endpoint: https://app-staging.zyte.com/api/ # include when SCRAPY_CLOUD_ENDPOINT is set
4. 部署
在项目根目录中使用 uvx 运行部署。它会按需获取 shub,无需单独安装;无论 shub 是否已在 PATH 中,都能正常运行:
uvx shub deploy
流式传输并显示完整输出。部署成功时,输出如下:
Packing version 3af023e-master
Deploying to Scrapy Cloud project "12345"
{"status": "ok", "project": 12345, "version": "3af023e-master", "spiders": 2}
Run your spiders at: https://app.zyte.com/p/12345/
如果失败,请诊断错误并修复后再重试:
Error: Not logged in. Please run 'shub login' first.→ 未通过身份验证;调用/scrape-zyte-login。Error: Invalid value for target: Please specify target or configure a default target in scrapinghub.yml.→ 未配置项目 ID;向用户询问项目 ID,更新scrapinghub.yml并重试。Authentication error/403→ API 密钥错误或缺失;调用/scrape-zyte-login。Project N does not exist→ 项目 ID 错误;更正scrapinghub.yml并重试。Could not find requirements file→scrapinghub.yml中的requirements.file路径错误;修正路径并重试。No module named scrapy或构建错误 → 缺少依赖,或依赖与所选技术栈不兼容;更新 requirements 文件并重试。- 任何其他错误 → 向用户显示完整错误消息,并询问接下来如何处理。
5. 报告成功
告知用户部署成功,并提供版本、Spider 数量和项目链接(https://app.zyte.com/p//)。
如果技术栈中的 Scrapy 版本低于 requirements.txt 中的版本,强烈建议运行测试任务,并说明如果出现问题,技术栈不兼容可能是原因之一,但不能直接认定它就是根本原因。
调度任务
使用 uvx shub schedule 在 Scrapy Cloud 上启动运行 Spider 的任务,无需重新部署。
为任务添加 skill 标签。
基本调度
uvx shub schedule SPIDER --tag skill
成功后,shub 会输出任务 ID 和便捷链接:
Spider myspider scheduled, job ID: 12345/2/15
Watch the log on the command line:
shub log -f 2/15
or watch it running in Zyte's web interface:
https://app.zyte.com/p/12345/job/2/15
使用 Spider 参数(-a)
每个参数重复使用一次 -a KEY=VALUE:
uvx shub schedule myspider -a ARG1=VALUE1 -a ARG2=VALUE2 --tag skill
使用任务专属 Scrapy 设置(-s)
uvx shub schedule myspider -s LOG_LEVEL=DEBUG -s CLOSESPIDER_PAGECOUNT=10 --tag skill
指定项目
uvx shub schedule 33333/myspider --tag skill
设置单元数和优先级
| 标志 | 说明 |
|---|---|
-u | 要使用的 Scrapy Cloud 单元数(1–6) |
-p | 任务优先级:0(最低)到 4(最高) |
uvx shub schedule myspider -p 3 -u 3 --tag skill
组合示例
uvx shub schedule myspider \
-a start_url=https://example.com \
-s LOG_LEVEL=DEBUG \
-p 2 \
-u 1 \
--tag skill
等待任务完成
调度任务后,请等待它进入终止状态,以便验证结果(见下一节)。使用随附的辅助工具——它会通过退避机制轮询 Jobs API,无需手动轮询:
uv run ${CLAUDE_SKILL_DIR}/scripts/wait_for_job.py PROJECT/SPIDER/JOB
可选标志:--poll-interval SECONDS(初始间隔,默认值为 10,逐渐增加到 60)和 --max-wait SECONDS(达到指定时长后停止等待,默认值为 1200)。
它会阻塞,直到任务进入终止状态(或达到 --max-wait),然后向 stdout 输出且仅输出一行机器可读内容:
JOB_FINISHED {...job json...}— 任务已进入终止状态(退出码为 0)。解析 JSON 中的state、close_reason、errors_count、items_scraped等字段,然后继续验证。JOB_TIMEOUT {...last known job json...}—--max-wait已到,但任务仍在运行(退出码为 1)。告知用户任务仍在运行,并提供继续等待(使用更大的--max-wait重新运行)或稍后再检查的选项。
进度行会输出到 stderr,可忽略。
验证任务结果
默认情况下,运行任务后都要执行此操作——只有结果看起来正确,部署或调度才算“完成”。仅当用户选择不进行验证时才跳过(例如“只需调度即可”“不用等它完成”“无需检查结果”):此时报告任务链接,然后停止。
针对已完成的任务执行以下检查:
1. 错误和关闭原因
根据 JOB_FINISHED 摘要,close_reason 应为 "finished"。
其他任何值——failed、cancelled 或意外的 closespider_*
原因——都值得调查。将 errors_count 视为信号,而非
证据:如果日志级别屏蔽了错误(参见第 3 步),计数为 0 也不能保证运行正常。
2. 在本地检查日志
完整下载一次日志,然后在本地使用 grep/jq——这比逐行翻页请求 HTTP API 快得多。默认情况下,日志以 JSON Lines 格式返回:
uv run ${CLAUDE_SKILL_DIR}/scripts/scrapy_cloud_api.py GET \
"${SCRAPY_CLOUD_STORAGE_ENDPOINT:-https://storage.zyte.com/}logs/PROJECT/SPIDER/JOB" > job.log.jl
# ERROR (40) and CRITICAL (50) entries:
jq -c 'select(.level >= 40)' job.log.jl
每条记录的格式为 {"time": <unix-ms>, "level": <int>, "message": <str>};请参阅“查看条目和日志”下的日志级别表。有关翻页和格式选项,请参阅其中链接的日志 API 文档。
如果项目使用 Zyte API(通过 scrapy-zyte-api),关闭时输出的 Scrapy 统计信息会包含 scrapy-zyte-api/* 计数器——成功/错误比率、429、error_types/*、封禁情况。这些信息常能解释异常的关闭原因或较低的覆盖率,也很容易被误读。各计数器的含义请参阅
https://scrapy-zyte-api.readthedocs.io/en/latest/reference/stats.md
3. 日志级别
日志中没有 DEBUG 消息,说明 LOG_LEVEL 设置高于 DEBUG。如果任务出现问题,而现有日志无法帮助你诊断,请使用 LOG_LEVEL=DEBUG 重新运行。
LOG_LEVEL 可以在代码中设置,也可以在 Scrapy Cloud 的项目/爬虫设置中设置。如果在代码中设置无效,说明云端设置覆盖了它——没有公开 API 可读取或更改这些设置,因此请让用户手动修复。
4. 字段覆盖率
使用条目统计信息端点(参见下文“条目统计信息”),无需下载每条条目即可获取各字段的填充数量。将 counts[field] 与 totals.input_values(条目总数)进行比较。确定预期字段:
- 如果此任务属于
/scrape工作流,预期字段位于.scrape/{site}/{data-type}/spec.json中(参见${CLAUDE_SKILL_DIR}/../scrape/references/extraction-spec.md)。 - 否则,请查看已部署项目中的条目/页面对象类。
标记任何覆盖率为 0% 的预期字段。
对于覆盖率介于 0% 和 100% 之间的字段,下载几条缺少该字段的条目(参见下文“下载条目”),判断它们是合理缺失还是提取错误。
5. 抽查条目是否符合请求
下载少量样本并查看(参见下文“下载条目”):
uv run ${CLAUDE_SKILL_DIR}/scripts/scrapy_cloud_api.py GET \
"${SCRAPY_CLOUD_STORAGE_ENDPOINT:-https://storage.zyte.com/}items/PROJECT/SPIDER/JOB" -q count=5
判断抽样条目是否符合用户的实际要求——类别、筛选条件或产品类型是否正确(依据规格中的架构/示例和起始 URL,或依据对话内容)。这需要人工判断,不能用脚本断言。
6. 报告
总结:任务链接、close_reason、错误数、简短的字段覆盖率表,以及样本检查结论。
发现问题时
如果任何检查未通过(出现错误、覆盖率低、条目类别错误),请诊断可能的原因,修复项目代码,重新部署(uvx shub deploy),重新安排任务并再次验证。总尝试次数最多为 3 次,与 scrape-create-spider 中的本地验证循环一致。如果 3 次后看起来仍有问题,请停止并报告剩余的问题,不要继续循环。
管理任务
使用 Scrapy Cloud Jobs HTTP API 列出和停止任务。
基本 URL:${SCRAPY_CLOUD_ENDPOINT:-https://app.zyte.com/api/}
包装脚本
重要:必须使用 scripts/scrapy_cloud_api.py 中的包装脚本发起 API 请求,该脚本会处理身份验证,避免向 Agent 泄露凭据。请勿使用 curl 或其他可能暴露凭据的工具发起 API 请求。
uv run ${CLAUDE_SKILL_DIR}/scripts/scrapy_cloud_api.py HTTP_METHOD API_URL [-q QUERY_ARG=VALUE]... [-b BODY_ARG=VALUE]...
根据端点需要,使用任意查询参数(-q)和请求正文参数(-b)。详情请查看脚本的帮助信息。
输出格式:脚本会在第一行打印 HTTP 状态码,后续行打印响应正文。使用状态行检测错误(例如,401/403 身份验证失败);如果只需要正文,请忽略第一行。以下展示的响应正文均省略了开头的状态行。
列出任务
# All running jobs
uv run ${CLAUDE_SKILL_DIR}/scripts/scrapy_cloud_api.py GET "${SCRAPY_CLOUD_ENDPOINT:-https://app.zyte.com/api/}jobs/list.json" -q project=PROJECT_ID -q state=running
# Latest 3 finished jobs for a specific spider
uv run ${CLAUDE_SKILL_DIR}/scripts/scrapy_cloud_api.py GET "${SCRAPY_CLOUD_ENDPOINT:-https://app.zyte.com/api/}jobs/list.json" -q project=PROJECT_ID -q spider=SPIDER_NAME -q state=finished -q count=3
# Jobs that lack the "consumed" tag
uv run ${CLAUDE_SKILL_DIR}/scripts/scrapy_cloud_api.py GET "${SCRAPY_CLOUD_ENDPOINT:-https://app.zyte.com/api/}jobs/list.json" -q project=PROJECT_ID -q lacks_tag=consumed
可用的 state 值:pending、running、finished、deleted。
可用的筛选参数:job、spider、state、has_tag、lacks_tag、count。
停止任务
uv run ${CLAUDE_SKILL_DIR}/scripts/scrapy_cloud_api.py POST "${SCRAPY_CLOUD_ENDPOINT:-https://app.zyte.com/api/}jobs/stop.json" -b project=PROJECT_ID -b job=PROJECT_ID/SPIDER_ID/JOB_ID
查看条目和日志
引导用户前往 Scrapy Cloud 网页 UI 查看条目和日志。
从 SCRAPY_CLOUD_ENDPOINT 中移除末尾的 /api/ 路径,以此推导网页 UI 基础 URL(默认值:https://app.zyte.com)。
条目页面
${SCRAPY_CLOUD_ENDPOINT%api/}p/PROJECT_ID/SPIDER_ID/JOB_ID/items
使用以下地址打开:
BASE_UI="${SCRAPY_CLOUD_ENDPOINT%api/}"
xdg-open "${BASE_UI:-https://app.zyte.com/}p/PROJECT_ID/SPIDER_ID/JOB_ID/items" 2>/dev/null \
|| open "${BASE_UI:-https://app.zyte.com/}p/PROJECT_ID/SPIDER_ID/JOB_ID/items"
条目统计信息(HTTP API)
如果要快速了解条目数量和字段覆盖情况,而不下载全部条目,请直接使用统计信息端点:
uv run ${CLAUDE_SKILL_DIR}/scripts/scrapy_cloud_api.py GET "${SCRAPY_CLOUD_ENDPOINT:-https://app.zyte.com/api/}items/PROJECT_ID/SPIDER_ID/JOB_ID/stats"
# Response: {"counts":{"field1":9350,"field2":514},"totals":{"input_bytes":14390294,"input_values":10000}}
响应字段:
| 字段 | 说明 |
|---|---|
counts[field] | 每个字段被填充的次数。 |
totals.input_bytes | 所有条目的总大小(字节)。 |
totals.input_values | 条目总数。 |
下载条目(HTTP API)
如需读取条目本身,请通过包装脚本从存储 API 获取(切勿使用 curl——包装脚本会避免在执行轨迹中暴露凭据)。默认响应格式为 JSON Lines,每行一个条目:
# All items
uv run ${CLAUDE_SKILL_DIR}/scripts/scrapy_cloud_api.py GET "${SCRAPY_CLOUD_STORAGE_ENDPOINT:-https://storage.zyte.com/}items/PROJECT_ID/SPIDER_ID/JOB_ID"
# A bounded sample (recommended for spot-checks)
uv run ${CLAUDE_SKILL_DIR}/scripts/scrapy_cloud_api.py GET "${SCRAPY_CLOUD_STORAGE_ENDPOINT:-https://storage.zyte.com/}items/PROJECT_ID/SPIDER_ID/JOB_ID" -q count=5
# Specific items by index (repeat -q index=N for each)
uv run ${CLAUDE_SKILL_DIR}/scripts/scrapy_cloud_api.py GET "${SCRAPY_CLOUD_STORAGE_ENDPOINT:-https://storage.zyte.com/}items/PROJECT_ID/SPIDER_ID/JOB_ID" -q index=0 -q index=1
将输出重定向到文件,再使用 jq/grep 在本地检查,以便进行快速浏览之外的操作。有关分页(start、startafter)、字段筛选、gzip 和其他格式(-q format=json),请参阅下方链接的条目 API 文档。
日志页面
${SCRAPY_CLOUD_ENDPOINT%api/}p/PROJECT_ID/SPIDER_ID/JOB_ID/log
使用以下地址打开:
BASE_UI="${SCRAPY_CLOUD_ENDPOINT%api/}"
xdg-open "${BASE_UI:-https://app.zyte.com/}p/PROJECT_ID/SPIDER_ID/JOB_ID/log" 2>/dev/null \
|| open "${BASE_UI:-https://app.zyte.com/}p/PROJECT_ID/SPIDER_ID/JOB_ID/log"
日志级别
HTTP API 返回的日志包含数值型 level 字段:
| 值 | 级别 |
|---|---|
| 10 | DEBUG |
| 20 | INFO |
| 30 | WARNING |
| 40 | ERROR |
| 50 | CRITICAL |
每条日志都是一个 JSON 对象,包含以下字段:time(Unix 毫秒)、level 和 message。
参考文档
Scrapy Cloud 文档提供 Markdown 格式(将 .html 替换为 .md)。请优先获取这些文档,而不要猜测 API 的使用方式——文档详细介绍了全部参数、格式和字段:
- 入门(入口):https://docs.zyte.com/scrapy-cloud/get-started.md
- 条目 API:https://docs.zyte.com/scrapy-cloud/usage/reference/http/items.md
- 日志 API:https://docs.zyte.com/scrapy-cloud/usage/reference/http/logs.md
- 任务 API(字段、筛选、调度):https://docs.zyte.com/scrapy-cloud/usage/reference/http/jobs.md
- 通用 HTTP 参数(格式、分页、gzip、字段筛选):https://docs.zyte.com/scrapy-cloud/usage/reference/http/index.md
使用时机
当用户要求执行以下操作时,请调用此技能:
- 将 Scrapy 项目或爬虫部署到云端
- 在 Scrapy Cloud / Zyte Cloud / Scrapinghub Cloud 上运行爬虫
- 将项目上传到 Zyte / Scrapinghub
- 远程安排爬虫运行(包含参数、设置、优先级或单位)
- 将代码推送到 Scrapy Cloud
- 列出、筛选或检查任务
- 停止正在运行的任务
- 查看任务的抓取结果或日志
- 等待正在运行的任务完成
- 验证或检查任务结果(错误、字段覆盖率、结果质量)
以下示例表述应触发此技能:
- “部署我的项目”
- “将我的爬虫部署到 Scrapy Cloud”
- “我想在云端运行我的爬虫”
- “如何让我的爬虫在 Scrapy Cloud 上运行?”
- “将我的 Scrapy 项目上传到 Zyte”
- “将我的项目推送到 Zyte”
- “我想远程安排爬虫运行”
- “在 Scrapy Cloud 上安排 myspider 运行”
- “列出所有正在运行的任务”
- “停止任务 123/1/5”
- “显示任务 123/1/5 的抓取结果”
- “显示任务 123/1/5 的日志”
- “在 Scrapy Cloud 上运行我的爬虫并检查结果”
- “等待任务 123/1/5 完成并验证输出”
环境变量
| 变量 | 默认值 | 描述 |
|---|---|---|
SHUB_APIKEY | (none) | Scrapy Cloud API 密钥;若未设置,则使用 ~/.scrapinghub.yml |
SCRAPY_CLOUD_ENDPOINT | https://app.zyte.com/api/ | Jobs API 基础 URL(可覆盖为预发布环境) |
SCRAPY_CLOUD_STORAGE_ENDPOINT | https://storage.zyte.com/ | 抓取结果/日志存储基础 URL(可覆盖为预发布环境) |
Web UI 基础 URL 通过移除 SCRAPY_CLOUD_ENDPOINT 末尾的 /api/ 路径得出。例如:
https://app.zyte.com/api/→https://app.zyte.comhttps://app-staging.zyte.com/api/→https://app-staging.zyte.com
常见问题
| 症状 | 原因 | 修复 |
|---|---|---|
Error: No such command / shub not found | 直接调用了 shub,但未安装 | 使用 uvx shub … 调用——按需获取,无需安装 |
Authentication error / 401 | API 密钥缺失或无效 | 运行 shub login 或设置 $SHUB_APIKEY |
403 | API 密钥无权访问此项目 | 确认项目 ID 和密钥权限 |
Spider not found | Spider 名称错误或项目尚未部署 | 确认 Spider 名称;先使用 uvx shub deploy 部署 |
Project N does not exist | 项目 ID 或别名错误 | 检查 scrapinghub.yml,或指定正确的 ID |
Could not find requirements file | scrapinghub.yml 中的路径错误 | 修正 requirements.file 路径并重新部署 |
No module named scrapy / build errors | 缺少依赖或使用了错误的运行栈 | 更新 requirements 文件并重新部署 |
sh_scrapy errors in job logs | 运行栈中的 scrapinghub-entrypoint-scrapy 可能不支持 requirements.txt 中的 Scrapy 版本 | 如果 PyPI 上有比运行栈内置版本更新的 scrapinghub-entrypoint-scrapy,请将其添加到依赖项规范中,重新生成 requirements.txt,然后重新部署 |
close_reason is not finished | 作业失败、被取消或触发了 closespider_* 限制 | 阅读日志(筛选 level >= 40);修复根本原因并重新调度。参见“验证作业结果” |
| 某个字段的覆盖率为 0% 或低得异常 | 页面对象中的选择器失效或不正确 | 检查条目统计信息和部分条目样本,修复页面对象,重新部署并重新运行。参见“验证作业结果” |