scrapy/docs/topics/coroutines.rst

8.3 KiB

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> </head>

Coroutines

System Message: ERROR/3 (<stdin>, line 7)

Unknown directive type "versionadded".

.. versionadded:: 2.0

Scrapy has :ref:`partial support <coroutine-support>` for the :ref:`coroutine syntax <async>`.

System Message: ERROR/3 (<stdin>, line 9); backlink

Unknown interpreted text role "ref".

System Message: ERROR/3 (<stdin>, line 9); backlink

Unknown interpreted text role "ref".

Supported callables

The following callables may be defined as coroutines using async def, and hence use coroutine syntax (e.g. await, async for, async with):

General usage

There are several use cases for coroutines in Scrapy.

Code that would return Deferreds when written for previous Scrapy versions, such as downloader middlewares and signal handlers, can be rewritten to be shorter and cleaner:

System Message: WARNING/2 (<stdin>, line 63)

Cannot analyze code. Pygments package not found.

.. code-block:: python

    from itemadapter import ItemAdapter


    class DbPipeline:
        def _update_item(self, data, item):
            adapter = ItemAdapter(item)
            adapter["field"] = data
            return item

        def process_item(self, item, spider):
            adapter = ItemAdapter(item)
            dfd = db.get_some_data(adapter["id"])
            dfd.addCallback(self._update_item, item)
            return dfd

becomes:

System Message: WARNING/2 (<stdin>, line 82)

Cannot analyze code. Pygments package not found.

.. code-block:: python

    from itemadapter import ItemAdapter


    class DbPipeline:
        async def process_item(self, item, spider):
            adapter = ItemAdapter(item)
            adapter["field"] = await db.get_some_data(adapter["id"])
            return item

Coroutines may be used to call asynchronous code. This includes other coroutines, functions that return Deferreds and functions that return :term:`awaitable objects <awaitable>` such as :class:`~asyncio.Future`. This means you can use many useful Python libraries providing such code:

System Message: ERROR/3 (<stdin>, line 93); backlink

Unknown interpreted text role "term".

System Message: ERROR/3 (<stdin>, line 93); backlink

Unknown interpreted text role "class".

System Message: WARNING/2 (<stdin>, line 98)

Cannot analyze code. Pygments package not found.

.. code-block:: python

    class MySpiderDeferred(Spider):
        # ...
        async def parse(self, response):
            additional_response = await treq.get("https://additional.url")
            additional_data = await treq.content(additional_response)
            # ... use response and additional_data to yield items and requests


    class MySpiderAsyncio(Spider):
        # ...
        async def parse(self, response):
            async with aiohttp.ClientSession() as session:
                async with session.get("https://additional.url") as additional_response:
                    additional_data = await additional_response.text()
            # ... use response and additional_data to yield items and requests

Note

Many libraries that use coroutines, such as aio-libs, require the :mod:`asyncio` loop and to use them you need to :doc:`enable asyncio support in Scrapy<asyncio>`.

System Message: ERROR/3 (<stdin>, line 116); backlink

Unknown interpreted text role "mod".

System Message: ERROR/3 (<stdin>, line 116); backlink

Unknown interpreted text role "doc".

Note

If you want to await on Deferreds while using the asyncio reactor, you need to :ref:`wrap them<asyncio-await-dfd>`.

System Message: ERROR/3 (<stdin>, line 120); backlink

Unknown interpreted text role "ref".

Common use cases for asynchronous code include:

Mixing synchronous and asynchronous spider middlewares

System Message: ERROR/3 (<stdin>, line 141)

Unknown directive type "versionadded".

.. versionadded:: 2.7

The output of a :class:`~scrapy.Request` callback is passed as the result parameter to the :meth:`~scrapy.spidermiddlewares.SpiderMiddleware.process_spider_output` method of the first :ref:`spider middleware <topics-spider-middleware>` from the :ref:`list of active spider middlewares <topics-spider-middleware-setting>`. Then the output of that process_spider_output method is passed to the process_spider_output method of the next spider middleware, and so on for every active spider middleware.

