Способы создания заданий

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

Например, если мы создадим задание с именем “contact_page”, то мы должны будем объявить в нашем классе паука метод c именем “task_contact_page”:

    ...
    self.add_task(Task('contact_page', url='http://domain.com/contact.html'))
    ...

def task_contact_page(self, grab, task):
    ...

Имя функции-обработчика определяется так: берётся имя задания и добавляется префикс “task_”.

Рассмотрим различные способы создания Task-заданий.

initial_urls

В атрибуте паука initial_urls Можно указать список адресов, с обработки которых паук должен начать свою работу:

class ExampleSpider(Spider):
    initial_urls = ['http://google.com/', 'http://yahoo.com/']

Для всех адресов, перечисленных в initial_urls будет создано задание с именем ‘initial’. Это самый простой способ создания заданий, вы не можете управлять ничем кроме адресов запрашиваемых документов.

task_generator

Более сложный способ создания начальных заданий. Метод с именем task_generator должен являться python-генератором т.е. функцией выдающей множество значений с помощью инструкции yield. Spider будет обращаться к новым задания из task_generator каждый раз, когда его очередь будет опустошаться. Это позволяет не опасаться того, что вы создадите слишком много заданий. Выглядит это так: в начале работы паук извлекает некоторое количество заданий с помощью task_generator и помещает их в очередь, далее он выполняет запросы и следит за количеством заданий в очереди. Как только их становится слишком мало, паук обращется ещё раз к task_generator и добавляет новые задания.

К примеру, вы можете открыть файл с миллионом записей и последовательно читать строки из него, создавая всё новые и новые задания:

class ExampleSpider(Spider):
    def task_generator(self):
        for line in open('var/urls.txt'):
            yield Task('download', url=line.strip())

add_task

Независимо от того, каким способом вы создали новое задание, в очередь заданий оно попадёт с помощью метода add_task. В случае использования intial_urls или task_generator метод add_task будет вызван неявно, но вы, конечно, можете использовать его напрямую, чтобы добавить новое задание в любом месте выполнения программы. Это можно делать даже до начала работы паука. Например:

bot = ExampleSpider()
bot.add_task('google', url='http://google.com')
bot.run()

yield

Инструкцию yield для создания заданий вы можете использовать в двух местах, во-первых, в методе task_generator, о чём уже писалось выше, во-вторых, в любой функции-обработчике результата. При вызове функции-обработчика Spider ловит все задания, которые она генерирует и складывает в очередь заданий. Создание заданий с помощью yield ничем не отличается от использования метода add_task, разве что запись получается более короткая:

class ExampleSpider(Spider):
    initial_urls = ['http://google.com']

    def task_initial(self, grab, task):
        # Google page was fetched
        # Now let's download yahoo page
        yield Task('yahoo', url='yahoo.com')

    def task_yahoo(self, grab, task):
        pass

Резюме

Для задания начальных заданий используйте атрибут initial_urls, если вам нужна более сложная логика создания начальных заданий, используйте метод task_generator. Для создания заданий внутри функций-обработчиков используйте инструкцию yield. Использовать метод add_task напрямую вам практически никога не понадобится.

Есть также ряд методов для типичных случаев генерации новых заданий: обработка пагинации, обработка списка ссылок. Смотрите модуль grab.spider.pattern.