2016-05-02 4 views
0

Во-первых, вот что я пытаюсь сделать:Как получить доступ к трубопроводной пула базы данных в Scrapy паук

У меня есть 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. Другое предложение?

Update: это мой трубопровод с дб бассейном

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) 
+0

для предметов, которые вы имеете в виду подписи? – eLRuLL

+0

не URL-адреса, я думаю, правильная терминология будет «узлами» xml. я вижу, как говорящие вещи сбивают с толку. есть один xml-файл и в нем много узлов, каждый из которых я сохраняю как элемент. Я обновлю вопрос, чтобы сделать это более ясным: – jeffjv

+0

да, но вы хотите избежать ссылок на эти узлы правильно? делать несколько запросов на один и тот же URL-адрес для будущих заданий. – eLRuLL

ответ

1

Я думаю, что вы имеете в виду URL по item, помните, что для scrapyitem является вывод данных, а pipeline является механизмом для решения эти выходные элементы.

Конечно, вам не нужно открывать множество подключений для выполнения ваших запросов db, но вам нужно будет выполнить необходимые запросы. Это зависит от того, сколько записей у вас есть в вашей базе данных, чтобы сделать только один запрос или один за 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) 

UPDATE

Проблема здесь состоит в том, что DUPEFILTER_CLASS не предлагает паука на его request_seen объекта или даже получить в конструкторе, поэтому я думаю, что ваш лучший снимок с Downloader Middleware, где вы можете сделать исключение IgnoreRequest.

  1. Instantiate соединение дб на пауке, вы можете сделать это на самом (конструктор) паук, или вы также можете добавить его через сигнал на Middleware или трубопроводе, мы добавим его на 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. Теперь на вашем Pipeline, удалите экземпляр dbpool и получить его от spider аргумента при необходимости, помните, чем process_item получает деталь и паук в качестве аргумента, так что вы должны быть в состоянии использовать spider.dbpool чтобы проверить ваше соединение db.

  3. запомнить activate your middleware.

Таким образом, вы должны делать только один экземпляр соединения db внутри объекта паука.

+0

Хотя это может быть другим способом достижения моей конечной цели, я все равно создаю дополнительное подключение к моей базе данных, кроме пула соединений, который я уже сделал в конвейере. Мой вопрос о том, как подключиться к существующему пулу, будь то мой паук, дублирующий фильтр, подобный этому или иначе – jeffjv

+0

Я добавил соответствующие биты кода моего конвейера. Я считаю, что у scrapy есть менеджер для конвейеров элементов. Я просто не уверен, как получить к нему доступ, или если это даже имеет смысл. – jeffjv

+0

ОК, это просто интересно, пожалуйста, проверьте обновленный ответ. – eLRuLL