mcpField
S
scrape-scrapy-cloud

scrape-scrapy-cloud

github.com/zytedata云与 DevOps11153 次安装2 天前更新
代码仓库

关于 scrape-scrapy-cloud

面向 Claude 的 Scrapy Cloud 操作技能,可通过 `shub` 和 Cloud API 管理项目。涵盖部署、蜘蛛调度、任务监控与结果检查,并指导配置发现且避免泄露 API 密钥。

主要功能

  1. 01使用 `uvx shub` 部署 Scrapy 项目
  2. 02按参数和设置调度蜘蛛
  3. 03等待任务并检查状态
  4. 04检查日志、字段覆盖率和样本条目
  5. 05发现项目配置且不暴露密钥

适用场景

  1. 01将 Scrapy 项目部署到 Scrapy Cloud
  2. 02使用自定义参数调度蜘蛛
  3. 03检查已完成任务的日志和提取条目

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/zytedata/skills --skill scrape-scrapy-cloud

安装后在 Claude Code 中输入/scrape-scrapy-cloud确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

scripts/
auth.py
scrapy_cloud_api.py
wait_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——按原样使用;将空字符串视为“未提供参数”:

  1. 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 字段:

值级别
10DEBUG
20INFO
30WARNING
40ERROR
50CRITICAL

每条日志都是一个 JSON 对象,包含以下字段:time(Unix 毫秒)、level 和 message。

参考文档

Scrapy Cloud 文档提供 Markdown 格式(将 .html 替换为 .md)。请优先获取这些文档,而不要猜测 API 的使用方式——文档详细介绍了全部参数、格式和字段:


使用时机

当用户要求执行以下操作时,请调用此技能:

  • 将 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_ENDPOINThttps://app.zyte.com/api/Jobs API 基础 URL(可覆盖为预发布环境)
SCRAPY_CLOUD_STORAGE_ENDPOINThttps://storage.zyte.com/抓取结果/日志存储基础 URL(可覆盖为预发布环境)

Web UI 基础 URL 通过移除 SCRAPY_CLOUD_ENDPOINT 末尾的 /api/ 路径得出。例如:

  • https://app.zyte.com/api/ → https://app.zyte.com
  • https://app-staging.zyte.com/api/ → https://app-staging.zyte.com

常见问题

症状原因修复
Error: No such command / shub not found直接调用了 shub,但未安装使用 uvx shub … 调用——按需获取,无需安装
Authentication error / 401API 密钥缺失或无效运行 shub login 或设置 $SHUB_APIKEY
403API 密钥无权访问此项目确认项目 ID 和密钥权限
Spider not foundSpider 名称错误或项目尚未部署确认 Spider 名称;先使用 uvx shub deploy 部署
Project N does not exist项目 ID 或别名错误检查 scrapinghub.yml,或指定正确的 ID
Could not find requirements filescrapinghub.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% 或低得异常页面对象中的选择器失效或不正确检查条目统计信息和部分条目样本,修复页面对象,重新部署并重新运行。参见“验证作业结果”

评论

支持 Markdown