System Message: ERROR/3 (<stdin>, line 143); backlink

Unknown interpreted text role "class".

System Message: ERROR/3 (<stdin>, line 143); backlink

Unknown interpreted text role "meth".

System Message: ERROR/3 (<stdin>, line 143); backlink

Unknown interpreted text role "ref".

System Message: ERROR/3 (<stdin>, line 143); backlink

Unknown interpreted text role "ref".

Scrapy supports mixing :ref:`coroutine methods <async>` and synchronous methods in this chain of calls.

System Message: ERROR/3 (<stdin>, line 152); backlink

Unknown interpreted text role "ref".

However, if any of the process_spider_output methods is defined as a synchronous method, and the previous Request callback or process_spider_output method is a coroutine, there are some drawbacks to the asynchronous-to-synchronous conversion that Scrapy does so that the synchronous process_spider_output method gets a synchronous iterable as its result parameter:

  • The whole output of the previous Request callback or process_spider_output method is awaited at this point.

  • If an exception raises while awaiting the output of the previous Request callback or process_spider_output method, none of that output will be processed.

    This contrasts with the regular behavior, where all items yielded before an exception raises are processed.

Asynchronous-to-synchronous conversions are supported for backward compatibility, but they are deprecated and will stop working in a future version of Scrapy.

To avoid asynchronous-to-synchronous conversions, when defining Request callbacks as coroutine methods or when using spider middlewares whose process_spider_output method is an :term:`asynchronous generator`, all active spider middlewares must either have their process_spider_output method defined as an asynchronous generator or :ref:`define a process_spider_output_async method <universal-spider-middleware>`.

System Message: ERROR/3 (<stdin>, line 176); backlink

Unknown interpreted text role "term".

System Message: ERROR/3 (<stdin>, line 176); backlink

Unknown interpreted text role "ref".

Note

When using third-party spider middlewares that only define a synchronous process_spider_output method, consider :ref:`making them universal <universal-spider-middleware>` through :ref:`subclassing <tut-inheritance>`.

System Message: ERROR/3 (<stdin>, line 183); backlink

Unknown interpreted text role "ref".

System Message: ERROR/3 (<stdin>, line 183); backlink

Unknown interpreted text role "ref".

Universal spider middlewares

System Message: ERROR/3 (<stdin>, line 194)

Unknown directive type "versionadded".

.. versionadded:: 2.7

To allow writing a spider middleware that supports asynchronous execution of its process_spider_output method in Scrapy 2.7 and later (avoiding :ref:`asynchronous-to-synchronous conversions <sync-async-spider-middleware>`) while maintaining support for older Scrapy versions, you may define process_spider_output as a synchronous method and define an :term:`asynchronous generator` version of that method with an alternative name: process_spider_output_async.

System Message: ERROR/3 (<stdin>, line 196); backlink

Unknown interpreted text role "ref".

System Message: ERROR/3 (<stdin>, line 196); backlink

Unknown interpreted text role "term".

For example:

System Message: WARNING/2 (<stdin>, line 206)

Cannot analyze code. Pygments package not found.

.. code-block:: python

    class UniversalSpiderMiddleware:
        def process_spider_output(self, response, result, spider):
            for r in result:
                # ... do something with r
                yield r

        async def process_spider_output_async(self, response, result, spider):
            async for r in result:
                # ... do something with r
                yield r

Note

This is an interim measure to allow, for a time, to write code that works in Scrapy 2.7 and later without requiring asynchronous-to-synchronous conversions, and works in earlier Scrapy versions as well.

In some future version of Scrapy, however, this feature will be deprecated and, eventually, in a later version of Scrapy, this feature will be removed, and all spider middlewares will be expected to define their process_spider_output method as an asynchronous generator.

</html>