V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
a65420321a
V2EX  ›  Python

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

  •  
  •   a65420321a · 2020-07-08 17:29:41 +08:00 · 2485 次点击
    这是一个创建于 1603 天前的主题,其中的信息可能已经有所发展或是发生改变。
    有个 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 复用同一个链接吗?
    搜了一圈,各种出错,没有实际进展
    12 条回复    2020-07-13 09:54:38 +08:00
    zdnyp
        1
    zdnyp  
       2020-07-08 17:49:15 +08:00
    写一个 pipeline 的基类,在 open_spider 的时候连接,close_spider 的时候关闭
    a65420321a
        2
    a65420321a  
    OP
       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
        3
    996635  
       2020-07-08 19:47:03 +08:00
    建议楼主看一下 scrapy 自带的几个 extensions 例子, 可能会有一些灵感. 印象中 scrapy 准备的几个钩子可以做这个事情.
    另外,如果想全局复用连接池, 要考虑线程安全的问题.
    spider 之间传递上下文靠 meta.
    pipeline 里可以获取 spider 的对象.
    a65420321a
        4
    a65420321a  
    OP
       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
        5
    Kobayashi  
       2020-07-09 12:58:22 +08:00 via Android
    @zdnyp 搭车问一下。我目前也是基类,每个 pipeline 绑定一个 Redis connection 。不过不太明白 itempipeline 工作原理,itempipeline 支持并行还是并发,pipeline 上单一 Redis 连接后用吗,要必要开连接池?

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

    scrapy 文档里 architecture 解释的太简单了。
    Kobayashi
        7
    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
        8
    a65420321a  
    OP
       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
        9
    Kobayashi  
       2020-07-09 21:44:09 +08:00
    @a65420321a 按照我定义 RedisPipeline 类的方法再定义一个 ProxyPipeline 基类。自己创建的 MyPipeline 同时继承这 2 个类即可。我自己使用方式是在 Pipeline 中同时使用 redis 和 database 连接。
    a65420321a
        10
    a65420321a  
    OP
       2020-07-10 10:38:37 +08:00
    @Kobayashi
    。。。pipeline 里面可以给 request 加代理吗?
    Kobayashi
        11
    Kobayashi  
       2020-07-11 01:14:38 +08:00 via Android
    @a65420321a pipeline 只能拿到 Item,也就是你从结果中拿到的数据。加代理要在请求被 downloader 处理前,自定义 downloader middleware 即可。

    兄弟,翻翻教程和文档好吗?教程的话推荐《 Python 3 爬虫开发实战》的最后几张 scrapy 讲解,介绍了 scrapy 架构。重要的是带了很多可以生产直接使用的实例。之后读完文档就可以随意用了。
    a65420321a
        12
    a65420321a  
    OP
       2020-07-13 09:54:38 +08:00
    @Kobayashi
    看过的~你们说 pipeline 让我以为 middleware 可以调 pipeline 来着。。。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1988 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 16:16 · PVG 00:16 · LAX 08:16 · JFK 11:16
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.