Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions Skills/feapder-crawler/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
---
name: feapder-crawler
description: 当用户要使用、生成、维护、审查或排查基于 feapder 框架的 Python 爬虫时使用;如果用户正在做 feapder 项目、要求用 feapder、安装/配置 feapder,或当前工作区代码/依赖已显示在使用 feapder,即使用户没再次说 feapder,只要在讨论分布式爬虫、断点续爬、批次采集、任务表消费、自动入库、Request/Response、Item/Pipeline、浏览器渲染、代理/去重、CLI 项目模板、运行时配置或 feapder.utils.tools 小工具,也要使用。若用户明确指定 Scrapy、requests-only、Playwright-only、FastAPI、Celery 等非 feapder 技术栈,则不要使用,除非任务是迁移到 feapder、对比 feapder,或用户明确要求改用这些技术。
metadata:
short-description: Build and debug feapder crawlers
---

# Feapder Crawler

这个 Skill 用于处理 feapder 爬虫相关工作:创建爬虫、选择爬虫类型、接入任务队列、解析响应、保存数据、配置运行环境,或诊断现有 feapder 项目。

## 第一轮判断

1. 先判断用户是在创建新爬虫、修改现有爬虫、排查运行问题、设计采集架构,还是配置入库/渲染/部署。
2. 如果是现有项目,先读真实入口,不要只按印象判断:
- `setting.py`
- `main.py`
- `spiders/`
- `items/`
- 自定义 pipeline 模块
- tests 或示例爬虫
3. 如果当前目录不确定或不在项目根,先通过 `main.py`、`setting.py`、`spiders/`、`items/`、`requirements.txt` / `pyproject.toml` 中的 feapder 依赖定位项目根;确认项目根后再编辑配置或运行生成命令。
4. 确认实际运行方式:
- 直接运行爬虫脚本
- `main.py` + `ArgumentParser`
- TaskSpider 或 BatchSpider 的 master/worker 分离
- feaplat 托管部署
5. 配置优先级按这个顺序判断:爬虫类 `__custom_setting__` > 项目 `setting.py` > 环境变量 > `feapder/setting.py` 默认值。
6. “feapder 项目上下文”不等于必须在 feapder 源码仓库里;用户可能在新文件夹、业务项目或空目录里创建 feapder 爬虫。只要用户要求用 feapder,或项目依赖/代码形态显示正在使用 feapder,就按 feapder 工作流处理。

## 爬虫类型选择

- 小型、单机、无需 Redis、无需分布式和断点续爬:用 `AirSpider`。
- Redis 分布式、断点续爬、自动 item 缓冲、失败状态和大任务队列:用 `Spider`。
- 种子任务来自 Redis/MySQL 或其他任务源,且需要区分任务下发与 worker 消费:用 `TaskSpider`。
- 周期性批次采集,任务状态和批次记录必须落 MySQL:用 `BatchSpider`。
- 多数据源共用一个 Spider 调度:用 `BaseParser` + `Spider.add_parser()`。
- 多数据源共用一个 BatchSpider 批次调度:用 `BatchParser` + `BatchSpider.add_parser()`,任务行里要能标识 parser。

详细取舍和示例见 `references/spider-selection.md`。

## 按场景读取

- 新项目、CLI、项目结构和启动入口:读 `references/project-workflow.md`。
- Request callback、中间件、校验、Response 提取:读 `references/request-response.md`。
- Item、UpdateItem、数据库写入、CSV/MySQL/Mongo/custom pipeline:读 `references/item-pipeline.md`。
- settings、Redis/MySQL/Mongo、重试、日志、报警、任务丢失、缓存:读 `references/settings-runtime.md`。
- TaskSpider 和 BatchSpider 的 master/worker 流程:读 `references/batch-task-spider.md`。
- 多 parser 集成:读 `references/parser-integration.md`。
- feaplat 部署/平台运行定位:读 `references/feaplat-deploy.md`。
- 浏览器渲染、Playwright/Selenium、代理、UA、去重:读 `references/rendering-proxy-dedup.md`。
- feapder 自带小工具,如 cookies、URL、HTML、JSON、时间、hash、SQL、报警、CLI shell、文件工具:读 `references/utilities.md`。
- 需要对源码做定位或验证时:读 `references/source-map.md`。

## 实施规则

