本文以前文中的需求以及代码式开发为例, 如果你还不了解, 可以跳转查看
需求分析为: 需求分析
代码开发为: 代码式开发
1. 定义模型
首先, 你需要导入 bricks 爬虫基类, 然后定义一个类继承这个基类, 本文以配置式开发(template)为例, 因此我们需要导入的基类的地址为: bricks.spider.template.Spider
from bricks.spider.template import Spider
class MySpider(Spider):
pass2. 添加配置
Spider 的配置是 bricks.spider.template.Config, 有以下几个属性
-
init: 关于初始化的配置 -
download: 关于下载请求的配置 -
parse: 解析配置 -
pipeline: 存储配置 -
events: 事件配置
from bricks.spider.template import Spider, Config
class MySpider(Spider):
@property
def config(self) -> Config:
return Config()2.1 配置初始化: init
inti 配置为一个列表, 列表中传入 template.Init 类型, 其配置参数作用为:
func: 初始化函数, 在初始化时会调用该函数, 测试时可以填写一个lambda表达式,bricks目前封装了一些内置方法, 在bricks.plugins.make_seeds中, 如by_csv,by_mongo,by_redis,by_sqliteargs: 初始化函数的位置参数kwargs: 初始化函数的关键词参数layout: 一些修改参数rename: 将结果中的某些键值删除替换为另一键值, 例:{"a": "b"}, 将结果中的a键值改为b键值default: 为结果中的某个键设置默认值, 例:{"a": 1000}factory: 将结果中的某个值作为参数传入工厂函数中, 例:{"a": lambda x: x + 1}show: 是否展示结果中的某个键值, 例:{"a": True, "b": lambda: x: x> 10}
import time
from bricks.db.redis_ import Redis
from bricks.plugins.make_seeds import by_redis
from bricks.spider import template
from bricks.spider.template import Config
# 需自行配置 redis
from no_views.conn import redis_config
class Spider(template.Spider):
def __init__(self, **kwargs):
super().__init__(**kwargs)
# 需自行配置 redis
self.redis = Redis(
**redis_config
)
@property
def config(self) -> Config:
return Config(
init=[
template.Init(
func=by_redis,
kwargs={
'conn': self.redis,
'path': '*|*',
'key_type': 'string',
},
layout=template.Layout(
factory={
"time": lambda: time.time()
}
)
)
]
)2.2 配置下载: download
与 init 类似, 配置为一个列表, 需传入 template.Download, 其配置参数为:
url: 请求地址params: 请求参数method: 请求方法body: 请求体, 所有的body,data,json都在这里填入, 会根据headers中的Content-Type自动判断headers: 请求头cookies: 请求cookiesoptions: 其他配置, 包括auth等等, 下载器需要的额外参数也放在这里timeout: 超时时间allow_redirects: 是否允许重定向proxies: 代理, 形式为:"http://ip:prot"proxy: 代理键, 代理键会在bricks中自动获取代理, 形式为:{"ref": ...(代理类), ...(连接参数)}is_success: 通过状态码判断请求是否通过, 形式为脚本字符串:"response.status_code in [200, 302]"retry: 重试次数起始值max_retry: 最大重试次数
import time
from bricks.db.redis_ import Redis
from bricks.plugins.make_seeds import by_redis
from bricks.spider import template
from bricks.spider.template import Config
from no_views.conn import redis_config
class Spider(template.Spider):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.redis = Redis(
**redis_config
)
@property
def config(self) -> Config:
return Config(
init=[
template.Init(
func=by_redis,
kwargs={
# 需自行配置 redis
'conn': self.redis,
'path': '*|*',
'key_type': 'string',
},
layout=template.Layout(
factory={
"time": lambda: time.time()
}
)
)
],
download=[
template.Download(
url="https://fx1.service.kugou.com/mfanxing-home/h5/cdn/room/index/list_v2",
params={
"page": "{page}",
"cid": 6000
},
headers={
"User-Agent": "@chrome",
"Content-Type": "application/json;charset=UTF-8",
}
)
]
)2.3 配置解析: parse
与以上类似, 配置 template.Parse, 参数配置为:
func: 解析函数, 在解析时会调用该函数, 或者填入框架默认支持的解析器, 如:json,xpath,jsonpath,regexargs: 解析函数的位置参数kwargs: 解析函数的关键词参数layout: 一些修改参数, 与init一致
import time
from bricks.db.redis_ import Redis
from bricks.plugins.make_seeds import by_redis
from bricks.spider import template
from bricks.spider.template import Config
from no_views.conn import redis_config
class Spider(template.Spider):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.redis = Redis(
**redis_config
)
@property
def config(self) -> Config:
return Config(
init=[
template.Init(
func=by_redis,
kwargs={
# 需自行配置 redis
'conn': self.redis,
'path': '*|*',
'key_type': 'string',
},
layout=template.Layout(
factory={
"time": lambda: time.time()
}
)
)
],
download=[
template.Download(
url="https://fx1.service.kugou.com/mfanxing-home/h5/cdn/room/index/list_v2",
params={
"page": "{page}",
"cid": 6000
},
headers={
"User-Agent": "@chrome",
"Content-Type": "application/json;charset=UTF-8",
}
)
],
parse=[
template.Parse(
func="json",
kwargs={
"rules": {
"data.list": {
"userId": "userId",
"roomId": "roomId",
"score": "score",
"startTime": "startTime",
"kugouId": "kugouId",
"status": "status",
}
}
},
layout=template.Layout(
rename={"userId": "user_id"},
default={"modify_at": time.time(), "page": "{page}", "seeds_time": "{time}"}
)
)
]
)2.4 配置存储: pipeline
配置 template.Pipeline, 参数为:
func: 存储函数, 在解析时会调用该函数,bricks目前封装了一些内置方法, 在bricks.plugins.storage中, 如to_csv,to_mongo,to_redis,to_sqliteargs: 存储函数的位置参数kwargs: 存储函数的关键词参数layout: 一些修改参数, 与init一致success: 是否在存储成功后将对应种子删除
import time
from bricks.db.redis_ import Redis
from bricks.plugins.make_seeds import by_redis
from bricks.spider import template
from bricks.spider.template import Config
from no_views.conn import redis_config
class Spider(template.Spider):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.redis = Redis(
**redis_config
)
@property
def config(self) -> Config:
return Config(
init=[
template.Init(
func=by_redis,
kwargs={
# 需自行配置 redis
'conn': self.redis,
'path': '*|*',
'key_type': 'string',
},
layout=template.Layout(
factory={
"time": lambda: time.time()
}
)
)
],
download=[
template.Download(
url="https://fx1.service.kugou.com/mfanxing-home/h5/cdn/room/index/list_v2",
params={
"page": "{page}",
"cid": 6000
},
headers={
"User-Agent": "@chrome",
"Content-Type": "application/json;charset=UTF-8",
}
)
],
parse=[
template.Parse(
func="json",
kwargs={
"rules": {
"data.list": {
"userId": "userId",
"roomId": "roomId",
"score": "score",
"startTime": "startTime",
"kugouId": "kugouId",
"status": "status",
}
}
},
layout=template.Layout(
rename={"userId": "user_id"},
default={"modify_at": time.time(), "page": "{page}", "seeds_time": "{time}"}
)
)
],
pipeline=[
template.Pipeline(
func=lambda context: print(context.items),
success=True
)
]
)2.5 配置事件: events
events 为一个字典套列表套 task , 其中 events 的键为 bricks 内部的事件类型, 引用地址为: bricks.state.const, 一般常用的事件为:
BEFORE_REQUEST: 下载之前执行的事件AFTER_REQUEST: 下载之后执行的事件BERORE_PIPELINE: 存储之前执行的事件AFTER_PIPELINE: 存储之后执行的事件
具体的配置样例为:
events={
const.AFTER_REQUEST: [
template.Task(
func=scripts.is_success,
kwargs={
"match": [
"context.response.get('code') == 0"
]
}
),
],
const.BEFORE_PIPELINE: [
template.Task(
func=scripts.turn_page,
kwargs={
"match": [
"context.response.get('data.hasNextPage') == 1"
],
}
),
]
}如以上代码, 则会在请求之后执行 scripts.is_success 方法, 在存储之前执行 scripts.turn_page 方法, 这些方法默认会接受一个 context 参数(由 bricks 自动生成传递), 该参数为请求的上下文, 包含当前事件为止的所有属性, 如 request, response 等...
3 整体代码
import time
from bricks import const
from bricks.db.redis_ import Redis
from bricks.lib.queues import RedisQueue
from bricks.plugins import scripts
from bricks.plugins.make_seeds import by_redis
from bricks.spider import template
from bricks.spider.template import Config
from no_views.conn import redis_config
class Spider(template.Spider):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.redis = Redis(
**redis_config
)
@property
def config(self) -> Config:
return Config(
init=[
template.Init(
func=by_redis,
kwargs={
# 需自行配置 redis
'conn': self.redis,
'path': '*|*',
'key_type': 'string',
},
layout=template.Layout(
factory={
"time": lambda: time.time()
}
)
)
],
download=[
template.Download(
url="https://fx1.service.kugou.com/mfanxing-home/h5/cdn/room/index/list_v2",
params={
"page": "{page}",
"cid": 6000
},
headers={
"User-Agent": "@chrome",
"Content-Type": "application/json;charset=UTF-8",
}
)
],
parse=[
template.Parse(
func="json",
kwargs={
"rules": {
"data.list": {
"userId": "userId",
"roomId": "roomId",
"score": "score",
"startTime": "startTime",
"kugouId": "kugouId",
"status": "status",
}
}
},
layout=template.Layout(
rename={"userId": "user_id"},
default={"modify_at": time.time(), "page": "{page}", "seeds_time": "{time}"}
)
)
],
pipeline=[
template.Pipeline(
func=lambda context: print(context.items),
success=True
)
],
events={
const.AFTER_REQUEST: [
template.Task(
func=scripts.is_success,
kwargs={
"match": [
"context.response.get('code') == 0"
]
}
)
],
const.BEFORE_PIPELINE: [
template.Task(
func=scripts.turn_page,
kwargs={
"match": [
"context.response.get('data.hasNextPage') == 1"
]
}
)
]
}
)
if __name__ == '__main__':
spider = Spider(
task_queue=RedisQueue(**redis_config),
)
spider.run(
task_name='all',
)