【问题标题】:How to access pipeline database pool in scrapy spider如何在scrapy spider中访问管道数据库池
【发布时间】:2016-05-02 21:58:23
【问题描述】:

首先,这是我想要做的:

我有一个 XMLFeedSpider,它遍历 XML 文件中的产品列表(节点)并创建保存到管道中我的数据库的项目。第一次看到产品时,我需要创建请求以在产品的 url 字段上进行一些抓取以获取图像等。在后续读取提要时,如果我看到相同的产品,我不想浪费时间/资源这样做,只是想跳过这些额外的请求。要查看要跳过的产品,我需要访问我的数据库以查看该产品是否存在。

以下是我能想到的多种方法:

  1. 只需为蜘蛛中的每个产品创建一个数据库请求。这个 似乎是个坏主意。
  2. 在我的项目存储管道中,我已经创建了一个数据库池,如下所示: dbpool = adbapi.ConnectionPool('psycopg2', cp_max=2, cp_min=1, **dbargs) 并且使用它似乎更有效,所以我不会经常创建新的数据库连接。我不知道如何在我的蜘蛛中访问实例化的管道类(这可能更像是一个一般的 python 问题)。
    注意:这个人基本上是在问同样的问题,但并没有真正得到他想要的答案。 How to get the pipeline object in Scrapy spider
  3. 也许在开始抓取之前将所有产品 URL 加载到内存中,以便在处理产品时进行比较?哪里是这样做的好地方?
  4. 其他建议?

更新:这是我使用数据库池的管道

class PostgresStorePipeline(object):
    """A pipeline to store the item in a MySQL database.
    This implementation uses Twisted's asynchronous database API.
    """

    def __init__(self, dbpool):
        print "Opening connection pool..."
        dispatcher.connect(self.spider_closed, signals.spider_closed)
        self.dbpool = dbpool

    @classmethod
    def from_settings(cls, settings):
        dbargs = dict(
            host=settings['MYSQL_HOST'],
            database=settings['MYSQL_DBNAME'],
            user=settings['MYSQL_USER'],
            password=settings['MYSQL_PASSWD'],
            #charset='utf8',
            #use_unicode=True,
        )
        dbpool = adbapi.ConnectionPool('psycopg2', cp_max=2, cp_min=1, **dbargs)
        return cls(dbpool)

【问题讨论】:

  • 你的意思是 url 对吗?
  • 不是网址,我想正确的术语是 xml 的“节点”。我可以看到说项目是多么令人困惑。有一个 xml 文件,其中有许多节点,我将每个节点都保存为一个项目。我将更新问题以使其更清楚
  • 是的,但你想避免这些节点内的 url 对吗?在以后的工作中对同一个 url 做多个请求
  • 是的,我想避免节点中的 url,我已经在之前的抓取中将节点保存到数据库中。

标签: python scrapy scrapy-spider


【解决方案1】:

我认为您的意思是 URLitem,请记住,对于 scrapyitem 是数据输出,pipeline 是处理这些输出项的机制。

当然,您不需要打开许多连接来进行数据库查询,但您必须进行必要的查询。这取决于您的数据库中有多少条记录只能进行一次查询或每个URL 进行一次查询,您应该测试哪一条更适合您的情况。

我建议您设置自己的DUPEFILTER_CLASS,例如:

from scrapy.dupefilters import RFPDupeFilter

class DBDupeFilter(RFPDupeFilter):

    def __init__(self, *args, **kwargs):
        # self.cursor = .....                       # instantiate your cursor
        super(DBDupeFilter, self).__init__(*args, **kwargs)

    def request_seen(self, request):
        if self.cursor.execute("myquery"):          # if exists
            return True
        else:
            return super(DBDupeFilter, self).request_seen(request)

    def close(self, reason):
        self.cursor.close()                         # close  your cursor
        super(DBDupeFilter, self).close(reason)

更新

这里的问题是DUPEFILTER_CLASS 没有在其request_seen 对象上提供蜘蛛,甚至没有进入构造函数,所以我认为你最好的方法是使用Downloader Middleware,你可以在其中提高IgnoreRequest 异常。

  1. 在spider上实例化db连接,可以在spider本身(构造函数)上实现,也可以通过Middleware或Pipeline上的信号添加,我们在Middleware上添加:

    from scrapy.exceptions import IgnoreRequest
    
    class DBMiddleware(object):
    
        def __init__(self):
            pass
    
        @classmethod
        def from_crawler(cls, crawler):
            o = cls()
            crawler.signals.connect(o.spider_opened, signal=signals.spider_opened)
            return o
    
        def spider_opened(self, spider):
            spider.dbpool = adbapi.ConnectionPool('psycopg2', cp_max=2, cp_min=1, **dbargs)
    
        def process_request(self, request, spider):
            if spider.dbpool... # check if request.url inside the database
                raise IgnoreRequest()
    
  2. 1234563使用spider.dbpool 检查您的数据库连接。
  3. 记得activate your middleware

这样你应该只在蜘蛛对象中做一个数据库连接实例。

【讨论】:

  • 虽然这可能是实现我的最终目标的另一种方式,但除了我已经在管道中建立的连接池之外,我仍然会创建一个到我的数据库的附加连接。我的问题更多是关于如何连接到现有的池,无论是在我的蜘蛛中,还是像这样的 dup 过滤器
  • 我添加了管道代码的相关位。我相信scrapy有一个项目管道经理我只是不确定如何访问它,或者这是否有意义
  • 好的,这很有趣,请检查更新的答案。
  • 我认为你的更新是一个很好的选择,唯一的事情是如果我从管道中删除池实例化,那么我的其他蜘蛛通过 scrapyd 同时运行将无法共享全局管道上的数据库池?还是我错了,通过scrapyd安排的每个蜘蛛都会实例化它的数据库管道?
  • 因为管道不知道请求,它只处理每个输出 Scrapy item。蜘蛛可以访问管道,在这种情况下是不能访问管道的中间件,所以最好通过蜘蛛共享对象,因为两者都可以访问。
猜你喜欢
  • 1970-01-01
  • 2015-10-15
  • 2012-12-14
  • 2013-07-11
  • 2013-04-09
  • 1970-01-01
  • 2019-05-01
  • 2016-05-09
  • 2020-10-03
相关资源
最近更新 更多