- 优先使用 feapder 生成器和项目现有模式,不要手写一套不一致的脚手架。
- 用户要新建标准 feapder 项目时,默认使用 `feapder create -p <project_name>` 生成项目结构,不要手写目录和模板;只有用户明确要求单文件脚本或自定义结构时才例外。
- 在已有 feapder 标准项目里新增 spider 时,先定位项目根,再进入 `<project_root>/spiders/` 执行 `feapder create -s <spider_name>`;不要在项目根直接执行 `-s`,也不要手写模板替代生成器,除非用户明确要求。
- 已有项目新增或调整 Redis/MySQL/代理/线程/重试/渲染/pipeline 配置时,优先修改项目实际加载的 `setting.py`;只有配置确实只属于单个 spider,或项目已有模式使用 `__custom_setting__`,才放进 spider 类的 `__custom_setting__`。修改时增量合并用户已有配置,不要覆盖。
- 用户明确要求 feapder 或当前项目使用 feapder 时,示例代码默认必须继承 feapder 的 Spider 类,并使用 `feapder.Request`、`feapder.Response`、`Item` / `UpdateItem`、`ITEM_PIPELINES` 等框架路径;AI 不允许自行退化成纯 `requests`、`Scrapy`、独立 Playwright、手写 CSV 或直接 SQL。若你判断确实需要脱离 feapder 路径,必须先向用户说明原因、影响和替代方案,并明确请求用户授权;用户确认前不要写非 feapder 实现。
- 标准 feapder 项目里,数据模型默认放在 `items/<table>_item.py`,优先用 `feapder create -i <table_name>` 生成 `Item` / `UpdateItem` 类;spider 里只导入 `XxxItem`、赋字段、`yield item`。不要默认在 spider 里写 `_build_xxx_item()` 并动态构造 `feapder.Item()` / `feapder.UpdateItem()`,除非是单文件 `AirSpider`、临时 demo,或用户明确要求快速脚本。
- 用户询问“要不要脱离 feapder”“改成 requests 怎么样”这类方案判断时,只能先做风险/收益评估和替代方案说明;这不等于授权实现。只有用户明确确认“就改成非 feapder 实现”后,才可以写非 feapder 代码。
- 不要假设 `AirSpider` 的配置行为等同于 Redis 分布式爬虫;先确认基类和启动参数。
- 对 `BatchSpider` / `TaskSpider`,必须区分任务下发 `start_monitor_task()` 和 worker 采集 `start()`。
- 多页面、多来源、多步骤解析时,优先把回调写成公开的 `parse_xxx` 方法,并在 `feapder.Request(..., callback=self.parse_xxx)` 显式绑定;不要把所有 URL 分支塞进默认 `parse()` 再转调 `_parse_xxx` 私有方法。默认 `parse()` 只适合未指定 callback 的入口或很小的单页爬虫。
- 同一批种子可以直接构造多个并列来源 URL 时,在 `start_requests()` 中并列 `yield feapder.Request(..., callback=self.parse_source)`;不要先请求来源 A,再在 `parse_a()` 里创建来源 B 的请求。只有从当前 response 解析出来的派生 URL,才在对应 callback 内继续 `yield Request`。
- 排查入库问题时,沿着 `yield Item` 或 `yield UpdateItem` 追到 `ITEM_PIPELINES`,再看具体 pipeline 和配置。
- 排查解析问题时,先看 `Request.callback`、`parser_name`、`download_midware`、`validate`、`exception_request`、`failed_request`,不要急着改 scheduler。
- 遇到 import/path 问题时,记住 feapder 从 `items/` 或 `spiders/` 启动时会把项目根目录插入 `sys.path`。
- 做 PR 级别改动时,用最小相关测试或可运行示例验证,避免无关重构。

## 高信号源码

