Очистка форума Scrapy, стратегии синхронизации между конвейером статей и обработчиками запросов

Отказ от ответственности На этот вопрос сложно ответить напрямую, и вам нужно иметь хорошее понимание scrapy и последовательности программ, чтобы ответить на него. Мне трудно свернуть вопрос в нечто более простое, чтобы ответить прямо.

AFAIK нельзя вернуть запросы от обработчиков конвейера элементов. Я пытаюсь разобрать все сообщения в определенной категории с форума. Моя стратегия обхода форума следующая:

  1. Создайте список всех страниц в категории и отправьте их загрузчику для загрузки.
  2. Получить все темы на каждой странице и отправить их в конвейер.
  3. Подождите, пока все элементы страницы будут обработаны (вставлены в реляционную базу данных), а затем начните обход каждой темы.

У меня возникают проблемы с выяснением порядка последовательности шагов 3. Я использую следующие два объекта (перечислены в конце), чтобы помочь в логике секвенирования. category::process_page это обработчик запросов, используемый при обходе тематических страниц.

В классе категории:

Конец фазы 1 представляет все тематические страницы, которые были получены. Конец фазы 2 означает, что конвейер предмета прошел основную работу по всем темам.

Класс тем, который представляет все темы на конкретной странице списка тем, конец фазы 1 означает, что все темы на странице были отправлены в базу данных. Как только каждая тема на странице была добавлена ​​в базу данных, страница удаляется из категории - и как только все страницы сделаны, сканер должен перейти к загрузке всех тем.

Итак, как мне заблокировать загрузчик, чтобы он мог ждать завершения фазы 2 категории, используя логику, которая запускается в конвейере элементов? Есть ли в скрапе какой-то механизм для этого? возможно я могу перезапустить логику загрузчика из конвейера элементов?

Вероятно, существует множество способов сделать это, но я новичок в Python и системный программист C++ / C.

Примечание. Мой первоначальный дизайн должен был быть сделан из 3-4 различных пауков. Один возвращает заголовок форума, второй - все темы, третий - все сообщения, а четвертый - темы, которые необходимо обновить. Но, безусловно, должно быть более естественное решение этой проблемы, я хотел бы сложить последние 3 паука в один.

Я бы принял ответ, что ложка питает логику для запуска пауков, не прибегая к bash (было бы неплохо иметь возможность изгонять пауков из графического интерфейса), тогда я мог бы создать программу драйвера и придерживаться своего первоначального дизайна.

###############################################################################
class TopicPageItemBundle:
  def __init__(self,topic_page_url,category_item_bundle):
    self.url = topic_page_url
    self.topics = set()
    self.topics_phase1 = set()
    self.category_item_bundle = category_item_bundle

  def add_topic(self, topic_url):
    self.topics.add(topic_url)
    self.topics_phase1.add(topic_url)

  def topic_phase1_done(self, topic_url):
    self.topics.remove(topic_url)
    if len(self.topics_phase1) == 0:
      return true
    else:
      return false
###############################################################################
class CategoryItemBundle:
  def __init__(self,forum_id):
    self.topic_pages = {}
    self.topic_pages_phase1 = set()
    self.forum_id = forum_id

  def add_topic_page(self,topic_page_url):
    tpib = TopicPageItemBundle(topic_page_url,self)
    self.topic_pages[topic_page_url] = tpib 
    self.topic_pages_phase1.add(topic_page_url)
    self.topic_pages_phase2.add(topic_page_url)

  def process_page(self, response):
    return_items = []
    tp = TopicPage(response,self)
    pagenav = tp.nav()
    log.msg("received " + pagenav.make_nav_info(), log.INFO)

    page_bundle = self.topic_pages[response.url]

    posts = tp.extract_posts(self.forum_id)
    for post in posts:
      if post != None:
        page_bundle.add_topic(post["url"])
        post["page_topic_bundle"] = page_bundle
    return return_items

  # phase 1 represents finishing the retrieval of all topic pages in a forum
  def topic_page_phase1_done(self, topic_page_url):
    self.topic_pages_phase1.remove(topic_page_url)
    if len(self.topic_pages_phase1) == 0:
      return true
    else:
      return false

  def topic_page_phase2_done(self,topic_page_url)
    self.topic_pages_phase2.remove(topic_page_url)
    if len(self.topic_pages_phase2) == 0:
      return true
    else:
      return true
###############################################################################

2 ответа

Решение

Есть ли причина, по которой вы хотите начать очищать каждую тему только после того, как получите список всех из них и сохраните их в БД?

Потому что мой блокнотический поток обычно таков: получить страницу со списком тем; найти ссылки для каждой темы и получить запрос для каждой с обратным вызовом для очистки темы; найти ссылку на следующую страницу списка и получить обратный вызов для того же обратного вызова; и так далее.

AFAIK, если из обратного вызова вы сначала получите элемент темы, а затем запрос, ваш конвейер будет немедленно выполнен с данным элементом, поскольку в scrapy все происходит синхронно, и только ресурсы загружаются асинхронно с использованием витой.

Одним из подходов к решению проблемы является объединение кода в конвейере элементов для обработки темы в обработчике запросов. По сути, избавление от разрыва между конвейером элементов и загрузчиком, и таким образом у вас есть только одна фаза, фаза, которая означает, что все темы были вставлены в базу данных без синхронизации.

Это, однако, кажется немного неестественным, так как вы обходите "то, как все должно быть сделано" в скрапе. Вы можете продолжить использовать конвейер элементов, когда начнете очищать тему для сообщений, поскольку это не требует синхронизации.

Другие вопросы по тегам