6. feapder
这是什么:feapder(读作 "F-eapder",feapder = fast · easy · async · practical · durable · redis)是国产开源爬虫框架,最大卖点是分布式开箱即用:单机版不需要任何中间件,接上 Redis 就变成分布式集群——同一套代码,从笔记本平滑搬到多台服务器。
为什么学它:Scrapy 想做分布式要额外拼 scrapy-redis、自己管队列;feapder 把「分布式」做成了换一个类名的事,还内置了任务去重、失败重试、监控告警(对接钉钉/飞书/企业微信),国内业务型爬虫团队用得很多。
如何用:认识四种爬虫模板 → 用 AirSpider 跑通单机版 → 理解它和 Scrapy 的分工。
装包
pip install feapder
四种爬虫模板:按规模选
| 模板 | 类 | 依赖 | 适用场景 |
|---|---|---|---|
| 轻量 Air 爬虫 | feapder.AirSpider |
无(单机) | 中小规模,学习和单机项目首选 |
| 分布式爬虫 | feapder.Spider |
Redis | 大规模、多机、需要断点续爬 |
| 批次爬虫 | feapder.BatchSpider |
Redis | 按天/按批次的周期采集(增量更新) |
| 任务爬虫 | feapder.TaskSpider |
Redis | 任务由外部下发(如从数据库取待抓列表) |
一句话:学习和小项目用 AirSpider,量大上 Spider,周期任务上 BatchSpider。模板间代码结构几乎一致,升级只换继承的类。
AirSpider 跑通第一个爬虫
存成 feapder_demo.py 运行:
import feapder
class QuoteSpider(feapder.AirSpider):
def start_requests(self):
# 起点:yield 几个 Request,就爬几个页面
yield feapder.Request("https://quotes.toscrape.com/")
def parse(self, request, response):
for quote in response.css("div.quote"):
text = quote.css("span.text::text").get_first()
author = quote.css("small.author::text").get_first()
print(f"{author}:{text[:40]}...")
print(f"完成:{request.url}")
if __name__ == "__main__":
QuoteSpider().start()
你会看到:多线程并发抓取的日志刷屏,页面数据打印出来——feapder 默认开多线程,这是它和 Scrapy(默认 twisted 异步)气质不同的地方:直观、好调试。
代码结构和 Scrapy 神似:
| Scrapy | feapder | 说明 |
|---|---|---|
start_urls 列表 |
start_requests() 里 yield feapder.Request |
起点入口,逻辑可以更灵活 |
parse(self, response) |
parse(self, request, response) |
多了个 request,可回读请求参数 |
response.css(...).get() |
response.css(...).get_first() |
同一套选择器语法,方法名不同 |
| 框架命令行跑 | 类自己 .start() |
就是个普通 Python 程序,右键运行/断点调试都行 |
翻页同样是「解析里再 yield 新 Request」:
def parse(self, request, response):
next_page = response.css("li.next a::attr(href)").get_first()
if next_page:
yield feapder.Request(request.urljoin(next_page), callback=self.parse)
数据落库:yield Item 进管道
和 Scrapy 的 Item/Pipeline 思路一样,feapder 里把数据 yield feapder.Item(...),由框架统一送进存储管道;配置好数据库后还能自动建表入库(MySQL / MongoDB / ES 均支持)。入门阶段先用最朴素的方式接住数据——在 parse 里直接写文件:
def parse(self, request, response):
rows = []
for quote in response.css("div.quote"):
rows.append({
"text": quote.css("span.text::text").get_first(),
"author": quote.css("small.author::text").get_first(),
})
import json
with open("feapder_quotes.json", "a", encoding="utf-8") as f:
f.write(json.dumps(rows, ensure_ascii=False) + "\n")
正式项目再把存储搬进 Item 管道,业务代码保持干净。
上 Redis:单机变分布式
这是 feapder 的招牌时刻——把 AirSpider 改成 Spider,配一个 Redis 地址,任务队列就从「本机内存」搬进「Redis」:
import feapder
class QuoteSpider(feapder.Spider):
# 分布式配置(本机起了 Redis 就能体验多进程协作)
__custom_setting__ = {
"REDISDB_IP_PORTS": "127.0.0.1:6379",
"REDISDB_USER_PASS": "",
"REDISDB_DB": 0,
}
def start_requests(self):
yield feapder.Request("https://quotes.toscrape.com/")
def parse(self, request, response):
print(f"{request.url} 抓完了")
之后多台机器跑同一个类,任务自动分片、互不重复、谁挂了任务自动回收——你不需要写一行分布式代码。去重(防重复抓取)和断点续爬也随 Redis 队列自动获得。
常见翻车现场
| 症状 | 原因 | 修复 |
|---|---|---|
get() 取不到值报错 |
feapder 的取值方法是 get_first() / get_all() |
别混用 parsel 的 get()/getall() |
| Spider 启动连不上 Redis | Spider 模板必须配 Redis |
没装 Redis 就先用 AirSpider |
| 日志太多看不清数据 | feapder 日志默认详细 | 按官方文档调低日志级别 |
| Windows 下多线程卡住 | 终端与编码/防火墙偶发问题 | 加 if __name__ == "__main__" 入口(本来就该加) |
动手改
- 给
start_requests一次性 yield 第 1~10 页的 URL(提示:循环 1 到 10),和翻页写法对比 - 把
QuoteSpider(feapder.AirSpider)改成feapder.Spider并装个 Redis 体验分布式配置(没有 Redis 就跳过,读懂区别即可) - 在 parse 里统计每个标签(a.tag)出现次数,跑完打印 Top 5