scrapy 如何在多个模块里共用一个数据库连接池

2020-07-08 17:29:41 +08:00
 a65420321a
有个 scrapy 项目
整体流程大概是:
spider.start_requests 从 redis 的 tasks_keys 里获取 url
middleware.proxyMiddleware.process_request 从 redis 的 proxy_keys 里获取代理
spider.parse 解析出 item,需深度抓取的 url,深度抓取 url 放入 redis 的 tasks_keys 里
pipelines.MyItem 把解析出的 item 存入 redis 的 item_keys 里面
。。。。

总结来说,有 4 个地方需要用到同一个 redis 数据库,区别只在 key
目前的写法是,
middleware.proxyMiddleware 里面__init__声明了 redis.ConnectionPool 连接池,以及 redis.StrictRedis 连接
在 pipelines.MyItem 和 spider 继承的类__init__里面全部粘贴复制了一遍。。。

一模一样的代码写了三个地方

虽然程序可以正常跑,但是,这方法越看越觉得傻
有什么方法可以只声明一个 redis 链接,然后在 spider 、middleware 、pipeline 复用同一个链接吗?
搜了一圈,各种出错,没有实际进展
2309 次点击
所在节点    Python
12 条回复
zdnyp
2020-07-08 17:49:15 +08:00
写一个 pipeline 的基类,在 open_spider 的时候连接,close_spider 的时候关闭
a65420321a
2020-07-08 18:36:33 +08:00
@zdnyp


from .settings import ITEM_KEY
import json, redis

class RedisPipeline:

def __init__(self, redis_host, redis_port, redis_db):
self.redis_host = redis_host
self.redis_port = redis_port
self.redis_db = redis_db

@classmethod
def from_crawler(cls, crawler):
return cls(
redis_host=crawler.settings.get('REDIS_HOST'),
redis_port=crawler.settings.get('REDIS_PORT'),
redis_db=crawler.settings.get('REDIS_DB')
)

def open_spider(self, spider):
self.pool = redis.ConnectionPool(host=self.redis_host,
port=self.redis_port,
db=self.redis_db)
self.conn = redis.StrictRedis(connection_pool=self.pool)
print('#### pipelines.open_spider')

def close_spider(self, spider):
pass

def process_item(self, item, spider):
self.conn.rpush(ITEM_KEY, json.dumps(item))
return item



这样没错吧?
我没搞懂的是,在 middleware 和 spider 里面要怎么调用这个 self.conn 呢?
996635
2020-07-08 19:47:03 +08:00
建议楼主看一下 scrapy 自带的几个 extensions 例子, 可能会有一些灵感. 印象中 scrapy 准备的几个钩子可以做这个事情.
另外,如果想全局复用连接池, 要考虑线程安全的问题.
spider 之间传递上下文靠 meta.
pipeline 里可以获取 spider 的对象.
a65420321a
2020-07-08 20:25:26 +08:00
@996635

额,你说的钩子指的是哪些?

数据库链接搞定了
在 spider 下__init__构建了 self.pool 和 self.conn
然后在 pipeline 里通过 process_item 中的 spider 参数调用 spider.conn 可以复用数据库链接
middleware 同样是通过 process_request 的 spider 参数

话说,为啥我搜出来的都是说在 pipeline 下的 open_spider 里创建数据库链接,这样的话别的地方通过什么方式调用呢?
Kobayashi
2020-07-09 12:58:22 +08:00
@zdnyp 搭车问一下。我目前也是基类,每个 pipeline 绑定一个 Redis connection 。不过不太明白 itempipeline 工作原理,itempipeline 支持并行还是并发,pipeline 上单一 Redis 连接后用吗,要必要开连接池?

另外实际上 Redis 连接也能绑在 spider 上。spider 应该只有一个实例,异步的话是不是需要 redis 连接池? Redis 连接到底跟着谁比较好。

scrapy 文档里 architecture 解释的太简单了。
Kobayashi
2020-07-09 13:06:35 +08:00
Kobayashi
2020-07-09 13:10:10 +08:00
我的思路是

1. 每个 Pipeline 一个 redis 连接。由于不明白 pipeline 工作原理(并发?并行?),不确定要不要开连接池。
2. pipeline 上没有 redis 连接。直接绑在 Spider 上, Pipeline.process(self,item,spider) 可以访问到 spider 上的 redis 连接。这个恐怕需要连接池,大小不知道设置多少合适。
a65420321a
2020-07-09 19:49:47 +08:00
@Kobayashi
我之前一直在纠结,怎么在 spider 里面直接调用 pipeline 的 redis 链接,没有相通,最后我是在 spider 里面声明了 redis 连接池,在 pipeline 和 middleware 通过 spider 参数直接调用 redis 链接
现在看下来,在 pipeline 的 open_spider 中声明 redis 链接,在 spider 里面把需要筛选的东西处理成不同的 item,通过 pipeline 的 process_item 调用 redis 链接。。
我没理解错吧?
可是,如果这样的话,我需要在 middleware 里使用代理,也要用到 redis,这又要怎么搞。。。
Kobayashi
2020-07-09 21:44:09 +08:00
@a65420321a 按照我定义 RedisPipeline 类的方法再定义一个 ProxyPipeline 基类。自己创建的 MyPipeline 同时继承这 2 个类即可。我自己使用方式是在 Pipeline 中同时使用 redis 和 database 连接。
a65420321a
2020-07-10 10:38:37 +08:00
@Kobayashi
。。。pipeline 里面可以给 request 加代理吗?
Kobayashi
2020-07-11 01:14:38 +08:00
@a65420321a pipeline 只能拿到 Item,也就是你从结果中拿到的数据。加代理要在请求被 downloader 处理前,自定义 downloader middleware 即可。

兄弟,翻翻教程和文档好吗?教程的话推荐《 Python 3 爬虫开发实战》的最后几张 scrapy 讲解,介绍了 scrapy 架构。重要的是带了很多可以生产直接使用的实例。之后读完文档就可以随意用了。
a65420321a
2020-07-13 09:54:38 +08:00
@Kobayashi
看过的~你们说 pipeline 让我以为 middleware 可以调 pipeline 来着。。。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/688304

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX