.. _spider_task_building:

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

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


.. _spider_task_generator:

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`.