- `feapder/__init__.py`:公开 API 导出和导入路径行为。
- `feapder/core/base_parser.py`:parser 生命周期钩子。
- `feapder/core/spiders/`:AirSpider、Spider、TaskSpider、BatchSpider。
- `feapder/network/request.py`:Request 参数、重试、渲染标记、缓存辅助。
- `feapder/network/response.py`:xpath/css/re/json/bs4 提取和编码处理。
- `feapder/network/item.py`:Item、UpdateItem、表名、指纹、单 item pipeline。
- `feapder/pipelines/`:内置 pipeline 行为。
- `feapder/utils/tools.py`:cookies、URL、JSON、日期、SQL、hash、文件、JS、报警和常见转换工具。
- `feapder/commands/shell.py`:基于 `Request` 的 cURL/URL 响应调试器。
- `feapder/setting.py`:默认运行时配置全集。
- `feapder/commands/`:CLI 生成器。
4 changes: 4 additions & 0 deletions Skills/feapder-crawler/agents/openai.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
interface:
display_name: "Feapder Crawler"
short_description: "构建和排查 feapder 爬虫工作流"
default_prompt: "使用 $feapder-crawler 设计或排查一个 feapder 爬虫。"
227 changes: 227 additions & 0 deletions Skills/feapder-crawler/references/batch-task-spider.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
# TaskSpider 和 BatchSpider

## TaskSpider

`TaskSpider` 把种子任务下发和 worker 采集分开。

常见构造参数:

```python
spider = TaskSpiderTest(
task_table="spider_task",
task_keys=["id", "url"],
redis_key="test:task_spider",
keep_alive=True,
)
```

Redis 任务源:

```python
spider = TaskSpiderTest(
task_table="spider_task2",
task_table_type="redis",
redis_key="test:task_spider",
keep_alive=True,
use_mysql=False,
)
```

最小完整结构:

```python
import feapder
from items.seed_result_item import SeedResultItem


class SeedTaskSpider(feapder.TaskSpider):
def add_task(self):
self._redisdb.zadd(self._task_table, {"id": 1, "url": "https://example.com"})

def start_requests(self, task):
task_id, url = task
yield feapder.Request(url, task_id=task_id)

def parse(self, request, response):
item = SeedResultItem()
item.url = request.url
item.title = response.xpath("//title/text()").extract_first()
yield item


def create_mysql_task_spider():
return SeedTaskSpider(
task_table="spider_task",
task_keys=["id", "url"],
redis_key="feapder:task_spider",
keep_alive=True,
)


def create_redis_task_spider():
return SeedTaskSpider(
task_table="spider_task_redis",
task_table_type="redis",
redis_key="feapder:task_spider",
keep_alive=True,
use_mysql=False,
)
```

运行模式:

```python
spider.start_monitor_task() # 下发和监控任务
spider.start() # worker 采集
```

`add_task()` 可在 `start_monitor_task()` 阶段塞种子任务。不要把它写成死循环。

`start_requests(self, task)` 接收一行任务。常见读取方式:

```python
task_id, url = task
task_id = task.id
url = task["url"]
url = task.get("url")
```

## BatchSpider

`BatchSpider` 用于周期性批次采集。它用 Redis 做请求调度,用 MySQL 维护任务和批次状态。
不要把 BatchSpider 写成单入口普通脚本。它通常需要区分 master 下发/监控和 worker 采集,并在 request 上携带 `task_id`,解析完成后用 `update_task_batch()` 更新状态。

典型构造:

```python
spider = ProductSpider(
redis_key="feapder:product",
task_table="product_task",
task_keys=["id", "url"],
task_state="state",
batch_record_table="product_batch_record",
batch_name="product daily crawl",
batch_interval=1,
)
```

最小完整结构:

```python
import feapder
from items.product_price_item import ProductPriceItem


class ProductBatchSpider(feapder.BatchSpider):
def start_requests(self, task):
task_id, url = task
yield feapder.Request(url, task_id=task_id)

def parse(self, request, response):
item = ProductPriceItem()
item.url = request.url
item.title = response.xpath("//title/text()").extract_first()
yield item

yield self.update_task_batch(request.task_id, 1)

def failed_request(self, request, response, e):
yield self.update_task_batch(request.task_id, -1)


def create_spider():
return ProductBatchSpider(
redis_key="feapder:product_price",
task_table="product_task",
task_keys=["id", "url"],
task_state="state",
batch_record_table="product_batch_record",
batch_name="product price daily",
batch_interval=1,
)


if __name__ == "__main__":
spider = create_spider()
# spider.start_monitor_task() # master: 下发和监控任务
spider.start() # worker: 采集
```

任务状态约定:

- `0`:待抓取
- `1`:已完成
- `2`:抓取中或已下发
- `-1`:无效或永久失败

