# 简介
开发文档:https://feapder.com/#/README
- feapder 是一款上手简单,功能强大的 Python 爬虫框架,内置 AirSpider、Spider、TaskSpider、BatchSpider 四种爬虫解决不同场景的需求。
- 支持断点续爬、监控报警、浏览器渲染、海量数据去重等功能。
- 更有功能强大的爬虫管理系统 feaplat 为其提供方便的部署及调度
# 轻量爬虫(AirSpider)demo
# 创建项目
选择 AirSpider,回车
feapder create -s air_spider_test | |
> AirSpider | |
Spider | |
TaskSpider | |
BatchSpider |
会生成一个爬虫文件 air_spider_test.py
# 编辑爬虫文件
# -*- coding: utf-8 -*- | |
""" | |
Created on 2024-03-21 10:06:00 | |
--------- | |
@summary: | |
--------- | |
@author: yangxs | |
""" | |
import feapder | |
class Douban(feapder.AirSpider): | |
def start_requests(self): | |
for i in range(1, 6): | |
yield feapder.Request(f"https://movie.douban.com/top250?start={i * 25}&filter=") | |
def parse(self, request, response): | |
lis = response.xpath('//ol[@class="grid_view"]//li') | |
for li in lis: | |
item = dict() | |
item['title'] = li.xpath('.//span[@class="title"]/text()').extract_first() | |
item['score'] = li.xpath('.//span[@class="rating_num"]/text()').extract_first() | |
item['blurb'] = li.xpath('.//span[@class="inq"]/text()').extract_first() | |
item['href_info'] = li.xpath('.//div[@class="pic"]/a/@href').extract_first() | |
yield feapder.Request( | |
url=item['href_info'], | |
callback=self.detail_parse, | |
item=item | |
) | |
def detail_parse(self, request, response): | |
item = request.item | |
item['details'] = response.xpath('//span[@property="v:summary"]/text()').extract_first().strip() | |
yield item | |
if __name__ == "__main__": | |
Douban().start() |
start_requests()
发送请求的 urlparse()
解析函数
# 用 feapder 连接 MySQL 数据库
from feapder.db.mysqldb import MysqlDB | |
db = MysqlDB( | |
ip="192.168.10.129", port=3306, db="spider", user_name="root", ser_pass="123456"6 | |
) |
# 创建表
sql_creat_table = """ | |
create table if not exists douban_feapder( | |
id int primary key auto_increment, | |
title varchar(100) not null, | |
score varchar(100), | |
blurb varchar(100), | |
href_info varchar(100), | |
details text | |
); | |
""" | |
db.execute(sql_creat_table) |
# 添加数据
# ignore: 当插入数据的 id 已存在时,则直接忽略 | |
sql_save = "insert ignore into douban_feapder(title,score,blurb,href_info,details) values (%s,%s,%s,%s,%s)" | |
db.add(sql_save % ('1', '2', '3', '4', '5')) |
# 通过 setting.py 配置文件连接 MySQL 数据库
# 创建配置文件
feapder create --setting |
会生成 setting.py 文件,在此文件中配置 MySQL 链接相关信息
# MYSQL | |
MYSQL_IP = "192.168.10.129" | |
MYSQL_PORT = 3306 | |
MYSQL_DB = "spider" | |
MYSQL_USER_NAME = "root" | |
MYSQL_USER_PASS = "123456" |
# 生成 item 文件
在执行此命令前,需要先创建后对应的 MySQL 数据表
feapder create -i 表名 | |
Item | |
>Item 支持字典赋值 | |
UpdateItem | |
UpdateItem 支持字典赋值 |
再将生成的 item 文件中的类导入爬虫脚本代码文件中
from douban_feapder_item import DoubanFeapderItem | |
item = DoubanFeapderItem() |
然后将爬虫脚本提取的字段赋值给 item 对象,最后 yield item
,启动爬虫后,数据就会自动存入对应的数据库表
# 整体流程
创建 spider
feapder create -s douban
> AirSpider
Spider
TaskSpider
BatchSpider
# 会自动生成 douban.py,这个文件就是主要的爬虫启动文件
创建配置值文件
feapder create --setting
# 会自动生成一个 setting.py 文件
并设置 setting.py 中的 MySQL 数据库连接参数
# MYSQL
MYSQL_IP = "192.168.10.129"
MYSQL_PORT = 3306
MYSQL_DB = "spider"
MYSQL_USER_NAME = "root"
MYSQL_USER_PASS = "123456"
创建数据库表
自己创建一个 py 文件,用于执行创建数据库表的 SQL 语句
from feapder.db.mysqldb import MysqlDB
db = MysqlDB()
sql_creat_table = """
create table if not exists douban_feapder(
id int primary key auto_increment,
title varchar(100) not null,
score varchar(100),
blurb varchar(100),
href_info varchar(100),
details text
);
"""
db.execute(sql_creat_table)
直接运行即可,执行完后到自己的 MySQL 数据库查看是否
douban_feapder
表是否创建成功,若没创建成功,可手动创建创建 item 文件
feapder create -i douban_feapder
Item
>Item 支持字典赋值
UpdateItem
UpdateItem 支持字典赋值
# 会自动生成 douban_feapder_item.py
- douban_feapder --> 上一步中创建的表名
编辑第一步创建的 spider 文件
# douban.py
# -*- coding: utf-8 -*-
"""
Created on 2024-03-21 10:06:00
---------
@summary:
---------
@author: yangxs
"""
import feapder
from douban_feapder_item import DoubanFeapderItem
class Douban(feapder.AirSpider):
def start_requests(self):
for i in range(1, 6):
yield feapder.Request(f"https://movie.douban.com/top250?start={i * 25}&filter=")
def parse(self, request, response):
lis = response.xpath('//ol[@class="grid_view"]//li')
for li in lis:
item = DoubanFeapderItem()
item['title'] = li.xpath('.//span[@class="title"]/text()').extract_first()
item['score'] = li.xpath('.//span[@class="rating_num"]/text()').extract_first()
item['blurb'] = li.xpath('.//span[@class="inq"]/text()').extract_first()
item['href_info'] = li.xpath('.//div[@class="pic"]/a/@href').extract_first()
yield feapder.Request(
url=item['href_info'],
callback=self.detail_parse,
item=item
)
def detail_parse(self, request, response):
item = request.item
item['details'] = response.xpath('//span[@property="v:summary"]/text()').extract_first().strip()
print(item)
yield item
if __name__ == "__main__":
# thread_count 指定 5 个线程运行
Douban(thread_count=5).start()
- 爬虫脚本在解析提取数据字段时,存入 item 对象中数据对应的 key,要与创建的 douban_feapder_item.py 中__init__下的字段名保持一致
最后直接运行爬虫脚本文件 douban.py 即可
# 多数据库存储
将爬下来的数据同时存储到 MySQL 和 MongoDB
# 创建爬虫项目
feapder create -p ttjijin |
# 创建 Item
在创建 item 之前,先创建好 MySQL 数据库表
创建好数据库表后,执行 item 创建命令 (在 /item 路径下执行命令)
feapder create -i ttjijin | |
Item | |
>Item 支持字典赋值 | |
UpdateItem | |
UpdateItem 支持字典赋值 |
如果创建 item 时 -i 后面接的名称就是数据库表的名称,就不需要修改 __table_name__
,否则需要修改成对应的表名
# ttjijin_item.py | |
from feapder import Item | |
class TtjijinItem(Item): | |
""" | |
This class was generated by feapder | |
command: feapder create -i ttjijin | |
""" | |
__table_name__ = "ttjijin" | |
def __init__(self, *args, **kwargs): | |
self.daily_growth_rate = kwargs.get('daily_growth_rate') # 日增长率 | |
self.dtime = kwargs.get('dtime') # 日期 | |
...... |
# 配置 setting.py
配置 MySQL 与 MongoDB 的链接参数,并打开 Pipelines 管道
# MYSQL | |
MYSQL_IP = "192.168.10.129" | |
MYSQL_PORT = 3306 | |
MYSQL_DB = "spider" | |
MYSQL_USER_NAME = "root" | |
MYSQL_USER_PASS = "123456" | |
# MONGODB | |
MONGO_IP = "192.168.10.129" | |
MONGO_PORT = 27017 | |
MONGO_DB = "spider" | |
MONGO_USER_NAME = "root" | |
MONGO_USER_PASS = "123456" | |
ITEM_PIPELINES = [ | |
"feapder.pipelines.mysql_pipeline.MysqlPipeline", | |
"feapder.pipelines.mongo_pipeline.MongoPipeline", | |
] |
# 创建 pipeline.py
在 main.py 同级路径创建 pipelines 文件夹,并创建 mong_pipeline.py、mysql_pipeline.py
编辑内容
# mong_pipeline.py | |
from feapder.db.mongodb import MongoDB | |
from feapder.pipelines import BasePipeline | |
class MongoPipeline(BasePipeline): | |
def __init__(self): | |
self.db = MongoDB() | |
def save_items(self, table, items): | |
self.db.add_batch(coll_name='ttjijin', datas=items) |
# mysql_pipeline.py | |
from feapder.db.mysqldb import MysqlDB | |
from feapder.pipelines import BasePipeline | |
from items.ttjijin_item import TtjijinItem | |
class MysqlPipeline(BasePipeline): | |
def __init__(self): | |
self.db = MysqlDB() | |
def save_items(self, table, items): | |
sql = "insert ignore into ttjijin (no,cname,ename,dtime,iopv," \ | |
"ljjz,daily_growth_rate,week,month,three_month," \ | |
"six_month,year,two_year,three_year,this_year,since,procedure_fee) " \ | |
"values (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)" | |
datas = [] | |
for item in items: | |
item = TtjijinItem(item) | |
datas.append([item.no, item.cname, item.ename, item.dtime, item.iopv, item.ljjz, | |
item.daily_growth_rate, item.week, item.month, item.three_month, | |
item.six_month, item.year, item.two_year, item.three_month, | |
item.since, item.procedure_fee]) | |
self.db.add_batch(sql=sql, datas=datas) |
# 创建编辑 spider 文件
执行爬虫脚本创建命令(/spiders 路径下执行)
feapder create -s ttjijin | |
> AirSpider | |
Spider | |
TaskSpider | |
BatchSpider |
编辑爬虫脚本文件 ttjijin.py
import random | |
import re | |
import feapder | |
from items.ttjijin_item import TtjijinItem | |
class Ttjijin(feapder.AirSpider): | |
def start_requests(self): | |
url = "https://fund.eastmoney.com/data/rankhandler.aspx" | |
headers = { | |
"Accept-Language": "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7", | |
"Referer": "https://fund.eastmoney.com/data/fundranking.html", | |
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36", | |
} | |
for i in range(1, 11): | |
params = { | |
"op": "ph", | |
"dt": "kf", | |
"ft": "all", | |
"rs": "", | |
"gs": "0", | |
"sc": "qjzf", | |
"st": "desc", | |
"sd": "2023-03-20", | |
"ed": "2024-03-20", | |
"qdii": "", | |
"tabSubtype": ",,,,,", | |
"pi": str(i), | |
"pn": "50", | |
"dx": "1", | |
"v": str(random.random()) | |
} | |
yield feapder.Request(url, headers=headers, params=params, method="GET") | |
def parse(self, request, response): | |
# 提取网站 title | |
res = response.text | |
data = re.findall("datas:\[(.*?)\]", res) | |
lis = data[0][1:-1].split('","') | |
for li in lis: | |
item = TtjijinItem() | |
row = li.split(',') | |
item.no = row[0] | |
item.cname = row[1] | |
item.ename = row[2] | |
item.dtime = row[3] | |
item.iopv = row[4] | |
item.ljjz = row[5] | |
item.daily_growth_rate = row[6] + '%' if row[6] else '' | |
item.week = row[7] + '%' if row[7] else '' | |
item.month = row[8] + '%' if row[8] else '' | |
item.three_month = row[9] + '%' if row[9] else '' | |
item.six_month = row[10] + '%' if row[10] else '' | |
item.year = row[11] + '%' if row[11] else '' | |
item.two_year = row[12] + '%' if row[12] else '' | |
item.three_year = row[13] + '%' if row[13] else '' | |
item.this_year = row[14] + '%' if row[14] else '' | |
item.since = row[15] + '%' if row[15] else '' | |
item.procedure_fee = row[20] | |
yield item | |
if __name__ == "__main__": | |
Ttjijin(thread_count=5).start() |
最后运行爬虫脚本 ttjijin.py
即可
# 中间件
# 默认中间件
设置 userAgent,从写 download_midware 方法(位置:爬虫脚本文件)
import feapder | |
class Baidu(feapder.AirSpider): | |
def download_midware(self, request): | |
request.headers = {'User-Agent': '1111111111'} | |
def start_requests(self): | |
yield feapder.Request("https://www.baidu.com") | |
def parse(self, request, response): | |
# 提取网站 title | |
print(response.xpath("//title/text()").extract_first()) | |
# 提取网站描述 | |
print(response.xpath("//meta[@name='description']/@content").extract_first()) | |
print("网站地址: ", response.url) | |
if __name__ == "__main__": | |
Baidu().start() |
download_midware()
就是默认中间件的函数名,若在start_requests()
函数yield feapder.Request()
时不指定中间件,则会使用默认的中间件函数,即download_midware()
# 自定义中间件
import feapder | |
class Baidu(feapder.AirSpider): | |
def my_download_midware(self, request, response): | |
request.headers = {'User-Agent': '1111111111'} | |
def start_requests(self): | |
yield feapder.Request("https://www.baidu.com", download_midware=my_download_midware) | |
def parse(self, request, response): | |
# 提取网站 title | |
print(response.xpath("//title/text()").extract_first()) | |
# 提取网站描述 | |
print(response.xpath("//meta[@name='description']/@content").extract_first()) | |
print("网站地址: ", response.url) | |
if __name__ == "__main__": | |
Baidu().start() |
- 自定义中间件,只需要在
start_requests()
函数yield feapder.Request()
时指定download_midware=自定义中间件函数名
即可 - 注意:若自定义中间要处理请求,则返回 request;若中间件要处理响应,则返回 response
- 若同时要指定多个中间件,
yield feapder.Request()
时指定download_midware=[中间件1,中间件2,...]
即可