每个批次默认会把非 `-1` 任务重置为 `0`。如果是增量采集,已完成任务不应重置,可以重写 `init_task()` 并置空。

## 任务完成状态

请求中携带 `task_id`:

```python
def start_requests(self, task):
task_id, url = task
yield feapder.Request(url, task_id=task_id)
```

标记完成:

```python
yield self.update_task_batch(request.task_id, 1)
```

超过最大重试后标记无效:

```python
def failed_request(self, request, response, e):
yield self.update_task_batch(request.task_id, -1)
```

普通 `Spider` 失败处理通常不需要更新任务表,但可以记录失败、切换 cookie/proxy 或返回新请求:

```python
def exception_request(self, request, response, e):
# 单次异常,可调整 request 后重试
request.headers = {"User-Agent": "Mozilla/5.0"}
yield request


def failed_request(self, request, response, e):
# 超过最大重试次数后的最终失败
self.logger.error(f"failed url={request.url}, error={e}")
```

不要在 `parse()` 里手写主重试循环;优先使用 `SPIDER_MAX_RETRY_TIMES`、`validate()`、`exception_request()` 和 `failed_request()`。

## Debug 模式

Spider:

```python
debug_spider = SpiderTest.to_DebugSpider(
redis_key="feapder:spider",
request=feapder.Request("https://example.com"),
)
debug_spider.start()
```

BatchSpider:

```python
debug_spider = BatchSpiderTest.to_DebugBatchSpider(
task_id=1,
redis_key="feapder:batch",
task_table="batch_task",
task_keys=["id", "url"],
task_state="state",
batch_record_table="batch_record",
batch_name="batch test",
batch_interval=1,
)
debug_spider.start()
```

Debug 模式默认通常不入库、不更新任务状态,除非显式配置。
Debug 模式适合调单个 request、`parse`、`validate`、`download_midware` 或单个批次任务;它不是生产全链路验证,不能替代 master/worker、队列、pipeline 和部署环境检查。
35 changes: 35 additions & 0 deletions Skills/feapder-crawler/references/feaplat-deploy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# feaplat 部署定位

feaplat 是 feapder 生态的爬虫管理系统。用户提到 feaplat、平台部署、平台调度、平台上不跑任务时,先做只读定位,不要直接改 spider 代码或本地启动方式。

## 只读定位顺序

1. 确认平台运行的是哪个项目包、哪个入口文件、哪个命令参数。
2. 读取项目 `main.py`,确认 `ArgumentParser` 参数和实际 spider 构造。
3. 读取目标 spider,确认继承类型:`AirSpider`、`Spider`、`TaskSpider`、`BatchSpider`。
4. 读取 `setting.py` 和 spider `__custom_setting__`,按配置优先级判断实际 Redis/MySQL/pipeline 配置。
5. 确认 master/worker 是否都运行:
- `TaskSpider` / `BatchSpider` 下发任务通常需要 `start_monitor_task()`。
- worker 采集需要 `start()`。
6. 检查 Redis 队列和失败队列 key 是否有数据:
- `{redis_key}:z_requests`
- `{redis_key}:z_failed_requests`
- `{redis_key}:s_failed_items`
- `{redis_key}:h_spider_status`
7. 检查 MySQL 任务表和批次记录表:
- `task_table`
- `task_state`
- `batch_record_table`
8. 检查日志中真实报错、配置值、入口参数和工作目录。
9. 区分平台问题、配置问题、队列问题、数据库连接问题和 spider 解析代码问题。

## 常见判断

- 平台上“不跑任务”不等于 spider 代码错,可能是 master 没下发、worker 没启动、`redis_key` 不一致、任务表状态不对、配置没加载或包版本不是最新。
- 平台部署后配置不生效时,优先确认平台注入的环境变量、项目 `setting.py`、spider `__custom_setting__`、工作目录和运行入口。
- `BatchSpider` 本批次未结束时,下一批通常不会开始;先查批次记录和任务表状态。
- 不要为了平台问题直接把 feapder 代码改成 `requests` 脚本;AI 判断必须脱离 feapder 时,也要先说明原因、影响、替代方案,并明确请求用户授权。

## 输出建议

先给用户一个定位清单和要读取/执行的只读命令。只有确认根因在代码或配置文件后,再做最小改动。
Loading