Address typos and grammar issues in docs/

This commit is contained in:
Adrián Chaves 2025-10-23 11:52:57 +02:00
parent 2d073a9c0d
commit fe057bece0
49 changed files with 826 additions and 807 deletions

View File

@ -12,7 +12,7 @@ The GitHub issue tracker's purpose is to deal with bug reports and feature reque
Keep in mind that by filing an issue, you are expected to comply with Scrapy's Code of Conduct, including treating everyone with respect: https://github.com/scrapy/scrapy/blob/master/CODE_OF_CONDUCT.md
The following is a suggested template to structure your issue, you can find more guidelines at https://doc.scrapy.org/en/latest/contributing.html#reporting-bugs
The following is a suggested template to structure your issue, you can find more guidelines at https://docs.scrapy.org/en/latest/contributing.html#reporting-bugs
-->

View File

@ -12,7 +12,7 @@ The GitHub issue tracker's purpose is to deal with bug reports and feature reque
Keep in mind that by filing an issue, you are expected to comply with Scrapy's Code of Conduct, including treating everyone with respect: https://github.com/scrapy/scrapy/blob/master/CODE_OF_CONDUCT.md
The following is a suggested template to structure your pull request, you can find more guidelines at https://doc.scrapy.org/en/latest/contributing.html#writing-patches and https://doc.scrapy.org/en/latest/contributing.html#submitting-patches
The following is a suggested template to structure your pull request, you can find more guidelines at https://docs.scrapy.org/en/latest/contributing.html#writing-patches and https://docs.scrapy.org/en/latest/contributing.html#submitting-patches
-->

View File

@ -7,13 +7,11 @@ Scrapy documentation quick start guide
This file provides a quick guide on how to compile the Scrapy documentation.
Setup the environment
---------------------
Set up the environment
----------------------
To compile the documentation you need Sphinx Python library. To install it
and all its dependencies run the following command from this dir
::
To compile the documentation you need the Sphinx Python library. To install it
and all its dependencies, run the following command from this directory::
pip install -r requirements.txt
@ -21,8 +19,8 @@ and all its dependencies run the following command from this dir
Compile the documentation
-------------------------
To compile the documentation (to classic HTML output) run the following command
from this dir::
To compile the documentation (to classic HTML output), run the following
command from this directory::
make html
@ -32,7 +30,7 @@ Documentation will be generated (in HTML format) inside the ``build/html`` dir.
View the documentation
----------------------
To view the documentation run the following command::
To view the documentation, run the following command::
make htmlview
@ -43,7 +41,7 @@ This command will fire up your default browser and open the main page of your
Start over
----------
To clean up all generated documentation files and start from scratch run::
To clean up all generated documentation files and start from scratch, run::
make clean
@ -53,15 +51,15 @@ Keep in mind that this command won't touch any documentation source files.
Recreating documentation on the fly
-----------------------------------
There is a way to recreate the doc automatically when you make changes, you
need to install watchdog (``pip install watchdog``) and then use::
You can recreate the documentation automatically when you make changes. Install
watchdog (``pip install watchdog``) and then run::
make watch
Alternative method using tox
----------------------------
To compile the documentation to HTML run the following command::
To compile the documentation to HTML, run the following command::
tox -e docs

View File

@ -6,7 +6,7 @@ Contributing to Scrapy
.. important::
Double check that you are reading the most recent version of this document
Double-check that you are reading the most recent version of this document
at https://docs.scrapy.org/en/master/contributing.html
By participating in this project you agree to abide by the terms of our
@ -216,14 +216,13 @@ has been validated and proven useful. Alternatively, you can start a
conversation in the `Scrapy subreddit`_ to discuss your idea first.
Sometimes there is an existing pull request for the problem you'd like to
solve, which is stalled for some reason. Often the pull request is in a
right direction, but changes are requested by Scrapy maintainers, and the
original pull request author hasn't had time to address them.
In this case consider picking up this pull request: open
a new pull request with all commits from the original pull request, as well as
additional changes to address the raised issues. Doing so helps a lot; it is
not considered rude as long as the original author is acknowledged by keeping
his/her commits.
solve, which is stalled for some reason. Often the pull request is in the right
direction, but changes are requested by Scrapy maintainers, and the original
pull request author hasn't had time to address them. In this case consider
picking up this pull request: open a new pull request with all commits from the
original pull request, as well as additional changes to address the raised
issues. Doing so helps a lot; it is not considered rude as long as the original
author is acknowledged by keeping their commits.
You can pull an existing pull request to a local branch
by running ``git fetch upstream pull/$PR_NUMBER/head:$BRANCH_NAME_TO_CREATE``
@ -233,9 +232,9 @@ with a name of the branch you want to create locally).
See also: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/checking-out-pull-requests-locally#modifying-an-inactive-pull-request-locally.
When writing GitHub pull requests, try to keep titles short but descriptive.
E.g. For bug #411: "Scrapy hangs if an exception raises in start_requests"
prefer "Fix hanging when exception occurs in start_requests (#411)"
instead of "Fix for #411". Complete titles make it easy to skim through
For example, for bug #411: "Scrapy hangs if an exception raises in
start_requests" prefer "Fix hanging when exception occurs in start_requests
(#411)" instead of "Fix for #411". Complete titles make it easy to skim through
the issue tracker.
Finally, try to keep aesthetic changes (:pep:`8` compliance, unused imports
@ -271,7 +270,7 @@ commit.
.. _pre-commit: https://pre-commit.com/
After your create a local clone of your fork of the Scrapy repository:
After you create a local clone of your fork of the Scrapy repository:
#. `Install pre-commit <https://pre-commit.com/#installation>`_.

View File

@ -110,7 +110,7 @@ My Scrapy crawler has memory leaks. What can I do?
See :ref:`topics-leaks`.
Also, Python has a builtin memory leak issue which is described in
Also, Python has a built-in memory leak issue which is described in
:ref:`topics-leaks-without-leaks`.
How can I make Scrapy consume less memory?
@ -128,8 +128,8 @@ middleware with a :ref:`custom downloader middleware
<topics-downloader-middleware-custom>` that requires less memory. For example:
- If your domain names are similar enough, use your own regular expression
instead joining the strings in :attr:`~scrapy.Spider.allowed_domains` into
a complex regular expression.
instead of joining the strings in :attr:`~scrapy.Spider.allowed_domains`
into a complex regular expression.
- If you can meet the installation requirements, use pyre2_ instead of
Pythons re_ to compile your URL-filtering regular expression. See
@ -274,8 +274,8 @@ Parsing big feeds with XPath selectors can be problematic since they need to
build the DOM of the entire feed in memory, and this can be quite slow and
consume a lot of memory.
In order to avoid parsing all the entire feed at once in memory, you can use
the :func:`~scrapy.utils.iterators.xmliter_lxml` and
In order to avoid parsing the entire feed at once in memory, you can use the
:func:`~scrapy.utils.iterators.xmliter_lxml` and
:func:`~scrapy.utils.iterators.csviter` functions. In fact, this is what
:class:`~scrapy.spiders.XMLFeedSpider` uses.
@ -311,8 +311,8 @@ Should I use spider arguments or settings to configure my spider?
-----------------------------------------------------------------
Both :ref:`spider arguments <spiderargs>` and :ref:`settings <topics-settings>`
can be used to configure your spider. There is no strict rule that mandates to
use one or the other, but settings are more suited for parameters that, once
can be used to configure your spider. There is no strict rule that mandates
using one or the other, but settings are more suited for parameters that, once
set, don't change much, while spider arguments are meant to change more often,
even on each spider run and sometimes are required for the spider to run at all
(for example, to set the start url of a spider).

View File

@ -21,16 +21,16 @@ Having trouble? We'd like to help!
* Try the :doc:`FAQ <faq>` -- it's got answers to some common questions.
* Looking for specific information? Try the :ref:`genindex` or :ref:`modindex`.
* Ask or search questions in `StackOverflow using the scrapy tag`_.
* Ask or search questions on `Stack Overflow using the scrapy tag`_.
* Ask or search questions in the `Scrapy subreddit`_.
* Search for questions on the archives of the `scrapy-users mailing list`_.
* Ask a question in the `#scrapy IRC channel`_,
* Ask a question in the `#scrapy IRC channel`_.
* Report bugs with Scrapy in our `issue tracker`_.
* Join the Discord community `Scrapy Discord`_.
.. _scrapy-users mailing list: https://groups.google.com/forum/#!forum/scrapy-users
.. _Scrapy subreddit: https://www.reddit.com/r/scrapy/
.. _StackOverflow using the scrapy tag: https://stackoverflow.com/tags/scrapy
.. _Stack Overflow using the scrapy tag: https://stackoverflow.com/tags/scrapy
.. _#scrapy IRC channel: irc://irc.freenode.net/scrapy
.. _issue tracker: https://github.com/scrapy/scrapy/issues
.. _Scrapy Discord: https://discord.com/invite/mv3yErfpvq
@ -132,7 +132,7 @@ Built-in services
topics/telnetconsole
:doc:`topics/logging`
Learn how to use Python's built-in logging on Scrapy.
Learn how to use Python's built-in logging in Scrapy.
:doc:`topics/stats`
Collect statistics about your scraping crawler.
@ -180,7 +180,7 @@ Solving specific problems
Get familiar with some Scrapy common practices.
:doc:`topics/broad-crawls`
Tune Scrapy for crawling a lot domains in parallel.
Tune Scrapy for crawling a lot of domains in parallel.
:doc:`topics/developer-tools`
Learn how to scrape with your browser's developer tools.
@ -195,7 +195,7 @@ Solving specific problems
Download files and/or images associated with your scraped items.
:doc:`topics/deploy`
Deploying your Scrapy spiders and run them in a remote server.
Deploy your Scrapy spiders and run them on a remote server.
:doc:`topics/autothrottle`
Adjust crawl rate dynamically based on load.
@ -246,7 +246,7 @@ Extending Scrapy
Customize the input and output of your spiders.
:doc:`topics/extensions`
Extend Scrapy with your custom functionality
Extend Scrapy with your custom functionality.
:doc:`topics/signals`
See all available signals and how to work with them.
@ -255,14 +255,14 @@ Extending Scrapy
Understand the scheduler component.
:doc:`topics/exporters`
Quickly export your scraped items to a file (XML, CSV, etc).
Quickly export your scraped items to a file (XML, CSV, etc.).
:doc:`topics/components`
Learn the common API and some good practices when building custom Scrapy
components.
:doc:`topics/api`
Use it on extensions and middlewares to extend Scrapy functionality.
Use it in extensions and middlewares to extend Scrapy functionality.
All the rest

View File

@ -5,16 +5,16 @@ Examples
========
The best way to learn is with examples, and Scrapy is no exception. For this
reason, there is an example Scrapy project named quotesbot_, that you can use to
reason, there is an example Scrapy project named quotesbot_ that you can use to
play and learn more about Scrapy. It contains two spiders for
https://quotes.toscrape.com, one using CSS selectors and another one using XPath
expressions.
https://quotes.toscrape.com, one using CSS selectors and another one using
XPath expressions.
The quotesbot_ project is available at: https://github.com/scrapy/quotesbot.
You can find more information about it in the project's README.
If you're familiar with git, you can checkout the code. Otherwise you can
download the project as a zip file by clicking
`here <https://github.com/scrapy/quotesbot/archive/master.zip>`_.
If you're familiar with git, you can check out the code. Otherwise, you can
download the project as a zip file by clicking `here
<https://github.com/scrapy/quotesbot/archive/master.zip>`_.
.. _quotesbot: https://github.com/scrapy/quotesbot

View File

@ -25,15 +25,15 @@ To install Scrapy using ``conda``, run::
conda install -c conda-forge scrapy
Alternatively, if youre already familiar with installation of Python packages,
you can install Scrapy and its dependencies from PyPI with::
Alternatively, if youre already familiar with installing Python packages, you
can install Scrapy and its dependencies from PyPI with::
pip install Scrapy
We strongly recommend that you install Scrapy in :ref:`a dedicated virtualenv <intro-using-virtualenv>`,
to avoid conflicting with your system packages.
Note that sometimes this may require solving compilation issues for some Scrapy
Note that this may sometimes require solving compilation issues for some Scrapy
dependencies depending on your operating system, so be sure to check the
:ref:`intro-install-platform-notes`.
@ -47,7 +47,7 @@ Things that are good to know
Scrapy is written in pure Python and depends on a few key Python packages (among others):
* `lxml`_, an efficient XML and HTML parser
* `parsel`_, an HTML/XML data extraction library written on top of lxml,
* `parsel`_, an HTML/XML data extraction library written on top of lxml
* `w3lib`_, a multi-purpose helper for dealing with URLs and web page encodings
* `twisted`_, an asynchronous networking framework
* `cryptography`_ and `pyOpenSSL`_, to deal with various network-level security needs
@ -73,14 +73,14 @@ Using a virtual environment (recommended)
TL;DR: We recommend installing Scrapy inside a virtual environment
on all platforms.
Python packages can be installed either globally (a.k.a system wide),
or in user-space. We do not recommend installing Scrapy system wide.
Python packages can be installed either globally (a.k.a. system-wide), or in
user-space. We do not recommend installing Scrapy system-wide.
Instead, we recommend that you install Scrapy within a so-called
"virtual environment" (:mod:`venv`).
Virtual environments allow you to not conflict with already-installed Python
system packages (which could break some of your system tools and scripts),
and still install packages normally with ``pip`` (without ``sudo`` and the likes).
Instead, we recommend that you install Scrapy within a so-called "virtual
environment" (:mod:`venv`). Virtual environments allow you to avoid conflicts
with already-installed Python system packages (which could break some of your
system tools and scripts), and still install packages normally with ``pip``
(without ``sudo`` or similar tools).
See :ref:`tut-venv` on how to create your virtual environment.
@ -92,7 +92,7 @@ below for non-Python dependencies that you may need to install beforehand).
.. _intro-install-platform-notes:
Platform specific installation notes
Platform-specific installation notes
====================================
.. _intro-install-windows:
@ -120,11 +120,12 @@ To install Scrapy on Windows using ``pip``:
#. Under the Workloads section, select **C++ build tools**.
#. Check the installation details and make sure following packages are selected as optional components:
#. Check the installation details and make sure the following packages are
selected as optional components:
* **MSVC** (e.g MSVC v142 - VS 2019 C++ x64/x86 build tools (v14.23) )
* **MSVC** (e.g. MSVC v142 - VS 2019 C++ x64/x86 build tools (v14.23) )
* **Windows SDK** (e.g Windows 10 SDK (10.0.18362.0))
* **Windows SDK** (e.g. Windows 10 SDK (10.0.18362.0))
#. Install the Visual Studio Build Tools.
@ -140,8 +141,8 @@ twisted and pyOpenSSL, and is compatible with recent Ubuntu distributions.
But it should support older versions of Ubuntu too, like Ubuntu 14.04,
albeit with potential issues with TLS connections.
**Don't** use the ``python-scrapy`` package provided by Ubuntu, they are
typically too old and slow to catch up with the latest Scrapy release.
**Don't** use the ``python-scrapy`` package provided by Ubuntu; it is typically
too old and slow to catch up with the latest Scrapy release.
To install Scrapy on Ubuntu (or Ubuntu-based) systems, you need to install
@ -187,8 +188,8 @@ solutions:
* Install `homebrew`_ following the instructions in https://brew.sh/
* Update your ``PATH`` variable to state that homebrew packages should be
used before system packages (Change ``.bashrc`` to ``.zshrc`` accordingly
if you're using `zsh`_ as default shell)::
used before system packages (change ``.bashrc`` to ``.zshrc`` accordingly
if you're using `zsh`_ as the default shell)::
echo "export PATH=/usr/local/bin:/usr/local/sbin:$PATH" >> ~/.bashrc
@ -196,7 +197,7 @@ solutions:
source ~/.bashrc
* Install python::
* Install Python::
brew install python
@ -206,7 +207,7 @@ solutions:
This method is a workaround for the above macOS issue, but it's an overall
good practice for managing dependencies and can complement the first method.
After any of these workarounds you should be able to install Scrapy::
After any of these workarounds, you should be able to install Scrapy::
pip install Scrapy
@ -218,14 +219,14 @@ We recommend using the latest PyPy version.
For PyPy3, only Linux installation was tested.
Most Scrapy dependencies now have binary wheels for CPython, but not for PyPy.
This means that these dependencies will be built during installation.
On macOS, you are likely to face an issue with building the Cryptography
dependency. The solution to this problem is described
`here <https://github.com/pyca/cryptography/issues/2692#issuecomment-272773481>`_,
This means that these dependencies will be built during installation. On macOS,
you are likely to face an issue with building the Cryptography dependency. The
solution to this problem is described `here
<https://github.com/pyca/cryptography/issues/2692#issuecomment-272773481>`_,
that is to ``brew install openssl`` and then export the flags that this command
recommends (only needed when installing Scrapy). Installing on Linux has no special
issues besides installing build dependencies.
Installing Scrapy with PyPy on Windows is not tested.
recommends (only needed when installing Scrapy). Installing Scrapy on Linux has
no special issues beyond installing the build dependencies. Installing Scrapy
with PyPy on Windows has not been tested.
You can check that Scrapy is installed correctly by running ``scrapy bench``.
If this command gives errors such as

View File

@ -4,13 +4,13 @@
Scrapy at a glance
==================
Scrapy (/ˈskreɪpaɪ/) is an application framework for crawling web sites and extracting
structured data which can be used for a wide range of useful applications, like
data mining, information processing or historical archival.
Scrapy (/ˈskreɪpaɪ/) is an application framework for crawling websites and
extracting structured data which can be used for a wide range of useful
applications, like data mining, information processing or historical archival.
Even though Scrapy was originally designed for `web scraping`_, it can also be
used to extract data using APIs (such as `Amazon Associates Web Services`_) or
as a general purpose web crawler.
as a general-purpose web crawler.
Walk-through of an example spider
@ -19,7 +19,7 @@ Walk-through of an example spider
In order to show you what Scrapy brings to the table, we'll walk you through an
example of a Scrapy Spider using the simplest way to run a spider.
Here's the code for a spider that scrapes famous quotes from website
Here's the code for a spider that scrapes famous quotes from the website
https://quotes.toscrape.com, following the pagination:
.. code-block:: python
@ -49,8 +49,9 @@ and run the spider using the :command:`runspider` command::
scrapy runspider quotes_spider.py -o quotes.jsonl
When this finishes you will have in the ``quotes.jsonl`` file a list of the
quotes in JSON Lines format, containing the text and author, which will look like this::
When this finishes, you will have a list of the quotes in JSON Lines format in
the ``quotes.jsonl`` file, containing the text and author, which will look like
this::
{"author": "Jane Austen", "text": "\u201cThe person, be it gentleman or lady, who has not pleasure in a good novel, must be intolerably stupid.\u201d"}
{"author": "Steve Martin", "text": "\u201cA day without sunshine is like, you know, night.\u201d"}
@ -73,19 +74,19 @@ look for a link to the next page and schedule another request using the same
``parse`` method as callback.
Here you will notice one of the main advantages of Scrapy: requests are
:ref:`scheduled and processed asynchronously <topics-architecture>`. This
means that Scrapy doesn't need to wait for a request to be finished and
processed, it can send another request or do other things in the meantime. This
also means that other requests can keep going even if a request fails or an
error happens while handling it.
:ref:`scheduled and processed asynchronously <topics-architecture>`. This means
that Scrapy doesn't need to wait for a request to be finished and processed; it
can send another request or do other things in the meantime. This also means
that other requests can keep going even if a request fails or an error happens
while handling it.
While this enables you to do very fast crawls (sending multiple concurrent
requests at the same time, in a fault-tolerant way) Scrapy also gives you
control over the politeness of the crawl through :ref:`a few settings
<topics-settings-ref>`. You can do things like setting a download delay between
each request, limiting the amount of concurrent requests per domain or per IP, and
even :ref:`using an auto-throttling extension <topics-autothrottle>` that tries
to figure these settings out automatically.
each request, limiting the number of concurrent requests per domain or per IP,
and even :ref:`using an auto-throttling extension <topics-autothrottle>` that
tries to figure these settings out automatically.
.. note::
@ -116,7 +117,7 @@ scraping easy and efficient, such as:
multiple formats (JSON, CSV, XML) and storing them in multiple backends (FTP,
S3, local filesystem)
* Robust encoding support and auto-detection, for dealing with foreign,
* Robust encoding support and auto-detection for dealing with foreign,
non-standard and broken encoding declarations.
* :ref:`Strong extensibility support <extending-scrapy>`, allowing you to plug
@ -137,10 +138,10 @@ scraping easy and efficient, such as:
console running inside your Scrapy process, to introspect and debug your
crawler
* Plus other goodies like reusable spiders to crawl sites from `Sitemaps`_ and
XML/CSV feeds, a media pipeline for :ref:`automatically downloading images
<topics-media-pipeline>` (or any other media) associated with the scraped
items, a caching DNS resolver, and much more!
* Plus other goodies such as reusable spiders to crawl sites from `Sitemaps`_
and XML/CSV feeds, a media pipeline for :ref:`automatically downloading
images <topics-media-pipeline>` (or any other media) associated with the
scraped items, a caching DNS resolver, and much more!
What's next?
============

View File

@ -15,7 +15,7 @@ This tutorial will walk you through these tasks:
1. Creating a new Scrapy project
2. Writing a :ref:`spider <topics-spiders>` to crawl a site and extract data
3. Exporting the scraped data using the command line
4. Changing spider to recursively follow links
4. Changing the spider to recursively follow links
5. Using spider arguments
Scrapy is written in Python_. The more you learn about Python, the more you
@ -76,10 +76,11 @@ This will create a ``tutorial`` directory with the following contents::
Our first Spider
================
Spiders are classes that you define and that Scrapy uses to scrape information from a website
(or a group of websites). They must subclass :class:`~scrapy.Spider` and define the initial
requests to be made, and optionally, how to follow links in pages and parse the downloaded
page content to extract data.
Spiders are classes that you define and that Scrapy uses to scrape information
from a website (or a group of websites). They must subclass
:class:`~scrapy.Spider` and define the initial requests to be made and,
optionally, how to follow links in pages and parse the downloaded page content
to extract data.
This is the code for our first Spider. Save it in a file named
``quotes_spider.py`` under the ``tutorial/spiders`` directory in your project:
@ -137,9 +138,9 @@ To put our spider to work, go to the project's top level directory and run::
scrapy crawl quotes
This command runs the spider named ``quotes`` that we've just added, that
will send some requests for the ``quotes.toscrape.com`` domain. You will get an output
similar to this::
This command runs the spider named ``quotes`` that we've just added, which will
send some requests for the ``quotes.toscrape.com`` domain. You will get an
output similar to this::
... (omitted for brevity)
2016-12-16 21:24:05 [scrapy.core.engine] INFO: Spider opened
@ -400,8 +401,8 @@ like this:
</div>
</div>
Let's open up scrapy shell and play a bit to find out how to extract the data
we want::
Let's open up the Scrapy shell and play a bit to find out how to extract the
data we want::
scrapy shell 'https://quotes.toscrape.com'
@ -668,14 +669,14 @@ As a shortcut for creating Request objects you can use
if next_page is not None:
yield response.follow(next_page, callback=self.parse)
Unlike scrapy.Request, ``response.follow`` supports relative URLs directly - no
need to call urljoin. Note that ``response.follow`` just returns a Request
instance; you still have to yield this Request.
Unlike :class:`scrapy.Request`, ``response.follow`` supports relative URLs
directly - no need to call urljoin. Note that ``response.follow`` just returns
a Request instance; you still have to yield this Request.
.. skip: start
You can also pass a selector to ``response.follow`` instead of a string;
this selector should extract necessary attributes:
You can also pass a selector to ``response.follow`` instead of a string; this
selector should extract the necessary attributes:
.. code-block:: python
@ -741,7 +742,7 @@ this time for scraping author information:
}
This spider will start from the main page, it will follow all the links to the
authors pages calling the ``parse_author`` callback for each of them, and also
author pages calling the ``parse_author`` callback for each of them, and also
the pagination links with the ``parse`` callback as we saw before.
Here we're passing callbacks to
@ -749,8 +750,8 @@ Here we're passing callbacks to
arguments to make the code shorter; it also works for
:class:`~scrapy.Request`.
The ``parse_author`` callback defines a helper function to extract and cleanup the
data from a CSS query and yields the Python dict with the author data.
The ``parse_author`` callback defines a helper function to extract and clean up
the data from a CSS query and yields the Python dict with the author data.
Another interesting thing this spider demonstrates is that, even if there are
many quotes from the same author, we don't need to worry about visiting the

View File

@ -150,7 +150,7 @@ Scrapy 2.13.2 (2025-06-09)
when it's closed before being fully initialized.
(:issue:`6857`, :issue:`6867`)
- Improved the README, updated the Scrapy logo in it.
- Improved the README and updated the Scrapy logo in it.
(:issue:`6831`, :issue:`6833`, :issue:`6839`)
- Restricted the Twisted version used in tests to below 25.5.0, as some tests
@ -169,14 +169,14 @@ Scrapy 2.13.2 (2025-06-09)
Scrapy 2.13.1 (2025-05-28)
--------------------------
- Give callback requests precedence over start requests when priority values
- Gave callback requests precedence over start requests when priority values
are the same.
This makes changes from 2.13.0 to start request handling more intuitive and
backward compatible. For scenarios where all requests have the same
This makes the changes from 2.13.0 to start request handling more intuitive
and backward compatible. For scenarios where all requests have the same
priorities, in 2.13.0 all start requests were sent before the first
callback request. In 2.13.1, same as in 2.12 and lower, start requests are
only sent when there are not enough pending callback requests to reach
callback request. In 2.13.1, the same as in 2.12 and lower, start requests
are only sent when there are not enough pending callback requests to reach
concurrency limits.
(:issue:`6828`)

View File

@ -4,7 +4,7 @@
Add-ons
=======
Scrapy's add-on system is a framework which unifies managing and configuring
Scrapy's add-on system is a framework that unifies managing and configuring
components that extend Scrapy's core functionality, such as middlewares,
extensions, or pipelines. It provides users with a plug-and-play experience in
Scrapy extension management, and grants extensive configuration control to
@ -71,7 +71,7 @@ usually best to leave its priority unchanged. For example, when editing a
If the ``update_settings`` method raises
:exc:`scrapy.exceptions.NotConfigured`, the add-on will be skipped. This makes
it easy to enable an add-on only when some conditions are met.
it easy to enable an add-on only when certain conditions are met.
Fallbacks
---------

View File

@ -22,9 +22,9 @@ functionality into Scrapy.
:synopsis: The Scrapy crawler
The Extension Manager is responsible for loading and keeping track of installed
extensions and it's configured through the :setting:`EXTENSIONS` setting which
contains a dictionary of all available extensions and their order similar to
how you :ref:`configure the downloader middlewares
extensions, and it's configured through the :setting:`EXTENSIONS` setting,
which contains a dictionary of all available extensions and their order similar
to how you :ref:`configure the downloader middlewares
<topics-downloader-middleware-setting>`.
.. autoclass:: Crawler
@ -89,7 +89,7 @@ how you :ref:`configure the downloader middlewares
The execution engine, which coordinates the core crawling logic
between the scheduler, downloader and spiders.
Some extension may want to access the Scrapy engine, to inspect or
Some extensions may want to access the Scrapy engine to inspect or
modify the downloader and scheduler behaviour, although this is an
advanced use and this API is not yet stable.

View File

@ -62,9 +62,9 @@ this:
:ref:`Spider Middleware <component-spider-middleware>` (see
:meth:`~scrapy.spidermiddlewares.SpiderMiddleware.process_spider_output`).
8. The :ref:`Engine <component-engine>` sends processed items to
:ref:`Item Pipelines <component-pipelines>`, then send processed Requests to
the :ref:`Scheduler <component-scheduler>` and asks for possible next Requests
8. The :ref:`Engine <component-engine>` sends processed items to :ref:`Item
Pipelines <component-pipelines>`, then sends processed Requests to the
:ref:`Scheduler <component-scheduler>` and asks for possible next Requests
to crawl.
9. The process repeats (from step 3) until there are no more requests from the

View File

@ -78,8 +78,8 @@ You should see an output like this::
That tells you that Scrapy is able to crawl about 3000 pages per minute in the
hardware where you run it. Note that this is a very simple spider intended to
follow links, any custom spider you write will probably do more stuff which
results in slower crawl rates. How slower depends on how much your spider does
and how well it's written.
results in slower crawl rates. How much slower depends on how much your spider
does and how well it's written.
Use scrapy-bench_ for more complex benchmarking.

View File

@ -13,7 +13,7 @@ In addition to this "focused crawl", there is another common type of crawling
which covers a large (potentially unlimited) number of domains, and is only
limited by time or other arbitrary constraint, rather than stopping when the
domain was crawled to completion or when there are no more requests to perform.
These are called "broad crawls" and is the typical crawlers employed by search
These are called "broad crawls" and are the typical crawlers employed by search
engines.
These are some common properties often found in broad crawls:
@ -44,9 +44,9 @@ efficient broad crawl.
Use the right :setting:`SCHEDULER_PRIORITY_QUEUE`
=================================================
Scrapys default scheduler priority queue is ``'scrapy.pqueues.ScrapyPriorityQueue'``.
It works best during single-domain crawl. It does not work well with crawling
many different domains in parallel
Scrapys default scheduler priority queue is
``'scrapy.pqueues.ScrapyPriorityQueue'``. It works best during a single-domain
crawl. It does not work well when crawling many different domains in parallel.
To apply the recommended priority queue use:
@ -87,9 +87,9 @@ Increase Twisted IO thread pool maximum size
Currently Scrapy does DNS resolution in a blocking way with usage of thread
pool. With higher concurrency levels the crawling could be slow or even fail
hitting DNS resolver timeouts. Possible solution to increase the number of
threads handling DNS queries. The DNS queue will be processed faster speeding
up establishing of connection and crawling overall.
hitting DNS resolver timeouts. A possible solution is to increase the number of
threads handling DNS queries. The DNS queue will be processed faster, speeding
up connection establishment and the crawl overall.
To increase maximum thread pool size use:
@ -100,10 +100,11 @@ To increase maximum thread pool size use:
Setup your own DNS
==================
If you have multiple crawling processes and single central DNS, it can act
like DoS attack on the DNS server resulting to slow down of entire network or
even blocking your machines. To avoid this setup your own DNS server with
local cache and upstream to some large DNS like OpenDNS or Verizon.
If you have multiple crawling processes and a single central DNS, it can act
like a DoS attack on the DNS server, resulting in a slowdown of the entire
network or even blocking your machines. To avoid this, set up your own DNS
server with a local cache and upstream to some large DNS like OpenDNS or
Verizon.
Reduce log level
================
@ -124,10 +125,10 @@ To set the log level use:
Disable cookies
===============
Disable cookies unless you *really* need. Cookies are often not needed when
doing broad crawls (search engine crawlers ignore them), and they improve
performance by saving some CPU cycles and reducing the memory footprint of your
Scrapy crawler.
Disable cookies unless you *really* need them. Cookies are often not needed
when doing broad crawls (search engine crawlers ignore them), and disabling
them improves performance by saving some CPU cycles and reducing the memory
footprint of your Scrapy crawler.
To disable cookies use:
@ -138,10 +139,10 @@ To disable cookies use:
Disable retries
===============
Retrying failed HTTP requests can slow down the crawls substantially, especially
when sites causes are very slow (or fail) to respond, thus causing a timeout
error which gets retried many times, unnecessarily, preventing crawler capacity
to be reused for other domains.
Retrying failed HTTP requests can slow down the crawls substantially,
especially when sites are very slow (or fail) to respond, thus causing a
timeout error that gets retried many times, unnecessarily preventing crawler
capacity from being reused for other domains.
To disable retries use:
@ -167,8 +168,8 @@ Disable redirects
Consider disabling redirects, unless you are interested in following them. When
doing broad crawls it's common to save redirects and resolve them when
revisiting the site at a later crawl. This also help to keep the number of
request constant per crawl batch, otherwise redirect loops may cause the
revisiting the site at a later crawl. This also helps to keep the number of
requests constant per crawl batch, otherwise redirect loops may cause the
crawler to dedicate too many resources on any specific domain.
To disable redirects use:

View File

@ -10,7 +10,7 @@ Scrapy is controlled through the ``scrapy`` command-line tool, to be referred to
here as the "Scrapy tool" to differentiate it from the sub-commands, which we
just call "commands" or "Scrapy commands".
The Scrapy tool provides several commands, for multiple purposes, and each one
The Scrapy tool provides several commands for multiple purposes, and each one
accepts a different set of arguments and options.
(The ``scrapy deploy`` command has been removed in 1.0 in favor of the
@ -65,7 +65,7 @@ structure by default, similar to this::
...
The directory where the ``scrapy.cfg`` file resides is known as the *project
root directory*. That file contains the name of the python module that defines
root directory*. That file contains the name of the Python module that defines
the project settings. Here is an example:
.. code-block:: ini
@ -105,7 +105,7 @@ for ``scrapy`` to use::
Using the ``scrapy`` tool
=========================
You can start by running the Scrapy tool with no arguments and it will print
You can start by running the Scrapy tool with no arguments, and it will print
some usage help and the available commands::
Scrapy X.Y - no active project
@ -119,8 +119,8 @@ some usage help and the available commands::
[...]
The first line will print the currently active project if you're inside a
Scrapy project. In this example it was run from outside a project. If run from inside
a project it would have printed something like this::
Scrapy project. In this example, it was run from outside a project. If run from
inside a project, it would have printed something like this::
Scrapy X.Y - project: myproject
@ -137,8 +137,8 @@ project::
scrapy startproject myproject [project_dir]
That will create a Scrapy project under the ``project_dir`` directory.
If ``project_dir`` wasn't specified, ``project_dir`` will be the same as ``myproject``.
That will create a Scrapy project under the ``project_dir`` directory. If
``project_dir`` isn't specified, it defaults to ``myproject``.
Next, you go inside the new project directory::
@ -159,7 +159,8 @@ For example, to create a new spider::
Some Scrapy commands (like :command:`crawl`) must be run from inside a Scrapy
project. See the :ref:`commands reference <topics-commands-ref>` below for more
information on which commands must be run from inside projects, and which not.
information on which commands must be run from inside projects and which ones
do not.
Also keep in mind that some commands may have slightly different behaviours
when running them from inside projects. For example, the fetch command will use
@ -186,7 +187,7 @@ And you can see all available commands with::
There are two kinds of commands, those that only work from inside a Scrapy
project (Project-specific commands) and those that also work without an active
Scrapy project (Global commands), though they may behave slightly differently
when run from inside a project (as they would use the project overridden
when run from inside a project (as they would use the project-overridden
settings).
Global commands:
@ -218,8 +219,7 @@ startproject
* Requires project: *no*
Creates a new Scrapy project named ``project_name``, under the ``project_dir``
directory.
If ``project_dir`` wasn't specified, ``project_dir`` will be the same as ``project_name``.
directory. If ``project_dir`` isn't specified, it defaults to ``project_name``.
Usage example::
@ -236,7 +236,10 @@ genspider
.. versionadded:: 2.6.0
The ability to pass a URL instead of a domain.
Creates a new spider in the current folder or in the current project's ``spiders`` folder, if called from inside a project. The ``<name>`` parameter is set as the spider's ``name``, while ``<domain or URL>`` is used to generate the ``allowed_domains`` and ``start_urls`` spider's attributes.
Creates a new spider in the current folder or in the current project's
``spiders`` folder, if called from inside a project. The ``<name>`` parameter
becomes the spider's ``name``, while ``<domain or URL>`` is used to generate
the spider's ``allowed_domains`` and ``start_urls`` attributes.
Usage example::
@ -253,9 +256,9 @@ Usage example::
$ scrapy genspider -t crawl scrapyorg scrapy.org
Created spider 'scrapyorg' using template 'crawl'
This is just a convenient shortcut command for creating spiders based on
pre-defined templates, but certainly not the only way to create spiders. You
can just create the spider source code files yourself, instead of using this
This command is just a convenient shortcut for creating spiders based on
pre-defined templates, but it's certainly not the only way to create spiders;
you can create the spider source code files yourself instead of using this
command.
.. command:: crawl
@ -274,9 +277,13 @@ Supported options:
* ``-a NAME=VALUE``: set a spider argument (may be repeated)
* ``--output FILE`` or ``-o FILE``: append scraped items to the end of FILE (use - for stdout). To define the output format, set a colon at the end of the output URI (i.e. ``-o FILE:FORMAT``)
* ``--output FILE`` or ``-o FILE``: append scraped items to the end of FILE
(use ``-`` for stdout). To define the output format, add a colon at the end
of the output URI (for example, ``-o FILE:FORMAT``)
* ``--overwrite-output FILE`` or ``-O FILE``: dump scraped items into FILE, overwriting any existing file. To define the output format, set a colon at the end of the output URI (i.e. ``-O FILE:FORMAT``)
* ``--overwrite-output FILE`` or ``-O FILE``: dump scraped items into FILE,
overwriting any existing file. To define the output format, add a colon at
the end of the output URI (for example, ``-O FILE:FORMAT``)
Usage examples::
@ -284,10 +291,10 @@ Usage examples::
[ ... myspider starts crawling ... ]
$ scrapy crawl -o myfile:csv myspider
[ ... myspider starts crawling and appends the result to the file myfile in csv format ... ]
[ ... myspider starts crawling and appends the result to the file myfile in CSV format ... ]
$ scrapy crawl -O myfile:json myspider
[ ... myspider starts crawling and saves the result in myfile in json format overwriting the original content... ]
[ ... myspider starts crawling and saves the result in myfile in JSON format, overwriting the original content ... ]
.. command:: check
@ -349,7 +356,7 @@ Edit the given spider using the editor defined in the ``EDITOR`` environment
variable or (if unset) the :setting:`EDITOR` setting.
This command is provided only as a convenient shortcut for the most common
case, the developer is of course free to choose any tool or IDE to write and
case; the developer is of course free to choose any tool or IDE to write and
debug spiders.
Usage example::
@ -373,7 +380,7 @@ attribute which overrides the User Agent, it will use that one.
So this command can be used to "see" how your spider would fetch a certain page.
If used outside a project, no particular per-spider behaviour would be applied
If used outside a project, no particular per-spider behaviour will be applied,
and it will just use the default Scrapy downloader settings.
Supported options:
@ -431,10 +438,10 @@ shell
* Syntax: ``scrapy shell [url]``
* Requires project: *no*
Starts the Scrapy shell for the given URL (if given) or empty if no URL is
given. Also supports UNIX-style local file paths, either relative with
``./`` or ``../`` prefixes or absolute file paths.
See :ref:`topics-shell` for more info.
Starts the Scrapy shell for the given URL (if provided) or leaves it empty if
no URL is given. It also supports UNIX-style local file paths, either relative
with ``./`` or ``../`` prefixes or absolute file paths. See :ref:`topics-shell`
for more info.
Supported options:
@ -479,16 +486,18 @@ Supported options:
* ``--spider=SPIDER``: bypass spider autodetection and force use of specific spider
* ``--a NAME=VALUE``: set spider argument (may be repeated)
* ``-a NAME=VALUE``: set spider argument (may be repeated)
* ``--callback`` or ``-c``: spider method to use as callback for parsing the
response
* ``--meta`` or ``-m``: additional request meta that will be passed to the callback
request. This must be a valid json string. Example: --meta='{"foo" : "bar"}'
* ``--meta`` or ``-m``: additional request meta that will be passed to the
callback request. This must be a valid JSON string. Example:
``--meta='{"foo": "bar"}'``
* ``--cbkwargs``: additional keyword arguments that will be passed to the callback.
This must be a valid json string. Example: --cbkwargs='{"foo" : "bar"}'
* ``--cbkwargs``: additional keyword arguments that will be passed to the
callback. This must be a valid JSON string. Example: ``--cbkwargs='{"foo":
"bar"}'``
* ``--pipelines``: process items through pipelines
@ -540,8 +549,8 @@ settings
Get the value of a Scrapy setting.
If used inside a project it'll show the project setting value, otherwise it'll
show the default Scrapy value for that setting.
If used inside a project, it'll show the project setting value; otherwise,
it'll show the default Scrapy value for that setting.
Example usage::
@ -605,7 +614,7 @@ spider or a special internal one:
* :command:`view`
They use an internal instance of :class:`scrapy.crawler.AsyncCrawlerProcess` or
:class:`scrapy.crawler.CrawlerProcess` for this. In most cases this detail
:class:`scrapy.crawler.CrawlerProcess` for this. In most cases, this detail
shouldn't matter to the user running the command, but when the user :ref:`needs
a non-default Twisted reactor <disable-asyncio>`, it may be important.
@ -621,9 +630,9 @@ project-level setting is set to :ref:`the asyncio reactor <install-asyncio>`
<default-settings>`) and :ref:`the setting of the spider being run
<spider-settings>` is set to :ref:`a different one <disable-asyncio>`, because
:class:`~scrapy.crawler.AsyncCrawlerProcess` only supports the asyncio reactor.
In this case you should set the :setting:`FORCE_CRAWLER_PROCESS` setting to
In this case, you should set the :setting:`FORCE_CRAWLER_PROCESS` setting to
``True`` (at the project level or via the command line) so that Scrapy uses
:class:`~scrapy.crawler.CrawlerProcess` which supports all reactors.
:class:`~scrapy.crawler.CrawlerProcess`, which supports all reactors.
Custom project commands
=======================
@ -658,7 +667,7 @@ You can also add Scrapy commands from an external library by adding a
``scrapy.commands`` section in the entry points of the library ``setup.py``
file.
The following example adds ``my_command`` command:
The following example adds the ``my_command`` command:
.. skip: next

View File

@ -97,8 +97,8 @@ For example:
print("log is enabled!")
Components do not need to declare their custom settings programmatically.
However, they should document them, so that users know they exist and how to
use them.
However, they should document them so that users know they exist and how to use
them.
It is a good practice to prefix custom settings with the name of the component,
to avoid collisions with custom settings of other existing (or future)
@ -114,8 +114,8 @@ initialization, and if ``False``, raise
:exc:`~scrapy.exceptions.NotConfigured`.
When choosing a name for a custom setting, it is also a good idea to have a
look at the names of :ref:`built-in settings <topics-settings-ref>`, to try to
maintain consistency with them.
look at the names of :ref:`built-in settings <topics-settings-ref>` to maintain
consistency with them.
.. _enforce-component-requirements:
@ -135,7 +135,7 @@ In the case of :ref:`downloader middlewares <topics-downloader-middleware>`,
<topics-item-pipeline>`, and :ref:`spider middlewares
<topics-spider-middleware>`, you should raise
:exc:`~scrapy.exceptions.NotConfigured`, passing a description of the issue as
a parameter to the exception so that it is printed in the logs, for the user to
a parameter to the exception so that it is printed in the logs for the user to
see. For other components, feel free to raise whatever other exception feels
right to you; for example, :exc:`RuntimeError` would make sense for a Scrapy
version mismatch, while :exc:`ValueError` may be better if the issue is the
@ -156,7 +156,7 @@ If your requirement is a minimum Scrapy version, you may use
if parse_version(scrapy.__version__) < parse_version("2.7"):
raise RuntimeError(
f"{MyComponent.__qualname__} requires Scrapy 2.7 or "
f"later, which allow defining the process_spider_output "
f"later, which allows defining the process_spider_output "
f"method of spider middlewares as an asynchronous "
f"generator."
)
@ -168,7 +168,7 @@ The following function can be used to create an instance of a component class:
.. autofunction:: scrapy.utils.misc.build_from_crawler
The following function can also be useful when implementing a component, to
The following function can also be useful when implementing a component to
report the import path of the component class, e.g. when reporting problems:
.. autofunction:: scrapy.utils.python.global_object_name

View File

@ -1,16 +1,16 @@
.. _topics-contracts:
=================
Spiders Contracts
=================
================
Spider Contracts
================
Testing spiders can get particularly annoying and while nothing prevents you
from writing unit tests the task gets cumbersome quickly. Scrapy offers an
Testing spiders can get particularly annoying, and while nothing prevents you
from writing unit tests, the task gets cumbersome quickly. Scrapy offers an
integrated way of testing your spiders by the means of contracts.
This allows you to test each callback of your spider by hardcoding a sample url
and check various constraints for how the callback processes the response. Each
contract is prefixed with an ``@`` and included in the docstring. See the
This allows you to test each callback of your spider by hardcoding a sample URL
and checking various constraints for how the callback processes the response.
Each contract is prefixed with an ``@`` and included in the docstring. See the
following example:
.. code-block:: python
@ -73,7 +73,7 @@ Use the :command:`check` command to run the contract checks.
Custom Contracts
================
If you find you need more power than the built-in Scrapy contracts you can
If you find you need more power than the built-in Scrapy contracts, you can
create and load your own contracts in the project by using the
:setting:`SPIDER_CONTRACTS` setting:
@ -100,17 +100,17 @@ override three methods:
.. method:: Contract.adjust_request_args(args)
This receives a ``dict`` as an argument containing default arguments
for request object. :class:`~scrapy.Request` is used by default,
but this can be changed with the ``request_cls`` attribute.
If multiple contracts in chain have this attribute defined, the last one is used.
This receives a ``dict`` containing default arguments for the request
object. :class:`~scrapy.Request` is used by default, but you can change
it with the ``request_cls`` attribute. If multiple contracts in the
chain define this attribute, the last one is used.
Must return the same or a modified version of it.
It must return the same dictionary or a modified version.
.. method:: Contract.pre_process(response)
This allows hooking in various checks on the response received from the
sample request, before it's being passed to the callback.
sample request, before it is passed to the callback.
.. method:: Contract.post_process(output)
@ -134,10 +134,8 @@ response received:
class HasHeaderContract(Contract):
"""
Demo contract which checks the presence of a custom header
@has_header X-CustomHeader
"""
"""Demo contract that checks the presence of a custom header:
@has_header X-CustomHeader"""
name = "has_header"
@ -152,8 +150,8 @@ Detecting check runs
====================
When ``scrapy check`` is running, the ``SCRAPY_CHECK`` environment variable is
set to the ``true`` string. You can use :data:`os.environ` to perform any change to
your spiders or your settings when ``scrapy check`` is used:
set to the ``true`` string. You can use :data:`os.environ` to make any changes
to your spiders or your settings when ``scrapy check`` is used:
.. code-block:: python

View File

@ -69,16 +69,16 @@ hence use coroutine syntax (e.g. ``await``, ``async for``, ``async with``):
Using Deferred-based APIs
=========================
In addition to native coroutine APIs Scrapy has some APIs that return a
In addition to native coroutine APIs, Scrapy has some APIs that return a
:class:`~twisted.internet.defer.Deferred` object or take a user-supplied
function that returns a :class:`~twisted.internet.defer.Deferred` object. These
APIs are also asynchronous but don't yet support native ``async def`` syntax.
In the future we plan to add support for the ``async def`` syntax to these APIs
or replace them with other APIs where changing the existing ones is
In the future, we plan to add support for the ``async def`` syntax to these
APIs or replace them with other APIs where changing the existing ones is
possible.
The following Scrapy methods return :class:`~twisted.internet.defer.Deferred`
objects (this list is not complete as it only includes methods that we think
objects (this list is not complete, as it only includes methods that we think
may be useful for user code):
- :class:`scrapy.crawler.Crawler`:
@ -151,7 +151,7 @@ return coroutines are listed in :ref:`coroutine-support`):
- ``stat_file()``
In most cases you can use these APIs in code that otherwise uses coroutines, by
In most cases, you can use these APIs in code that otherwise uses coroutines by
wrapping a :class:`~twisted.internet.defer.Deferred` object into a
:class:`~asyncio.Future` object or vice versa. See :ref:`asyncio-await-dfd` for
more information about this.
@ -212,8 +212,8 @@ becomes:
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:
:term:`awaitable objects <awaitable>` such as :class:`~asyncio.Future`. This
means you can use many useful Python libraries that provide such code:
.. skip: next
.. code-block:: python
@ -235,8 +235,8 @@ This means you can use many useful Python libraries providing such code:
# ... 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>`.
:mod:`asyncio` loop, and to use them you need to :doc:`enable asyncio
support in Scrapy<asyncio>`.
.. note:: If you want to ``await`` on Deferreds while using the asyncio reactor,
you need to :ref:`wrap them<asyncio-await-dfd>`.
@ -341,7 +341,7 @@ synchronous ``process_spider_output`` method gets a synchronous iterable as its
- 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
- If an exception is raised while awaiting the output of the previous
``Request`` callback or ``process_spider_output`` method, none of that
output will be processed.
@ -442,8 +442,8 @@ For example:
to define their ``process_spider_output`` method as an asynchronous
generator.
Since 2.13.0, Scrapy provides a base class,
Since version 2.13.0, Scrapy provides a base class,
:class:`~scrapy.spidermiddlewares.base.BaseSpiderMiddleware`, which implements
the ``process_spider_output()`` and ``process_spider_output_async()`` methods,
so instead of duplicating the processing code you can override the
so instead of duplicating the processing code, you can override the
``get_processed_request()`` and/or the ``get_processed_item()`` method.

View File

@ -5,7 +5,7 @@ Debugging Spiders
=================
This document explains the most common techniques for debugging spiders.
Consider the following Scrapy spider below:
Consider the following Scrapy spider:
.. skip: next
.. code-block:: python
@ -40,19 +40,19 @@ Consider the following Scrapy spider below:
# populate more `item` fields
return item
Basically this is a simple spider which parses two pages of items (the
start_urls). Items also have a details page with additional information, so we
use the ``cb_kwargs`` functionality of :class:`~scrapy.Request` to pass a
partially populated item.
Basically, this is a simple spider that parses two pages of items (the start
URLs). Items also have a details page with additional information, so we use
the ``cb_kwargs`` functionality of :class:`~scrapy.Request` to pass a partially
populated item.
Parse Command
=============
The most basic way of checking the output of your spider is to use the
:command:`parse` command. It allows to check the behaviour of different parts
of the spider at the method level. It has the advantage of being flexible and
simple to use, but does not allow debugging code inside a method.
:command:`parse` command. It allows you to check the behaviour of different
parts of the spider at the method level. It has the advantage of being flexible
and simple to use, but it does not allow debugging code inside a method.
.. highlight:: none
@ -90,7 +90,7 @@ Using the ``--verbose`` or ``-v`` option we can see the status at each depth lev
# Requests -----------------------------------------------------------------
[]
Checking items scraped from a single start_url, can also be easily achieved
Checking items scraped from a single start URL can also be easily achieved
using::
$ scrapy parse --spider=myspider -d 3 'http://example.com/page1'
@ -101,10 +101,10 @@ using::
Scrapy Shell
============
While the :command:`parse` command is very useful for checking behaviour of a
spider, it is of little help to check what happens inside a callback, besides
showing the response received and the output. How to debug the situation when
``parse_details`` sometimes receives no item?
While the :command:`parse` command is very useful for checking the behaviour of
a spider, it is of little help when checking what happens inside a callback
besides showing the response received and the output. How do you debug the
situation when ``parse_details`` sometimes receives no item?
.. highlight:: python
@ -129,7 +129,7 @@ See also: :ref:`topics-shell-inspect-response`.
Open in browser
===============
Sometimes you just want to see how a certain response looks in a browser, you
Sometimes you just want to see how a certain response looks in a browser; you
can use the :func:`~scrapy.utils.response.open_in_browser` function for that:
.. autofunction:: scrapy.utils.response.open_in_browser
@ -140,7 +140,7 @@ Logging
Logging is another useful option for getting information about your spider run.
Although not as convenient, it comes with the advantage that the logs will be
available in all future runs should they be necessary again:
available in all future runs should you need them again:
.. code-block:: python

View File

@ -24,8 +24,8 @@ Deploying to a Scrapyd Server
`Scrapyd`_ is an open source application to run Scrapy spiders. It provides
a server with HTTP API, capable of running and monitoring Scrapy spiders.
To deploy spiders to Scrapyd, you can use the scrapyd-deploy tool provided by
the `scrapyd-client`_ package. Please refer to the `scrapyd-deploy
To deploy spiders to Scrapyd, you can use the ``scrapyd-deploy`` tool provided
by the `scrapyd-client`_ package. Please refer to the `scrapyd-deploy
documentation`_ for more information.
Scrapyd is maintained by some of the Scrapy developers.
@ -38,12 +38,12 @@ Deploying to Zyte Scrapy Cloud
`Zyte Scrapy Cloud`_ is a hosted, cloud-based service by Zyte_, the company
behind Scrapy.
Zyte Scrapy Cloud removes the need to setup and monitor servers and provides a
Zyte Scrapy Cloud removes the need to set up and monitor servers and provides a
nice UI to manage spiders and review scraped items, logs and stats.
To deploy spiders to Zyte Scrapy Cloud you can use the `shub`_ command line
tool.
Please refer to the `Zyte Scrapy Cloud documentation`_ for more information.
To deploy spiders to Zyte Scrapy Cloud, you can use the ``shub`` command-line
tool. Please refer to the `Zyte Scrapy Cloud documentation`_ for more
information.
Zyte Scrapy Cloud is compatible with Scrapyd and one can switch between
them as needed - the configuration is read from the ``scrapy.cfg`` file

View File

@ -4,10 +4,10 @@
Using your browser's Developer Tools for scraping
=================================================
Here is a general guide on how to use your browser's Developer Tools
to ease the scraping process. Today almost all browsers come with
built in `Developer Tools`_ and although we will use Firefox in this
guide, the concepts are applicable to any other browser.
Here is a general guide on how to use your browser's Developer Tools to ease
the scraping process. Today almost all browsers come with built-in `Developer
Tools`_, and although we will use Firefox in this guide, the concepts are
applicable to any other browser.
In this guide we'll introduce the basic tools to use from a browser's
Developer Tools by scraping `quotes.toscrape.com`_.
@ -19,9 +19,9 @@ Caveats with inspecting the live browser DOM
Since Developer Tools operate on a live browser DOM, what you'll actually see
when inspecting the page source is not the original HTML, but a modified one
after applying some browser clean up and executing JavaScript code. Firefox,
in particular, is known for adding ``<tbody>`` elements to tables. Scrapy, on
the other hand, does not modify the original page HTML, so you won't be able to
after applying some browser clean up and executing JavaScript code. Firefox, in
particular, is known for adding ``<tbody>`` elements to tables. Scrapy, on the
other hand, does not modify the original page HTML, so you won't be able to
extract any data if you use ``<tbody>`` in your XPath expressions.
Therefore, you should keep in mind the following things:
@ -42,17 +42,16 @@ Inspecting a website
====================
By far the most handy feature of the Developer Tools is the `Inspector`
feature, which allows you to inspect the underlying HTML code of
any webpage. To demonstrate the Inspector, let's look at the
`quotes.toscrape.com`_-site.
feature, which allows you to inspect the underlying HTML code of any webpage.
To demonstrate the Inspector, let's look at the `quotes.toscrape.com`_ site.
On the site we have a total of ten quotes from various authors with specific
tags, as well as the Top Ten Tags. Let's say we want to extract all the quotes
on this page, without any meta-information about authors, tags, etc.
Instead of viewing the whole source code for the page, we can simply right click
on a quote and select ``Inspect Element (Q)``, which opens up the `Inspector`.
In it you should see something like this:
Instead of viewing the whole source code for the page, we can simply
right-click on a quote and select ``Inspect Element (Q)``, which opens up the
`Inspector`. In it you should see something like this:
.. image:: _images/inspector_01.png
:width: 777
@ -76,10 +75,10 @@ anywhere.
The advantage of the `Inspector` is that it automatically expands and collapses
sections and tags of a webpage, which greatly improves readability. You can
expand and collapse a tag by clicking on the arrow in front of it or by double
clicking directly on the tag. If we expand the ``span`` tag with the ``class=
"text"`` we will see the quote-text we clicked on. The `Inspector` lets you
copy XPaths to selected elements. Let's try it out.
expand and collapse a tag by clicking on the arrow in front of it or by
double-clicking directly on the tag. If we expand the ``span`` tag with the
``class="text"`` attribute, we see the quote text we clicked on. The
`Inspector` lets you copy XPaths to selected elements. Let's try it out.
First open the Scrapy shell at https://quotes.toscrape.com/ in a terminal:
@ -99,17 +98,17 @@ Then, back to your web browser, right-click on the ``span`` tag, select
>>> response.xpath("/html/body/div/div[2]/div[1]/div[1]/span[1]/text()").getall()
['“The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”']
Adding ``text()`` at the end we are able to extract the first quote with this
basic selector. But this XPath is not really that clever. All it does is
go down a desired path in the source code starting from ``html``. So let's
see if we can refine our XPath a bit:
Adding ``text()`` at the end, we are able to extract the first quote with this
basic selector. But this XPath is not really that clever. All it does is go
down a desired path in the source code starting from ``html``. So let's see if
we can refine our XPath a bit:
If we check the `Inspector` again we'll see that directly beneath our
expanded ``div`` tag we have nine identical ``div`` tags, each with the
same attributes as our first. If we expand any of them, we'll see the same
structure as with our first quote: Two ``span`` tags and one ``div`` tag. We can
expand each ``span`` tag with the ``class="text"`` inside our ``div`` tags and
see each quote:
If we check the `Inspector` again we'll see that directly beneath our expanded
``div`` tag we have nine identical ``div`` tags, each with the same attributes
as our first. If we expand any of them, we'll see the same structure as with
our first quote: two ``span`` tags and one ``div`` tag. We can expand each
``span`` tag with the ``class="text"`` inside our ``div`` tags and see each
quote:
.. code-block:: html
@ -145,14 +144,14 @@ source code or directly scrolling to an element you selected. Let's demonstrate
a use case:
Say you want to find the ``Next`` button on the page. Type ``Next`` into the
search bar on the top right of the `Inspector`. You should get two results.
The first is a ``li`` tag with the ``class="next"``, the second the text
of an ``a`` tag. Right click on the ``a`` tag and select ``Scroll into View``.
If you hover over the tag, you'll see the button highlighted. From here
we could easily create a :ref:`Link Extractor <topics-link-extractors>` to
follow the pagination. On a simple site such as this, there may not be
the need to find an element visually but the ``Scroll into View`` function
can be quite useful on complex sites.
search bar on the top-right of the `Inspector`. You should get two results. The
first is a ``li`` tag with the ``class="next"``, the second the text of an
``a`` tag. Right-click on the ``a`` tag and select ``Scroll into View``. If you
hover over the tag, you'll see the button highlighted. From here we could
easily create a :ref:`Link Extractor <topics-link-extractors>` to follow the
pagination. On a simple site such as this, there may not be the need to find an
element visually but the ``Scroll into View`` function can be quite useful on
complex sites.
Note that the search bar can also be used to search for and test CSS
selectors. For example, you could search for ``span.text`` to find
@ -161,19 +160,19 @@ exactly the ``span`` tag with the ``class="text"`` in the page.
.. _topics-network-tool:
The Network-tool
The Network tool
================
While scraping you may come across dynamic webpages where some parts
of the page are loaded dynamically through multiple requests. While
this can be quite tricky, the `Network`-tool in the Developer Tools
greatly facilitates this task. To demonstrate the Network-tool, let's
take a look at the page `quotes.toscrape.com/scroll`_.
While scraping you may come across dynamic webpages where some parts of the
page are loaded dynamically through multiple requests. While this can be quite
tricky, the `Network` tool in the Developer Tools greatly facilitates this
task. To demonstrate the Network tool, let's take a look at the page
`quotes.toscrape.com/scroll`_.
The page is quite similar to the basic `quotes.toscrape.com`_-page,
but instead of the above-mentioned ``Next`` button, the page
automatically loads new quotes when you scroll to the bottom. We
could go ahead and try out different XPaths directly, but instead
we'll check another quite useful command from the Scrapy shell:
The page is quite similar to the basic `quotes.toscrape.com`_ page, but instead
of the above-mentioned ``Next`` button, the page automatically loads new quotes
when you scroll to the bottom. We could go ahead and try out different XPaths
directly, but instead we'll check another quite useful command from the Scrapy
shell:
.. skip: next
@ -192,12 +191,11 @@ bar with the word ``Loading...``.
:height: 296
:alt: Response from quotes.toscrape.com/scroll
The ``view(response)`` command let's us view the response our
shell or later our spider receives from the server. Here we see
that some basic template is loaded which includes the title,
the login-button and the footer, but the quotes are missing. This
tells us that the quotes are being loaded from a different request
than ``quotes.toscrape/scroll``.
The ``view(response)`` command lets us view the response our shell or later our
spider receives from the server. Here we see that some basic template is loaded
which includes the title, the login button and the footer, but the quotes are
missing. This tells us that the quotes are being loaded from a different
request than ``quotes.toscrape.com/scroll``.
If you click on the ``Network`` tab, you will probably only see
two entries. The first thing we do is enable persistent logs by
@ -218,30 +216,29 @@ Here we see every request that has been made when reloading the page
and can inspect each request and its response. So let's find out
where our quotes are coming from:
First click on the request with the name ``scroll``. On the right
you can now inspect the request. In ``Headers`` you'll find details
about the request headers, such as the URL, the method, the IP-address,
and so on. We'll ignore the other tabs and click directly on ``Response``.
First click on the request with the name ``scroll``. On the right you can now
inspect the request. In ``Headers`` you'll find details about the request
headers, such as the URL, the method, the IP address, and so on. We'll ignore
the other tabs and click directly on ``Response``.
What you should see in the ``Preview`` pane is the rendered HTML-code,
that is exactly what we saw when we called ``view(response)`` in the
shell. Accordingly the ``type`` of the request in the log is ``html``.
The other requests have types like ``css`` or ``js``, but what
interests us is the one request called ``quotes?page=1`` with the
type ``json``.
What you should see in the ``Preview`` pane is the rendered HTML code, exactly
what we saw when we called ``view(response)`` in the shell. Accordingly, the
``type`` of the request in the log is ``html``. The other requests have types
like ``css`` or ``js``, but what interests us is the one request called
``quotes?page=1`` with the type ``json``.
If we click on this request, we see that the request URL is
``https://quotes.toscrape.com/api/quotes?page=1`` and the response
is a JSON-object that contains our quotes. We can also right-click
on the request and open ``Open in new tab`` to get a better overview.
``https://quotes.toscrape.com/api/quotes?page=1`` and the response is a JSON
object that contains our quotes. We can also right-click on the request and
open ``Open in new tab`` to get a better overview.
.. image:: _images/network_03.png
:width: 777
:height: 375
:alt: JSON-object returned from the quotes.toscrape API
:alt: JSON object returned from the quotes.toscrape API
With this response we can now easily parse the JSON-object and
also request each page to get every quote on the site:
With this response we can now easily parse the JSON object and also request
each page to get every quote on the site:
.. code-block:: python
@ -264,15 +261,14 @@ also request each page to get every quote on the site:
url = f"https://quotes.toscrape.com/api/quotes?page={self.page}"
yield scrapy.Request(url=url, callback=self.parse)
This spider starts at the first page of the quotes-API. With each
response, we parse the ``response.text`` and assign it to ``data``.
This lets us operate on the JSON-object like on a Python dictionary.
We iterate through the ``quotes`` and print out the ``quote["text"]``.
If the handy ``has_next`` element is ``true`` (try loading
`quotes.toscrape.com/api/quotes?page=10`_ in your browser or a
page-number greater than 10), we increment the ``page`` attribute
and ``yield`` a new request, inserting the incremented page-number
into our ``url``.
This spider starts at the first page of the quotes-API. With each response, we
parse the ``response.text`` and assign it to ``data``. This lets us operate on
the JSON object like on a Python dictionary. We iterate through the ``quotes``
and print out the ``quote["text"]``. If the handy ``has_next`` element is
``true`` (try loading `quotes.toscrape.com/api/quotes?page=10`_ in your browser
or a page number greater than 10), we increment the ``page`` attribute and
``yield`` a new request, inserting the incremented page number into our
``url``.
.. _requests-from-curl:
@ -306,11 +302,11 @@ function to get a dictionary with the equivalent arguments:
Note that to translate a cURL command into a Scrapy request,
you may use `curl2scrapy <https://michael-shub.github.io/curl2scrapy/>`_.
As you can see, with a few inspections in the `Network`-tool we
were able to easily replicate the dynamic requests of the scrolling
functionality of the page. Crawling dynamic pages can be quite
daunting and pages can be very complex, but it (mostly) boils down
to identifying the correct request and replicating it in your spider.
As you can see, with a few inspections in the `Network` tool we were able to
easily replicate the dynamic requests of the scrolling functionality of the
page. Crawling dynamic pages can be quite daunting and pages can be very
complex, but it (mostly) boils down to identifying the correct request and
replicating it in your spider.
.. _Developer Tools: https://en.wikipedia.org/wiki/Web_development_tools
.. _quotes.toscrape.com: https://quotes.toscrape.com

View File

@ -5,7 +5,7 @@ Downloader Middleware
=====================
The downloader middleware is a framework of hooks into Scrapy's
request/response processing. It's a light, low-level system for globally
request/response processing. It's a light, low-level system for globally
altering Scrapy's requests and responses.
.. _topics-downloader-middleware-setting:
@ -42,9 +42,10 @@ middleware performs a different action and your middleware could depend on some
previous (or subsequent) middleware being applied.
If you want to disable a built-in middleware (the ones defined in
:setting:`DOWNLOADER_MIDDLEWARES_BASE` and enabled by default) you must define it
in your project's :setting:`DOWNLOADER_MIDDLEWARES` setting and assign ``None``
as its value. For example, if you want to disable the user-agent middleware:
:setting:`DOWNLOADER_MIDDLEWARES_BASE` and enabled by default) you must define
it in your project's :setting:`DOWNLOADER_MIDDLEWARES` setting and assign
``None`` as its value. For example, if you want to disable the user-agent
middleware:
.. code-block:: python
@ -54,7 +55,7 @@ as its value. For example, if you want to disable the user-agent middleware:
}
Finally, keep in mind that some middlewares may need to be enabled through a
particular setting. See each middleware documentation for more info.
particular setting. See each middleware's documentation for more info.
.. _topics-downloader-middleware-custom:
@ -79,14 +80,16 @@ defines one or more of these methods:
:class:`~scrapy.http.Response` object, return a :class:`~scrapy.Request`
object, or raise :exc:`~scrapy.exceptions.IgnoreRequest`.
If it returns ``None``, Scrapy will continue processing this request, executing all
other middlewares until, finally, the appropriate downloader handler is called
the request performed (and its response downloaded).
If it returns ``None``, Scrapy will continue processing this request,
executing all other middlewares until, finally, the appropriate
downloader handler is called and the request is performed (and its
response downloaded).
If it returns a :class:`~scrapy.http.Response` object, Scrapy won't bother
calling *any* other :meth:`process_request` or :meth:`process_exception` methods,
or the appropriate download function; it'll return that response. The :meth:`process_response`
methods of installed middleware is always called on every response.
If it returns a :class:`~scrapy.http.Response` object, Scrapy won't
bother calling *any* other :meth:`process_request` or
:meth:`process_exception` methods, or the appropriate download function;
it'll return that response. The :meth:`process_response` methods of
installed middleware are always called on every response.
If it returns a :class:`~scrapy.Request` object, Scrapy will stop calling
:meth:`process_request` methods and reschedule the returned request. Once the newly returned
@ -182,10 +185,10 @@ CookiesMiddleware
sends them back on subsequent requests (from that spider), just like web
browsers do.
.. caution:: When non-UTF8 encoded byte sequences are passed to a
:class:`~scrapy.Request`, the ``CookiesMiddleware`` will log
a warning. Refer to :ref:`topics-logging-advanced-customization`
to customize the logging behaviour.
.. caution:: When non-UTF-8 encoded byte sequences are passed to a
:class:`~scrapy.Request`, the ``CookiesMiddleware`` will log a warning.
Refer to :ref:`topics-logging-advanced-customization`
to customize the logging behavior.
.. caution:: Cookies set via the ``Cookie`` header are not considered by the
:ref:`cookies-mw`. If you need to set cookies for a request, use the
@ -307,23 +310,24 @@ HttpAuthMiddleware
.. class:: HttpAuthMiddleware
This middleware authenticates all requests generated from certain spiders
using `Basic access authentication`_ (aka. HTTP auth).
using `Basic access authentication`_ (aka HTTP auth).
To enable HTTP authentication for a spider, set the ``http_user`` and
``http_pass`` spider attributes to the authentication data and the
``http_auth_domain`` spider attribute to the domain which requires this
authentication (its subdomains will be also handled in the same way).
You can set ``http_auth_domain`` to ``None`` to enable the
authentication for all requests but you risk leaking your authentication
credentials to unrelated domains.
authentication (its subdomains will also be handled in the same way). You
can set ``http_auth_domain`` to ``None`` to enable the authentication for
all requests but you risk leaking your authentication credentials to
unrelated domains.
.. warning::
In previous Scrapy versions HttpAuthMiddleware sent the authentication
In previous Scrapy versions, HttpAuthMiddleware sent the authentication
data with all requests, which is a security problem if the spider
makes requests to several different domains. Currently if the
makes requests to several different domains. Currently, if the
``http_auth_domain`` attribute is not set, the middleware will use the
domain of the first request, which will work for some spiders but not
for others. In the future the middleware will produce an error instead.
for others. In the future, the middleware will produce an error
instead.
Example:
@ -351,8 +355,9 @@ HttpCacheMiddleware
.. class:: HttpCacheMiddleware
This middleware provides low-level cache to all HTTP requests and responses.
It has to be combined with a cache storage backend as well as a cache policy.
This middleware provides a low-level cache to all HTTP requests and
responses. It has to be combined with a cache storage backend as well as a
cache policy.
Scrapy ships with the following HTTP cache storage backends:
@ -372,7 +377,8 @@ HttpCacheMiddleware
.. reqmeta:: dont_cache
You can also avoid caching a response on every policy using :reqmeta:`dont_cache` meta key equals ``True``.
You can also avoid caching a response, regardless of the policy, by setting
the :reqmeta:`dont_cache` meta key to ``True``.
.. module:: scrapy.extensions.httpcache
:noindex:
@ -384,10 +390,10 @@ Dummy policy (default)
.. class:: DummyPolicy
This policy has no awareness of any HTTP Cache-Control directives.
Every request and its corresponding response are cached. When the same
request is seen again, the response is returned without transferring
anything from the Internet.
This policy has no awareness of any HTTP Cache-Control directives. Every
request and its corresponding response are cached. When the same request is
seen again, the response is returned without transferring anything from the
Internet.
The Dummy policy is useful for testing spiders faster (without having
to wait for downloads every time) and for trying your spider offline,
@ -576,8 +582,8 @@ HTTPCACHE_DIR
Default: ``'httpcache'``
The directory to use for storing the (low-level) HTTP cache. If empty, the HTTP
cache will be disabled. If a relative path is given, is taken relative to the
project data dir. For more info see: :ref:`topics-project-structure`.
cache will be disabled. If a relative path is given, it is taken relative to
the project data dir. For more info see: :ref:`topics-project-structure`.
.. setting:: HTTPCACHE_IGNORE_HTTP_CODES
@ -586,7 +592,7 @@ HTTPCACHE_IGNORE_HTTP_CODES
Default: ``[]``
Don't cache response with these HTTP codes.
Don't cache responses with these HTTP codes.
.. setting:: HTTPCACHE_IGNORE_MISSING
@ -671,10 +677,10 @@ Default: ``[]``
List of Cache-Control directives in responses to be ignored.
Sites often set "no-store", "no-cache", "must-revalidate", etc., but get
upset at the traffic a spider can generate if it actually respects those
directives. This allows to selectively ignore Cache-Control directives
that are known to be unimportant for the sites being crawled.
Sites often set "no-store", "no-cache", "must-revalidate", etc., but get upset
at the traffic a spider can generate if it actually respects those directives.
This allows you to selectively ignore Cache-Control directives that are known
to be unimportant for the sites being crawled.
We assume that the spider will not issue Cache-Control directives
in requests unless it actually needs them, so directives in requests are
@ -735,9 +741,10 @@ HttpProxyMiddleware
* ``no_proxy``
You can also set the meta key ``proxy`` per-request, to a value like
``http://some_proxy_server:port`` or ``http://username:password@some_proxy_server:port``.
Keep in mind this value will take precedence over ``http_proxy``/``https_proxy``
environment variables, and it will also ignore ``no_proxy`` environment variable.
``http://some_proxy_server:port`` or
``http://username:password@some_proxy_server:port``. Keep in mind that this
value will take precedence over ``http_proxy``/``https_proxy`` environment
variables, and it will also ignore ``no_proxy`` environment variable.
HttpProxyMiddleware settings
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -882,7 +889,8 @@ MetaRefreshMiddleware
.. class:: MetaRefreshMiddleware
This middleware handles redirection of requests based on meta-refresh html tag.
This middleware handles redirection of requests based on the HTML
meta-refresh tag.
The :class:`MetaRefreshMiddleware` can be configured through the following
settings (see the settings documentation for more info):
@ -891,9 +899,10 @@ settings (see the settings documentation for more info):
* :setting:`METAREFRESH_IGNORE_TAGS`
* :setting:`METAREFRESH_MAXDELAY`
This middleware obey :setting:`REDIRECT_MAX_TIMES` setting, :reqmeta:`dont_redirect`,
:reqmeta:`redirect_urls` and :reqmeta:`redirect_reasons` request meta keys as described
for :class:`RedirectMiddleware`
This middleware obeys the :setting:`REDIRECT_MAX_TIMES` setting,
:reqmeta:`dont_redirect`, :reqmeta:`redirect_urls` and
:reqmeta:`redirect_reasons` request meta keys as described for
:class:`RedirectMiddleware`
MetaRefreshMiddleware settings
@ -1002,7 +1011,7 @@ RETRY_HTTP_CODES
Default: ``[500, 502, 503, 504, 522, 524, 408, 429]``
Which HTTP response codes to retry. Other errors (DNS lookup issues,
connections lost, etc) are always retried.
connections lost, etc.) are always retried.
In some cases you may want to add 400 to :setting:`RETRY_HTTP_CODES` because
it is a common code used to indicate server overload. It is not included by

View File

@ -85,16 +85,16 @@ It might be enough to yield a :class:`~scrapy.Request` with the same HTTP
method and URL. However, you may also need to reproduce the body, headers and
form parameters (see :class:`~scrapy.FormRequest`) of that request.
As all major browsers allow to export the requests in curl_ format, Scrapy
incorporates the method :meth:`~scrapy.Request.from_curl` to generate an equivalent
:class:`~scrapy.Request` from a cURL command. To get more information
visit :ref:`request from curl <requests-from-curl>` inside the network
tool section.
As all major browsers allow you to export requests in curl_ format, Scrapy
incorporates the method :meth:`~scrapy.Request.from_curl` to generate an
equivalent :class:`~scrapy.Request` from a cURL command. To get more
information visit :ref:`request from curl <requests-from-curl>` inside the
network tool section.
Once you get the expected response, you can :ref:`extract the desired data from
it <topics-handling-response-formats>`.
You can reproduce any request with Scrapy. However, some times reproducing all
You can reproduce any request with Scrapy. However, sometimes reproducing all
necessary requests may not seem efficient in developer time. If that is your
case, and crawling speed is not a major concern for you, you can alternatively
consider :ref:`using a headless browser <topics-headless-browsing>`.
@ -271,8 +271,8 @@ The following is a simple snippet to illustrate its usage within a Scrapy spider
However, using `playwright-python`_ directly as in the above example
circumvents most of the Scrapy components (middlewares, dupefilter, etc).
We recommend using `scrapy-playwright`_ for a better integration.
circumvents most of the Scrapy components (middlewares, dupefilter, etc.). We
recommend using `scrapy-playwright`_ for a better integration.
.. _AJAX: https://en.wikipedia.org/wiki/Ajax_%28programming%29
.. _CSS: https://en.wikipedia.org/wiki/Cascading_Style_Sheets

View File

@ -8,12 +8,11 @@ Sending e-mail
:synopsis: Email sending facility
Although Python makes sending e-mails relatively easy via the :mod:`smtplib`
library, Scrapy provides its own facility for sending e-mails which is very
easy to use and it's implemented using :doc:`Twisted non-blocking IO
library, Scrapy provides its own facility for sending e-mails that is very easy
to use and is implemented using :doc:`Twisted non-blocking IO
<twisted:core/howto/defer-intro>`, to avoid interfering with the non-blocking
IO of the crawler. It also provides a simple API for sending attachments and
it's very easy to configure, with a few :ref:`settings
<topics-email-settings>`.
IO of the crawler. It also provides a simple API for sending attachments and is
very easy to configure, with a few :ref:`settings <topics-email-settings>`.
Quick example
=============
@ -27,7 +26,7 @@ the standard ``__init__`` method:
mailer = MailSender()
Or you can instantiate it passing a :class:`scrapy.Crawler` instance, which
Or you can instantiate it by passing a :class:`scrapy.Crawler` instance, which
will respect the :ref:`settings <topics-email-settings>`:
.. skip: start
@ -50,8 +49,8 @@ And here is how to use it to send an e-mail (without attachments):
MailSender class reference
==========================
The MailSender :ref:`components <topics-components>` is the preferred class to
use for sending emails from Scrapy, as it uses :doc:`Twisted non-blocking IO
The MailSender :ref:`component <topics-components>` is the preferred class to
use for sending e-mails from Scrapy, as it uses :doc:`Twisted non-blocking IO
<twisted:core/howto/defer-intro>`, like the rest of the framework.
.. class:: MailSender(smtphost=None, mailfrom=None, smtpuser=None, smtppass=None, smtpport=None)
@ -67,9 +66,9 @@ use for sending emails from Scrapy, as it uses :doc:`Twisted non-blocking IO
:param smtpuser: the SMTP user. If omitted, the :setting:`MAIL_USER`
setting will be used. If not given, no SMTP authentication will be
performed.
:type smtphost: str or bytes
:type smtpuser: str or bytes
:param smtppass: the SMTP pass for authentication.
:param smtppass: the SMTP password for authentication.
:type smtppass: str or bytes
:param smtpport: the SMTP port to connect to
@ -83,7 +82,7 @@ use for sending emails from Scrapy, as it uses :doc:`Twisted non-blocking IO
.. method:: send(to, subject, body, cc=None, attachs=(), mimetype='text/plain', charset=None)
Send email to the given recipients.
Send an e-mail to the given recipients.
:param to: the e-mail recipients as a string or as a list of strings
:type to: str or list
@ -98,10 +97,10 @@ use for sending emails from Scrapy, as it uses :doc:`Twisted non-blocking IO
:type body: str
:param attachs: an iterable of tuples ``(attach_name, mimetype,
file_object)`` where ``attach_name`` is a string with the name that will
appear on the e-mail's attachment, ``mimetype`` is the mimetype of the
attachment and ``file_object`` is a readable file object with the
contents of the attachment
file_object)`` where ``attach_name`` is a string with the name that
will appear on the e-mail attachment, ``mimetype`` is the mimetype of
the attachment, and ``file_object`` is a readable file object with
the contents of the attachment
:type attachs: collections.abc.Iterable
:param mimetype: the MIME type of the e-mail
@ -116,9 +115,9 @@ use for sending emails from Scrapy, as it uses :doc:`Twisted non-blocking IO
Mail settings
=============
These settings define the default ``__init__`` method values of the :class:`MailSender`
class, and can be used to configure e-mail notifications in your project without
writing any code (for those extensions and code that uses :class:`MailSender`).
These settings define the default arguments passed to :class:`MailSender` and
can be used to configure e-mail notifications in your project without writing
any code (for those extensions and code that use :class:`MailSender`).
.. setting:: MAIL_FROM
@ -154,8 +153,8 @@ MAIL_USER
Default: ``None``
User to use for SMTP authentication. If disabled no SMTP authentication will be
performed.
User to use for SMTP authentication. If disabled, no SMTP authentication will
be performed.
.. setting:: MAIL_PASS
@ -173,7 +172,8 @@ MAIL_TLS
Default: ``False``
Enforce using STARTTLS. STARTTLS is a way to take an existing insecure connection, and upgrade it to a secure connection using SSL/TLS.
Enforce using STARTTLS. STARTTLS is a way to take an existing insecure
connection and upgrade it to a secure connection using SSL/TLS.
.. setting:: MAIL_SSL
@ -182,4 +182,4 @@ MAIL_SSL
Default: ``False``
Enforce connecting using an SSL encrypted connection
Enforce connecting using an SSL-encrypted connection.

View File

@ -93,12 +93,12 @@ signal handler to indicate that no further bytes should be downloaded for a resp
The ``fail`` boolean parameter controls which method will handle the resulting
response:
* If ``fail=True`` (default), the request errback is called. The response object is
available as the ``response`` attribute of the ``StopDownload`` exception,
which is in turn stored as the ``value`` attribute of the received
:class:`~twisted.python.failure.Failure` object. This means that in an errback
defined as ``def errback(self, failure)``, the response can be accessed though
``failure.value.response``.
* If ``fail=True`` (default), the request errback is called. The response
object is available as the ``response`` attribute of the ``StopDownload``
exception, which is in turn stored as the ``value`` attribute of the received
:class:`~twisted.python.failure.Failure` object. This means that in an
errback defined as ``def errback(self, failure)``, the response can be
accessed through ``failure.value.response``.
* If ``fail=False``, the request callback is called instead.

View File

@ -11,34 +11,34 @@ Once you have scraped your items, you often want to persist or export those
items, to use the data in some other application. That is, after all, the whole
purpose of the scraping process.
For this purpose Scrapy provides a collection of Item Exporters for different
For this purpose, Scrapy provides a collection of Item Exporters for different
output formats, such as XML, CSV or JSON.
Using Item Exporters
====================
If you are in a hurry, and just want to use an Item Exporter to output scraped
data see the :ref:`topics-feed-exports`. Otherwise, if you want to know how
data, see the :ref:`topics-feed-exports`. Otherwise, if you want to know how
Item Exporters work or need more custom functionality (not covered by the
default exports), continue reading below.
In order to use an Item Exporter, you must instantiate it with its required
args. Each Item Exporter requires different arguments, so check each exporter
documentation to be sure, in :ref:`topics-exporters-reference`. After you have
In order to use an Item Exporter, you must instantiate it with its required
args. Each Item Exporter requires different arguments, so check each exporter's
documentation in :ref:`topics-exporters-reference` to be sure. After you have
instantiated your exporter, you have to:
1. call the method :meth:`~BaseItemExporter.start_exporting` in order to
signal the beginning of the exporting process
1. Call the method :meth:`~BaseItemExporter.start_exporting` in order to
signal the beginning of the exporting process
2. call the :meth:`~BaseItemExporter.export_item` method for each item you want
to export
2. Call the :meth:`~BaseItemExporter.export_item` method for each item you want
to export
3. and finally call the :meth:`~BaseItemExporter.finish_exporting` to signal
the end of the exporting process
3. Finally, call the :meth:`~BaseItemExporter.finish_exporting` method to
signal the end of the exporting process
Here you can see an :doc:`Item Pipeline <item-pipeline>` which uses multiple
Item Exporters to group scraped items to different files according to the
value of one of their fields:
Here you can see an :doc:`Item Pipeline <item-pipeline>` that uses multiple
Item Exporters to distribute scraped items into different files according to
the value of one of their fields:
.. code-block:: python
@ -159,9 +159,10 @@ BaseItemExporter
defining what fields to export, whether to export empty fields, or which
encoding to use.
These features can be configured through the ``__init__`` method arguments which
populate their respective instance attributes: :attr:`fields_to_export`,
:attr:`export_empty_fields`, :attr:`encoding`, :attr:`indent`.
These features can be configured through the ``__init__`` method arguments,
which populate their respective instance attributes:
:attr:`fields_to_export`, :attr:`export_empty_fields`, :attr:`encoding`,
:attr:`indent`.
.. versionadded:: 2.0
The *dont_fail* parameter.
@ -263,8 +264,9 @@ XmlItemExporter
Exports items in XML format to the specified file object.
:param file: the file-like object to use for exporting the data. Its ``write`` method should
accept ``bytes`` (a disk file opened in binary mode, a ``io.BytesIO`` object, etc)
:param file: the file-like object to use for exporting the data. Its
``write`` method should accept ``bytes`` (a disk file opened in binary
mode, an ``io.BytesIO`` object, etc)
:param root_element: The name of root element in the exported XML.
:type root_element: str
@ -297,7 +299,7 @@ XmlItemExporter
Item(name=['John', 'Doe'], age='23')
Would be serialized as::
It would be serialized as::
<?xml version="1.0" encoding="utf-8"?>
<items>
@ -320,8 +322,9 @@ CsvItemExporter
CSV columns, their order and their column names. The
:attr:`export_empty_fields` attribute has no effect on this exporter.
:param file: the file-like object to use for exporting the data. Its ``write`` method should
accept ``bytes`` (a disk file opened in binary mode, a ``io.BytesIO`` object, etc)
:param file: the file-like object to use for exporting the data. Its
``write`` method should accept ``bytes`` (a disk file opened in binary
mode, an ``io.BytesIO`` object, etc)
:param include_headers_line: If enabled, makes the exporter output a header
line with the field names taken from
@ -355,8 +358,9 @@ PickleItemExporter
Exports items in pickle format to the given file-like object.
:param file: the file-like object to use for exporting the data. Its ``write`` method should
accept ``bytes`` (a disk file opened in binary mode, a ``io.BytesIO`` object, etc)
:param file: the file-like object to use for exporting the data. Its
``write`` method should accept ``bytes`` (a disk file opened in binary
mode, an ``io.BytesIO`` object, etc)
:param protocol: The pickle protocol to use.
:type protocol: int
@ -366,7 +370,7 @@ PickleItemExporter
The additional keyword arguments of this ``__init__`` method are passed to the
:class:`BaseItemExporter` ``__init__`` method.
Pickle isn't a human readable format, so no output examples are provided.
Pickle isn't a human-readable format, so no output examples are provided.
PprintItemExporter
------------------
@ -375,8 +379,9 @@ PprintItemExporter
Exports items in pretty print format to the specified file object.
:param file: the file-like object to use for exporting the data. Its ``write`` method should
accept ``bytes`` (a disk file opened in binary mode, a ``io.BytesIO`` object, etc)
:param file: the file-like object to use for exporting the data. Its
``write`` method should accept ``bytes`` (a disk file opened in binary
mode, an ``io.BytesIO`` object, etc)
The additional keyword arguments of this ``__init__`` method are passed to the
:class:`BaseItemExporter` ``__init__`` method.
@ -399,8 +404,9 @@ JsonItemExporter
arguments to the :class:`~json.JSONEncoder` ``__init__`` method, so you can use any
:class:`~json.JSONEncoder` ``__init__`` method argument to customize this exporter.
:param file: the file-like object to use for exporting the data. Its ``write`` method should
accept ``bytes`` (a disk file opened in binary mode, a ``io.BytesIO`` object, etc)
:param file: the file-like object to use for exporting the data. Its
``write`` method should accept ``bytes`` (a disk file opened in binary
mode, an ``io.BytesIO`` object, etc)
A typical output of this exporter would be::
@ -409,10 +415,10 @@ JsonItemExporter
.. _json-with-large-data:
.. warning:: JSON is very simple and flexible serialization format, but it
doesn't scale well for large amounts of data since incremental (aka.
.. warning:: JSON is a very simple and flexible serialization format, but it
doesn't scale well for large amounts of data because incremental (aka
stream-mode) parsing is not well supported (if at all) among JSON parsers
(on any language), and most of them just parse the entire object in
in any language, and most of them just parse the entire object in
memory. If you want the power and simplicity of JSON with a more
stream-friendly format, consider using :class:`JsonLinesItemExporter`
instead, or splitting the output in multiple chunks.
@ -428,8 +434,9 @@ JsonLinesItemExporter
the :class:`~json.JSONEncoder` ``__init__`` method, so you can use any
:class:`~json.JSONEncoder` ``__init__`` method argument to customize this exporter.
:param file: the file-like object to use for exporting the data. Its ``write`` method should
accept ``bytes`` (a disk file opened in binary mode, a ``io.BytesIO`` object, etc)
:param file: the file-like object to use for exporting the data. Its
``write`` method should accept ``bytes`` (a disk file opened in binary
mode, an ``io.BytesIO`` object, etc)
A typical output of this exporter would be::

View File

@ -48,8 +48,8 @@ tasks triggered by them.
Sample extension
----------------
Here we will implement a simple extension to illustrate the concepts described
in the previous section. This extension will log a message every time:
Here we implement a simple extension to illustrate the concepts described in
the previous section. This extension will log a message every time:
* a spider is opened
* a spider is closed
@ -58,7 +58,7 @@ in the previous section. This extension will log a message every time:
The extension will be enabled through the ``MYEXT_ENABLED`` setting and the
number of items will be specified through the ``MYEXT_ITEMCOUNT`` setting.
Here is the code of such extension:
Here is the code for that extension:
.. code-block:: python
@ -158,7 +158,7 @@ Provides a telnet console for getting into a Python interpreter inside the
currently running Scrapy process, which can be very useful for debugging.
The telnet console must be enabled by the :setting:`TELNETCONSOLE_ENABLED`
setting, and the server will listen in the port specified in
setting, and the server will listen on the port specified in
:setting:`TELNETCONSOLE_PORT`.
.. _topics-extensions-ref-memusage:
@ -180,7 +180,7 @@ Monitors the memory used by the Scrapy process that runs the spider and:
The notification e-mails can be triggered when a certain warning value is
reached (:setting:`MEMUSAGE_WARNING_MB`) and when the maximum value is reached
(:setting:`MEMUSAGE_LIMIT_MB`) which will also cause the spider to be closed
(:setting:`MEMUSAGE_LIMIT_MB`), which will also cause the spider to be closed
and the Scrapy process to be terminated.
This extension is enabled by the :setting:`MEMUSAGE_ENABLED` setting and
@ -202,7 +202,8 @@ Memory debugger extension
An extension for debugging memory usage. It collects information about:
* objects uncollected by the Python garbage collector
* objects left alive that shouldn't. For more info, see :ref:`topics-leaks-trackrefs`
* objects left alive that shouldn't be. For more info, see
:ref:`topics-leaks-trackrefs`
To enable this extension, turn on the :setting:`MEMDEBUG_ENABLED` setting. The
info will be stored in the stats.
@ -255,8 +256,8 @@ settings:
.. note::
When a certain closing condition is met, requests which are
currently in the downloader queue (up to :setting:`CONCURRENT_REQUESTS`
When a certain closing condition is met, requests that are currently in the
downloader queue (up to :setting:`CONCURRENT_REQUESTS`
requests) are still processed.
.. setting:: CLOSESPIDER_TIMEOUT
@ -268,8 +269,8 @@ Default: ``0``
An integer which specifies a number of seconds. If the spider remains open for
more than that number of seconds, it will be automatically closed with the
reason ``closespider_timeout``. If zero (or non set), spiders won't be closed by
timeout.
reason ``closespider_timeout``. If zero (or not set), spiders won't be closed
by timeout.
.. setting:: CLOSESPIDER_TIMEOUT_NO_ITEM
@ -280,8 +281,8 @@ Default: ``0``
An integer which specifies a number of seconds. If the spider has not produced
any items in the last number of seconds, it will be closed with the reason
``closespider_timeout_no_item``. If zero (or non set), spiders won't be closed
regardless if it hasn't produced any items.
``closespider_timeout_no_item``. If zero (or not set), spiders won't be closed
even if they have not produced any items.
.. setting:: CLOSESPIDER_ITEMCOUNT
@ -291,9 +292,9 @@ CLOSESPIDER_ITEMCOUNT
Default: ``0``
An integer which specifies a number of items. If the spider scrapes more than
that amount and those items are passed by the item pipeline, the
spider will be closed with the reason ``closespider_itemcount``.
If zero (or non set), spiders won't be closed by number of passed items.
that amount and those items are passed by the item pipeline, the spider will be
closed with the reason ``closespider_itemcount``. If zero (or not set), spiders
won't be closed by the number of passed items.
.. setting:: CLOSESPIDER_PAGECOUNT
@ -304,7 +305,7 @@ Default: ``0``
An integer which specifies the maximum number of responses to crawl. If the spider
crawls more than that, the spider will be closed with the reason
``closespider_pagecount``. If zero (or non set), spiders won't be closed by
``closespider_pagecount``. If zero (or not set), spiders won't be closed by the
number of crawled responses.
.. setting:: CLOSESPIDER_PAGECOUNT_NO_ITEM
@ -318,7 +319,7 @@ An integer which specifies the maximum number of consecutive responses to crawl
without items scraped. If the spider crawls more consecutive responses than that
and no items are scraped in the meantime, the spider will be closed with the
reason ``closespider_pagecount_no_item``. If zero (or not set), spiders won't be
closed by number of crawled responses with no items.
closed by the number of crawled responses with no items.
.. setting:: CLOSESPIDER_ERRORCOUNT
@ -329,8 +330,8 @@ Default: ``0``
An integer which specifies the maximum number of errors to receive before
closing the spider. If the spider generates more than that number of errors,
it will be closed with the reason ``closespider_errorcount``. If zero (or non
set), spiders won't be closed by number of errors.
it will be closed with the reason ``closespider_errorcount``. If zero (or not
set), spiders won't be closed by the number of errors.
StatsMailer extension
~~~~~~~~~~~~~~~~~~~~~
@ -341,7 +342,7 @@ StatsMailer extension
.. class:: StatsMailer
This simple extension can be used to send a notification e-mail every time a
domain has finished scraping, including the Scrapy stats collected. The email
domain has finished scraping, including the Scrapy stats collected. The e-mail
will be sent to all recipients specified in the :setting:`STATSMAILER_RCPTS`
setting.

View File

@ -5,9 +5,9 @@ Feed exports
============
One of the most frequently required features when implementing scrapers is
being able to store the scraped data properly and, quite often, that means
generating an "export file" with the scraped data (commonly called "export
feed") to be consumed by other systems.
properly storing the scraped data and, quite often, that means generating an
"export file" with the scraped data (commonly called an "export feed") to be
consumed by other systems.
Scrapy provides this functionality out of the box with the Feed Exports, which
allows you to generate feeds with the scraped items, using multiple
@ -31,7 +31,7 @@ For serializing the scraped data, the feed exports use the :ref:`Item exporters
- :ref:`topics-feed-format-csv`
- :ref:`topics-feed-format-xml`
But you can also extend the supported format through the
But you can also extend the supported formats through the
:setting:`FEED_EXPORTERS` setting.
.. _topics-feed-format-json:
@ -98,9 +98,9 @@ Marshal
Storages
========
When using the feed exports you define where to store the feed using one or multiple URIs_
(through the :setting:`FEEDS` setting). The feed exports supports multiple
storage backend types which are defined by the URI scheme.
When using the feed exports you define where to store the feed using one or
multiple URIs_ (through the :setting:`FEEDS` setting). The feed exports support
multiple storage backend types that are defined by the URI scheme.
The storages backends supported out of the box are:
@ -126,8 +126,8 @@ being created. These parameters are:
- ``%(time)s`` - gets replaced by a timestamp when the feed is being created
- ``%(name)s`` - gets replaced by the spider name
Any other named parameter gets replaced by the spider attribute of the same
name. For example, ``%(site_id)s`` would get replaced by the ``spider.site_id``
Any other named parameter is replaced by the spider attribute of the same name.
For example, ``%(site_id)s`` would be replaced by the ``spider.site_id``
attribute the moment the feed is being created.
Here are some examples to illustrate:
@ -160,16 +160,16 @@ The feeds are stored in the local filesystem.
- Example URI: ``file:///tmp/export.csv``
- Required external libraries: none
Note that for the local filesystem storage (only) you can omit the scheme if
you specify an absolute path like ``/tmp/export.csv`` (Unix systems only).
Alternatively you can also use a :class:`pathlib.Path` object.
Note that for the local filesystem storage you can omit the scheme if you
specify an absolute path like ``/tmp/export.csv`` (Unix systems only).
Alternatively, you can use a :class:`pathlib.Path` object.
.. _topics-feed-storage-ftp:
FTP
---
The feeds are stored in a FTP server.
The feeds are stored on an FTP server.
- URI scheme: ``ftp``
- Example URI: ``ftp://user:pass@ftp.example.com/path/to/export.csv``

View File

@ -7,10 +7,10 @@ Item Pipeline
After an item has been scraped by a spider, it is sent to the Item Pipeline
which processes it through several components that are executed sequentially.
Each item pipeline component (sometimes referred as just "Item Pipeline") is a
Python class that implements a simple method. They receive an item and perform
an action over it, also deciding if the item should continue through the
pipeline or be dropped and no longer processed.
Each item pipeline component (sometimes referred to as just "Item Pipeline") is
a Python class that implements a simple method. Each component receives an item
and performs an action on it while deciding whether the item should continue
through the pipeline or be dropped and no longer processed.
Typical uses of item pipelines are:
@ -28,7 +28,7 @@ implement the following method:
.. method:: process_item(self, item)
This method is called for every item pipeline component.
Scrapy calls this method for every item processed by the pipeline component.
`item` is an :ref:`item object <item-types>`, see
:ref:`supporting-item-types`.
@ -42,7 +42,7 @@ implement the following method:
:param item: the scraped item
:type item: :ref:`item object <item-types>`
Additionally, they may also implement the following methods:
Additionally, a component may implement the following methods:
.. method:: open_spider(self)
@ -61,8 +61,8 @@ Price validation and dropping items with no prices
Let's take a look at the following hypothetical pipeline that adjusts the
``price`` attribute for those items that do not include VAT
(``price_excludes_vat`` attribute), and drops those items which don't
contain a price:
(``price_excludes_vat`` attribute), and drops those items that don't contain a
price:
.. code-block:: python
@ -109,16 +109,16 @@ format:
self.file.write(line)
return item
.. note:: The purpose of JsonWriterPipeline is just to introduce how to write
item pipelines. If you really want to store all scraped items into a JSON
file you should use the :ref:`Feed exports <topics-feed-exports>`.
.. note:: The JsonWriterPipeline example simply introduces how to write item
pipelines. If you really want to store all scraped items in a JSON file,
you should use the :ref:`Feed exports <topics-feed-exports>`.
Write items to MongoDB
----------------------
In this example we'll write items to MongoDB_ using pymongo_.
MongoDB address and database name are specified in Scrapy settings;
MongoDB collection is named after item class.
In this example, we'll write items to MongoDB_ using pymongo_. The MongoDB
address and database name are specified in the Scrapy settings; the MongoDB
collection is named after the item class.
The main point of this example is to show how to :ref:`get the crawler
<from-crawler>` and how to clean up the resources properly.
@ -161,13 +161,13 @@ The main point of this example is to show how to :ref:`get the crawler
.. _ScreenshotPipeline:
Take screenshot of item
-----------------------
Take a screenshot of an item
----------------------------
This example demonstrates how to use :doc:`coroutine syntax <coroutines>` in
the :meth:`process_item` method.
This item pipeline makes a request to a locally-running instance of Splash_ to
This item pipeline makes a request to a locally running instance of Splash_ to
render a screenshot of the item URL. After the request response is downloaded,
the item pipeline saves the screenshot to a file and adds the filename to the
item.
@ -184,12 +184,12 @@ item.
class ScreenshotPipeline:
"""Pipeline that uses Splash to render screenshot of
every Scrapy item."""
"""Pipeline that uses Splash to render a screenshot of every Scrapy
item."""
SPLASH_URL = "http://localhost:8050/render.png?url={}"
def __init__(crawler):
def __init__(self, crawler):
self.crawler = crawler
@classmethod
@ -204,16 +204,17 @@ item.
response = await self.crawler.engine.download_async(request)
if response.status != 200:
# Error happened, return item.
# An error occurred, so return the item.
return item
# Save screenshot to file, filename will be hash of url.
# Save the screenshot to a file; the filename is the hash of the
# URL.
url = adapter["url"]
url_hash = hashlib.md5(url.encode("utf8")).hexdigest()
filename = f"{url_hash}.png"
Path(filename).write_bytes(response.body)
# Store filename in item.
# Store the filename in the item.
adapter["screenshot_filename"] = filename
return item
@ -222,9 +223,9 @@ item.
Duplicates filter
-----------------
A filter that looks for duplicate items, and drops those items that were
already processed. Let's say that our items have a unique id, but our spider
returns multiples items with the same id:
This filter looks for duplicate items and drops those that were already
processed. Let's say that our items have a unique id, but our spider returns
multiple items with the same id:
.. code-block:: python

View File

@ -8,7 +8,7 @@ Items
:synopsis: Item and Field classes
The main goal in scraping is to extract structured data from unstructured
sources, typically, web pages. :ref:`Spiders <topics-spiders>` may return the
sources, typically web pages. :ref:`Spiders <topics-spiders>` may return the
extracted data as `items`, Python objects that define key-value pairs.
Scrapy supports :ref:`multiple types of items <item-types>`. When you create an
@ -21,9 +21,10 @@ receives an item, your code should :ref:`work for any item type
Item Types
==========
Scrapy supports the following types of items, via the `itemadapter`_ library:
Scrapy supports the following types of items via the `itemadapter`_ library:
:ref:`dictionaries <dict-items>`, :ref:`Item objects <item-objects>`,
:ref:`dataclass objects <dataclass-items>`, and :ref:`attrs objects <attrs-items>`.
:ref:`dataclass objects <dataclass-items>`, and :ref:`attrs objects
<attrs-items>`.
.. _itemadapter: https://github.com/scrapy/itemadapter
@ -49,16 +50,16 @@ make it the most feature-complete item type:
:class:`Item` objects replicate the standard :class:`dict` API, including
its ``__init__`` method.
:class:`Item` allows the defining of field names, so that:
:class:`Item` allows you to define field names, so that:
- :class:`KeyError` is raised when using undefined field names (i.e.
prevents typos going unnoticed)
- :class:`KeyError` is raised when using undefined field names (i.e. prevents
typos from going unnoticed)
- :ref:`Item exporters <topics-exporters>` can export all fields by
default even if the first scraped object does not have values for all
of them
:class:`Item` also allows the defining of field metadata, which can be used to
:class:`Item` also lets you define field metadata, which can be used to
:ref:`customize serialization <topics-exporters-field-serialization>`.
:mod:`trackref` tracks :class:`Item` objects to help find memory leaks
@ -82,11 +83,12 @@ Dataclass objects
.. versionadded:: 2.2
:func:`~dataclasses.dataclass` allows the defining of item classes with field names,
so that :ref:`item exporters <topics-exporters>` can export all fields by
default even if the first scraped object does not have values for all of them.
:func:`~dataclasses.dataclass` allows you to define item classes with field
names, so that :ref:`item exporters <topics-exporters>` can export all fields
by default even if the first scraped object does not have values for all of
them.
Additionally, ``dataclass`` items also allow you to:
Additionally, ``dataclass`` items allow you to:
* define the type and default value of each defined field.
@ -114,18 +116,18 @@ attr.s objects
.. versionadded:: 2.2
:func:`attr.s` allows the defining of item classes with field names,
so that :ref:`item exporters <topics-exporters>` can export all fields by
default even if the first scraped object does not have values for all of them.
:func:`attr.s` allows you to define item classes with field names, so that
:ref:`item exporters <topics-exporters>` can export all fields by default even
if the first scraped object does not have values for all of them.
Additionally, ``attr.s`` items also allow to:
Additionally, ``attr.s`` items allow you to:
* define the type and default value of each defined field.
* define custom field :ref:`metadata <attrs:metadata>`, which can be used to
:ref:`customize serialization <topics-exporters-field-serialization>`.
In order to use this type, the :doc:`attrs package <attrs:index>` needs to be installed.
To use this type, the :doc:`attrs package <attrs:index>` needs to be installed.
Example:
@ -163,9 +165,9 @@ Item subclasses are declared using a simple class definition syntax and
tags = scrapy.Field()
last_updated = scrapy.Field(serializer=str)
.. note:: Those familiar with `Django`_ will notice that Scrapy Items are
declared similar to `Django Models`_, except that Scrapy Items are much
simpler as there is no concept of different field types.
.. note:: Those familiar with `Django`_ will notice that Scrapy items are
declared similarly to `Django Models`_, except that Scrapy items are much
simpler as there is no concept of different field types.
.. _Django: https://www.djangoproject.com/
.. _Django Models: https://docs.djangoproject.com/en/dev/topics/db/models/
@ -177,19 +179,19 @@ Declaring fields
----------------
:class:`Field` objects are used to specify metadata for each field. For
example, the serializer function for the ``last_updated`` field illustrated in
the example above.
example, they can store the serializer function for the ``last_updated`` field
illustrated above.
You can specify any kind of metadata for each field. There is no restriction on
the values accepted by :class:`Field` objects. For this same
reason, there is no reference list of all available metadata keys. Each key
defined in :class:`Field` objects could be used by a different component, and
only those components know about it. You can also define and use any other
:class:`Field` key in your project too, for your own needs. The main goal of
:class:`Field` objects is to provide a way to define all field metadata in one
place. Typically, those components whose behaviour depends on each field use
certain field keys to configure that behaviour. You must refer to their
documentation to see which metadata keys are used by each component.
the values accepted by :class:`Field` objects. For this same reason, there is
no reference list of all available metadata keys. Each key defined in
:class:`Field` objects could be used by a different component, and only those
components know about it. You can also define and use any other :class:`Field`
key in your project too, for your own needs. The main goal of :class:`Field`
objects is to provide a way to define all field metadata in one place.
Typically, those components whose behavior depends on each field use certain
field keys to configure that behavior. You must refer to their documentation to
see which metadata keys are used by each component.
It's important to note that the :class:`Field` objects used to declare the item
do not stay assigned as class attributes. Instead, they can be accessed through
@ -217,7 +219,7 @@ Working with Item objects
.. skip: start
Here are some examples of common tasks performed with items, using the
``Product`` item :ref:`declared above <topics-items-declaring>`. You will
``Product`` item :ref:`declared above <topics-items-declaring>`. You will
notice the API is very similar to the :class:`dict` API.
Creating items
@ -341,7 +343,9 @@ Creating dicts from items:
>>> dict(product) # create a dict from all populated values
{'price': 1000, 'name': 'Desktop PC'}
Creating items from dicts:
Creating items from dicts:
.. code-block:: pycon
>>> Product({"name": "Laptop PC", "price": 1500})
Product(price=1500, name='Laptop PC')
@ -355,8 +359,8 @@ Creating dicts from items:
Extending Item subclasses
-------------------------
You can extend Items (to add more fields or to change some metadata for some
fields) by declaring a subclass of your original Item.
You can extend items (to add more fields or to change some metadata for some
fields) by declaring a subclass of your original item.
For example:
@ -387,7 +391,7 @@ Supporting All Item Types
In code that receives an item, such as methods of :ref:`item pipelines
<topics-item-pipeline>` or :ref:`spider middlewares
<topics-spider-middleware>`, it is a good practice to use the
<topics-spider-middleware>`, it is good practice to use the
:class:`~itemadapter.ItemAdapter` class to write code that works for any
supported item type.

View File

@ -7,7 +7,7 @@ Jobs: pausing and resuming crawls
Sometimes, for big sites, it's desirable to pause crawls and be able to resume
them later.
Scrapy supports this functionality out of the box by providing the following
Scrapy supports this functionality out of the box through the following
facilities:
* a scheduler that persists scheduled requests on disk
@ -20,12 +20,11 @@ facilities:
Job directory
=============
To enable persistence support you just need to define a *job directory* through
the ``JOBDIR`` setting. This directory will be for storing all required data to
keep the state of a single job (i.e. a spider run). It's important to note that
this directory must not be shared by different spiders, or even different
jobs/runs of the same spider, as it's meant to be used for storing the state of
a *single* job.
To enable persistence support, define a *job directory* through the ``JOBDIR``
setting. This directory stores all required data to keep the state of a single
job (i.e. a spider run). It's important to note that this directory must not be
shared with different spiders or with other runs of the same spider, as it's
meant to store the state of a *single* job.
How to use it
=============
@ -34,7 +33,7 @@ To start a spider with persistence support enabled, run it like this::
scrapy crawl somespider -s JOBDIR=crawls/somespider-1
Then, you can stop the spider safely at any time (by pressing Ctrl-C or sending
Then you can stop the spider safely at any time (by pressing Ctrl-C or sending
a signal), and resume it later by issuing the same command::
scrapy crawl somespider -s JOBDIR=crawls/somespider-1
@ -44,11 +43,11 @@ a signal), and resume it later by issuing the same command::
Keeping persistent state between batches
========================================
Sometimes you'll want to keep some persistent spider state between pause/resume
batches. You can use the ``spider.state`` attribute for that, which should be a
dict. There's :ref:`a built-in extension <topics-extensions-ref-spiderstate>`
that takes care of serializing, storing and loading that attribute from the job
directory, when the spider starts and stops.
Sometimes you'll want to keep persistent spider state between pause/resume
batches. Use the ``spider.state`` attribute for that. It should be a dict.
There's :ref:`a built-in extension <topics-extensions-ref-spiderstate>` that
takes care of serializing, storing and loading that attribute from the job
directory when the spider starts and stops.
Here's an example of a callback that uses the spider state (other spider code
is omitted for brevity):
@ -62,14 +61,14 @@ is omitted for brevity):
Persistence gotchas
===================
There are a few things to keep in mind if you want to be able to use the Scrapy
persistence support:
There are a few things to keep in mind if you want to use Scrapy's persistence
support:
Cookies expiration
------------------
Cookie expiration
-----------------
Cookies may expire. So, if you don't resume your spider quickly the requests
scheduled may no longer work. This won't be an issue if your spider doesn't rely
Cookies may expire, so if you don't resume your spider quickly, the scheduled
requests may no longer work. This won't be an issue if your spider doesn't rely
on cookies.

View File

@ -5,55 +5,53 @@ Debugging memory leaks
======================
In Scrapy, objects such as requests, responses and items have a finite
lifetime: they are created, used for a while, and finally destroyed.
lifetime: they are created, used for a while and finally destroyed.
From all those objects, the Request is probably the one with the longest
lifetime, as it stays waiting in the Scheduler queue until it's time to process
it. For more info see :ref:`topics-architecture`.
Of all those objects, the Request usually has the longest lifetime because it
waits in the Scheduler queue until it's time to process it. For more
information, see :ref:`topics-architecture`.
As these Scrapy objects have a (rather long) lifetime, there is always the risk
of accumulating them in memory without releasing them properly and thus causing
what is known as a "memory leak".
Because these Scrapy objects have a comparatively long lifetime, there is
always the risk of accumulating them in memory without releasing them properly
and thus causing what is known as a "memory leak".
To help debugging memory leaks, Scrapy provides a built-in mechanism for
tracking objects references called :ref:`trackref <topics-leaks-trackrefs>`,
and you can also use a third-party library called :ref:`muppy
<topics-leaks-muppy>` for more advanced memory debugging (see below for more
info). Both mechanisms must be used from the :ref:`Telnet Console
<topics-telnetconsole>`.
To help debug memory leaks, Scrapy provides a built-in mechanism for tracking
object references called :ref:`trackref <topics-leaks-trackrefs>`, and you can
also use a third-party library called :ref:`muppy <topics-leaks-muppy>` for
more advanced memory debugging (see below for more information). Both
mechanisms must be used from the :ref:`Telnet Console <topics-telnetconsole>`.
Common causes of memory leaks
=============================
It happens quite often (sometimes by accident, sometimes on purpose) that the
Scrapy developer passes objects referenced in Requests (for example, using the
:attr:`~scrapy.Request.cb_kwargs` or :attr:`~scrapy.Request.meta`
attributes or the request callback function) and that effectively bounds the
lifetime of those referenced objects to the lifetime of the Request. This is,
by far, the most common cause of memory leaks in Scrapy projects, and a quite
difficult one to debug for newcomers.
Scrapy developer passes objects referenced in requests—for example, through the
:attr:`~scrapy.Request.cb_kwargs` or :attr:`~scrapy.Request.meta` attributes or
the request callback function—and that effectively ties the lifetime of those
referenced objects to the lifetime of the request. This is, by far, the most
common cause of memory leaks in Scrapy projects, and it can be quite difficult
for newcomers to debug.
In big projects, the spiders are typically written by different people and some
of those spiders could be "leaking" and thus affecting the rest of the other
(well-written) spiders when they get to run concurrently, which, in turn,
affects the whole crawling process.
In big projects, spiders are typically written by different people, and some of
those spiders could be "leaking" and thus affecting the rest of the
(well-written) spiders when they run concurrently, which, in turn, affects the
whole crawling process.
The leak could also come from a custom middleware, pipeline or extension that
you have written, if you are not releasing the (previously allocated) resources
properly. For example, allocating resources on :signal:`spider_opened`
but not releasing them on :signal:`spider_closed` may cause problems if
you're running :ref:`multiple spiders per process <run-multiple-spiders>`.
The leak could also come from a custom middleware, pipeline, or extension that
you wrote if you are not releasing the previously allocated resources properly.
For example, allocating resources on :signal:`spider_opened` but not releasing
them on :signal:`spider_closed` may cause problems if you're running
:ref:`multiple spiders per process <run-multiple-spiders>`.
Too Many Requests?
------------------
By default Scrapy keeps the request queue in memory; it includes
:class:`~scrapy.Request` objects and all objects
referenced in Request attributes (e.g. in :attr:`~scrapy.Request.cb_kwargs`
and :attr:`~scrapy.Request.meta`).
While not necessarily a leak, this can take a lot of memory. Enabling
:ref:`persistent job queue <topics-jobs>` could help keeping memory usage
in control.
:class:`~scrapy.Request` objects and all objects referenced in request
attributes (for example, :attr:`~scrapy.Request.cb_kwargs` and
:attr:`~scrapy.Request.meta`). While this is not necessarily a leak, it can
consume a lot of memory. Enabling the :ref:`persistent job queue <topics-jobs>`
can help keep memory usage under control.
.. _topics-leaks-trackrefs:
@ -62,9 +60,9 @@ Debugging memory leaks with ``trackref``
.. skip: start
:mod:`trackref` is a module provided by Scrapy to debug the most common cases of
memory leaks. It basically tracks the references to all live Request,
Response, Item, Spider and Selector objects.
:mod:`trackref` is a module provided by Scrapy to debug the most common cases
of memory leaks. It tracks the references to all live Request, Response, Item,
Spider and Selector objects.
You can enter the telnet console and inspect how many objects (of the classes
mentioned above) are currently alive using the ``prefs()`` function which is an
@ -83,16 +81,15 @@ alias to the :func:`~scrapy.utils.trackref.print_live_refs` function::
FormRequest 878 oldest: 7s ago
As you can see, that report also shows the "age" of the oldest object in each
class. If you're running multiple spiders per process chances are you can
figure out which spider is leaking by looking at the oldest request or response.
You can get the oldest object of each class using the
class. If you're running multiple spiders per process, chances are you can
figure out which spider is leaking by looking at the oldest request or
response. You can get the oldest object of each class using the
:func:`~scrapy.utils.trackref.get_oldest` function (from the telnet console).
Which objects are tracked?
--------------------------
The objects tracked by ``trackrefs`` are all from these classes (and all its
subclasses):
``trackref`` tracks objects from these classes (and all their subclasses):
* :class:`scrapy.Request`
* :class:`scrapy.http.Response`
@ -109,9 +106,9 @@ Suppose we have some spider with a line similar to this one::
return Request(f"http://www.somenastyspider.com/product.php?pid={product_id}",
callback=self.parse, cb_kwargs={'referer': response})
That line is passing a response reference inside a request which effectively
ties the response lifetime to the requests' one, and that would definitely
cause memory leaks.
That line passes a response reference inside a request, which effectively ties
the response lifetime to the request's lifetime, and that will definitely cause
memory leaks.
Let's see how we can discover the cause (without knowing it
a priori, of course) by using the ``trackref`` tool.
@ -132,10 +129,10 @@ references:
The fact that there are so many live responses (and that they're so old) is
definitely suspicious, as responses should have a relatively short lifetime
compared to Requests. The number of responses is similar to the number
of requests, so it looks like they are tied in a some way. We can now go
and check the code of the spider to discover the nasty line that is
generating the leaks (passing response references inside requests).
compared to requests. The number of responses is similar to the number of
requests, so it looks like they are tied in some way. We can now check the code
of the spider to discover the line that is generating the leaks (passing
response references inside requests).
Sometimes extra information about live objects can be helpful.
Let's check the oldest response:
@ -147,8 +144,8 @@ Let's check the oldest response:
>>> r.url
'http://www.somenastyspider.com/product.php?pid=123'
If you want to iterate over all objects, instead of getting the oldest one, you
can use the :func:`scrapy.utils.trackref.iter_all` function:
If you want to iterate over all objects instead of getting only the oldest one,
you can use the :func:`scrapy.utils.trackref.iter_all` function:
.. code-block:: pycon
@ -161,11 +158,10 @@ can use the :func:`scrapy.utils.trackref.iter_all` function:
Too many spiders?
-----------------
If your project has too many spiders executed in parallel,
the output of :func:`prefs` can be difficult to read.
For this reason, that function has a ``ignore`` argument which can be used to
ignore a particular class (and all its subclasses). For
example, this won't show any live references to spiders:
If your project has too many spiders executed in parallel, the output of
:func:`prefs` can be difficult to read. For this reason, that function has an
``ignore`` argument that you can use to omit a particular class (and all its
subclasses). For example, this won't show any live references to spiders:
.. code-block:: pycon
@ -212,14 +208,13 @@ Here are the functions available in the :mod:`~scrapy.utils.trackref` module.
Debugging memory leaks with muppy
=================================
``trackref`` provides a very convenient mechanism for tracking down memory
leaks, but it only keeps track of the objects that are more likely to cause
memory leaks. However, there are other cases where the memory leaks could come
from other (more or less obscure) objects. If this is your case, and you can't
find your leaks using ``trackref``, you still have another resource: the muppy
library.
``trackref`` provides a convenient mechanism for tracking down memory leaks,
but it only keeps track of the objects that are more likely to cause them.
However, sometimes leaks come from other (more or less obscure) objects. If
that happens and you can't find your leaks using ``trackref``, you still have
another resource: the muppy library.
You can use muppy from `Pympler`_.
muppy is available as part of `Pympler`_.
.. _Pympler: https://pypi.org/project/Pympler/
@ -227,8 +222,8 @@ If you use ``pip``, you can install muppy with the following command::
pip install Pympler
Here's an example to view all Python objects available in
the heap using muppy:
Here's an example that shows all Python objects available in the heap using
muppy:
.. skip: start
.. code-block:: pycon
@ -260,7 +255,7 @@ the heap using muppy:
.. skip: end
For more info about muppy, refer to the `muppy documentation`_.
For more information about muppy, refer to the `muppy documentation`_.
.. _muppy documentation: https://pythonhosted.org/Pympler/muppy.html
@ -269,10 +264,10 @@ For more info about muppy, refer to the `muppy documentation`_.
Leaks without leaks
===================
Sometimes, you may notice that the memory usage of your Scrapy process will
only increase, but never decrease. Unfortunately, this could happen even
though neither Scrapy nor your project are leaking memory. This is due to a
(not so well) known problem of Python, which may not return released memory to
Sometimes you may notice that the memory usage of your Scrapy process only
increases and never decreases. Unfortunately, this could happen even though
neither Scrapy nor your project are leaking memory. This is due to a
not-so-well-known problem in Python, which may not return released memory to
the operating system in some cases. For more information on this issue see:
* `Python Memory Management <https://www.evanjones.ca/python-memory.html>`_
@ -293,6 +288,6 @@ completely. To quote the paper:
.. _this paper: https://www.evanjones.ca/memoryallocator/
To keep memory consumption reasonable you can split the job into several
smaller jobs or enable :ref:`persistent job queue <topics-jobs>`
and stop/start spider from time to time.
To keep memory consumption reasonable, you can split the job into several
smaller jobs or enable the :ref:`persistent job queue <topics-jobs>` and stop
and start the spider from time to time.

View File

@ -16,9 +16,10 @@ list of matching :class:`~scrapy.link.Link` objects from a
Link extractors are used in :class:`~scrapy.spiders.CrawlSpider` spiders
through a set of :class:`~scrapy.spiders.Rule` objects.
You can also use link extractors in regular spiders. For example, you can instantiate
:class:`LinkExtractor <scrapy.linkextractors.lxmlhtml.LxmlLinkExtractor>` into a class
variable in your spider, and use it from your spider callbacks:
You can also use link extractors in regular spiders. For example, you can
instantiate :class:`LinkExtractor
<scrapy.linkextractors.lxmlhtml.LxmlLinkExtractor>` as a class variable in your
spider and use it from your spider callbacks:
.. code-block:: python
@ -53,22 +54,22 @@ LxmlLinkExtractor
options. It is implemented using lxml's robust HTMLParser.
:param allow: a single regular expression (or list of regular expressions)
that the (absolute) urls must match in order to be extracted. If not
that the (absolute) URLs must match in order to be extracted. If not
given (or empty), it will match all links.
:type allow: str or list
:param deny: a single regular expression (or list of regular expressions)
that the (absolute) urls must match in order to be excluded (i.e. not
that the (absolute) URLs must match in order to be excluded (i.e. not
extracted). It has precedence over the ``allow`` parameter. If not
given (or empty) it won't exclude any links.
given (or empty), it won't exclude any links.
:type deny: str or list
:param allow_domains: a single value or a list of string containing
domains which will be considered for extracting the links
:param allow_domains: a single value or a list of strings containing
domains that will be considered for extracting the links.
:type allow_domains: str or list
:param deny_domains: a single value or a list of strings containing
domains which won't be considered for extracting the links
domains that won't be considered for extracting the links.
:type deny_domains: str or list
:param deny_extensions: a single value or list of strings containing
@ -82,15 +83,14 @@ LxmlLinkExtractor
``iso``, ``tar``, ``tar.gz``, ``webm``, and ``xz``.
:type deny_extensions: list
:param restrict_xpaths: is an XPath (or list of XPath's) which defines
regions inside the response where links should be extracted from.
If given, only the text selected by those XPath will be scanned for
links.
:param restrict_xpaths: an XPath (or list of XPaths) that defines regions
inside the response where links should be extracted. If given, only the
text selected by those XPaths will be scanned for links.
:type restrict_xpaths: str or list
:param restrict_css: a CSS selector (or list of selectors) which defines
regions inside the response where links should be extracted from.
Has the same behaviour as ``restrict_xpaths``.
:param restrict_css: a CSS selector (or list of selectors) that defines
regions inside the response where links should be extracted. It has the
same behaviour as ``restrict_xpaths``.
:type restrict_css: str or list
:param restrict_text: a single regular expression (or list of regular expressions)
@ -103,25 +103,25 @@ LxmlLinkExtractor
Defaults to ``('a', 'area')``.
:type tags: str or list
:param attrs: an attribute or list of attributes which should be considered when looking
:param attrs: an attribute or list of attributes that should be considered when looking
for links to extract (only for those tags specified in the ``tags``
parameter). Defaults to ``('href',)``
parameter). Defaults to ``('href',)``.
:type attrs: list
:param canonicalize: canonicalize each extracted url (using
w3lib.url.canonicalize_url). Defaults to ``False``.
Note that canonicalize_url is meant for duplicate checking;
it can change the URL visible at server side, so the response can be
different for requests with canonicalized and raw URLs. If you're
using LinkExtractor to follow links it is more robust to
keep the default ``canonicalize=False``.
:param canonicalize: canonicalize each extracted URL (using
w3lib.url.canonicalize_url). Defaults to ``False``. Note that
canonicalize_url is meant for duplicate checking; it can change the URL
visible at the server side, so the response can be different for
requests with canonicalized and raw URLs. If you're using LinkExtractor
to follow links, it is more robust to keep the default
``canonicalize=False``.
:type canonicalize: bool
:param unique: whether duplicate filtering should be applied to extracted
links.
:type unique: bool
:param process_value: a function which receives each value extracted from
:param process_value: a function that receives each value extracted from
the tag and attributes scanned and can modify the value and return a
new one, or return ``None`` to ignore the link altogether. If not
given, ``process_value`` defaults to ``lambda x: x``.
@ -146,12 +146,13 @@ LxmlLinkExtractor
:type process_value: collections.abc.Callable
:param strip: whether to strip whitespaces from extracted attributes.
According to HTML5 standard, leading and trailing whitespaces
must be stripped from ``href`` attributes of ``<a>``, ``<area>``
and many other elements, ``src`` attribute of ``<img>``, ``<iframe>``
elements, etc., so LinkExtractor strips space chars by default.
Set ``strip=False`` to turn it off (e.g. if you're extracting urls
from elements or attributes which allow leading/trailing whitespaces).
According to the HTML5 standard, leading and trailing whitespaces must
be stripped from ``href`` attributes of ``<a>``, ``<area>`` and many
other elements, the ``src`` attribute of ``<img>`` and ``<iframe>``
elements, etc., so LinkExtractor strips space characters by default.
Set ``strip=False`` to turn it off (for example, if you're extracting
URLs from elements or attributes that allow leading or trailing
whitespaces).
:type strip: bool
.. automethod:: extract_links

View File

@ -8,17 +8,18 @@ Item Loaders
:synopsis: Item Loader class
Item Loaders provide a convenient mechanism for populating scraped :ref:`items
<topics-items>`. Even though items can be populated directly, Item Loaders provide a
much more convenient API for populating them from a scraping process, by automating
some common tasks like parsing the raw extracted data before assigning it.
<topics-items>`. Even though items can be populated directly, Item Loaders
provide a more convenient API for populating them from a scraping process by
automating common tasks like parsing the raw extracted data before assigning
it.
In other words, :ref:`items <topics-items>` provide the *container* of
scraped data, while Item Loaders provide the mechanism for *populating* that
container.
Item Loaders are designed to provide a flexible, efficient and easy mechanism
for extending and overriding different field parsing rules, either by spider,
or by source format (HTML, XML, etc) without becoming a nightmare to maintain.
for extending and overriding different field parsing rules, either by spider or
by source format (HTML, XML, etc.) without becoming a nightmare to maintain.
.. note:: Item Loaders are an extension of the itemloaders_ library that make it
easier to work with Scrapy by adding support for
@ -76,13 +77,13 @@ data that will be assigned to the ``name`` field later.
Afterwards, similar calls are used for ``price`` and ``stock`` fields
(the latter using a CSS selector with the :meth:`~ItemLoader.add_css` method),
and finally the ``last_update`` field is populated directly with a literal value
and finally the ``last_updated`` field is populated directly with a literal value
(``today``) using a different method: :meth:`~ItemLoader.add_value`.
Finally, when all data is collected, the :meth:`ItemLoader.load_item` method is
called which actually returns the item populated with the data
previously extracted and collected with the :meth:`~ItemLoader.add_xpath`,
:meth:`~ItemLoader.add_css`, and :meth:`~ItemLoader.add_value` calls.
called, which returns the item populated with the data previously extracted and
collected with the :meth:`~ItemLoader.add_xpath`, :meth:`~ItemLoader.add_css`,
and :meth:`~ItemLoader.add_value` calls.
.. _topics-loaders-dataclass:
@ -118,15 +119,15 @@ Input and Output processors
===========================
An Item Loader contains one input processor and one output processor for each
(item) field. The input processor processes the extracted data as soon as it's
received (through the :meth:`~ItemLoader.add_xpath`, :meth:`~ItemLoader.add_css` or
:meth:`~ItemLoader.add_value` methods) and the result of the input processor is
collected and kept inside the ItemLoader. After collecting all data, the
:meth:`ItemLoader.load_item` method is called to populate and get the populated
:ref:`item object <topics-items>`. That's when the output processor is
called with the data previously collected (and processed using the input
processor). The result of the output processor is the final value that gets
assigned to the item.
field. The input processor processes the extracted data as soon as it's
received (through the :meth:`~ItemLoader.add_xpath`,
:meth:`~ItemLoader.add_css`, or :meth:`~ItemLoader.add_value` methods), and the
result of the input processor is collected and kept inside the Item Loader.
After collecting all data, :meth:`ItemLoader.load_item` is called to populate
and return the populated :ref:`item object <topics-items>`. That's when the
output processor is called with the data previously collected and processed by
the input processor. The result of the output processor is the final value that
gets assigned to the item.
Let's see an example to illustrate how the input and output processors are
called for a particular field (the same applies for any other field):
@ -152,25 +153,24 @@ So what happens is:
data collected in (1) (if any).
3. This case is similar to the previous ones, except that the data is extracted
from the ``css`` CSS selector, and passed through the same *input
processor* used in (1) and (2). The result of the input processor is appended to the
from the ``css`` CSS selector and passed through the same *input processor*
used in (1) and (2). The result of the input processor is appended to the
data collected in (1) and (2) (if any).
4. This case is also similar to the previous ones, except that the value to be
collected is assigned directly, instead of being extracted from a XPath
expression or a CSS selector.
However, the value is still passed through the input processors. In this
case, since the value is not iterable it is converted to an iterable of a
single element before passing it to the input processor, because input
processor always receive iterables.
expression or a CSS selector. However, the value is still passed through the
input processors. In this case, since the value is not iterable it is
converted to an iterable of a single element before passing it to the input
processor because input processors always receive iterables.
5. The data collected in steps (1), (2), (3) and (4) is passed through
the *output processor* of the ``name`` field.
The result of the output processor is the value assigned to the ``name``
field in the item.
It's worth noticing that processors are just callable objects, which are called
with the data to be parsed, and return a parsed value. So you can use any
It's worth noting that processors are just callable objects that are called
with the data to be parsed and return a parsed value. So you can use any
function as input or output processor. The only requirement is that they must
accept one (and only one) positional argument, which will be an iterable.
@ -179,9 +179,9 @@ accept one (and only one) positional argument, which will be an iterable.
.. note:: Both input and output processors must receive an iterable as their
first argument. The output of those functions can be anything. The result of
input processors will be appended to an internal list (in the Loader)
containing the collected values (for that field). The result of the output
processors is the value that will be finally assigned to the item.
input processors will be appended to an internal list (in the loader)
containing the collected values for that field. The result of the output
processors is the value that will be assigned to the item.
The other thing you need to keep in mind is that the values returned by input
processors are collected internally (in lists) and then passed to output

View File

@ -9,9 +9,9 @@ Logging
explicit calls to the Python standard logging. Keep reading to learn more
about the new logging system.
Scrapy uses :mod:`logging` for event logging. We'll
provide some simple examples to get you started, but for more advanced
use-cases it's strongly suggested to read thoroughly its documentation.
Scrapy uses :mod:`logging` for event logging. We'll provide some simple
examples to get you started, but for more advanced use-cases it's strongly
suggested to read its documentation thoroughly.
Logging works out of the box, and can be configured to some extent with the
Scrapy settings listed in :ref:`topics-logging-settings`.
@ -26,8 +26,8 @@ Scrapy from scripts as described in :ref:`run-from-script`.
Log levels
==========
Python's builtin logging defines 5 different levels to indicate the severity of a
given log message. Here are the standard ones, listed in decreasing order:
Python's built-in logging defines 5 different levels to indicate the severity
of a given log message. Here are the standard ones, listed in decreasing order:
1. ``logging.CRITICAL`` - for critical errors (highest severity)
2. ``logging.ERROR`` - for regular errors
@ -62,10 +62,10 @@ example, a common practice is to create different loggers for every module).
These loggers can be configured independently, and they allow hierarchical
constructions.
The previous examples use the root logger behind the scenes, which is a top level
logger where all messages are propagated to (unless otherwise specified). Using
``logging`` helpers is merely a shortcut for getting the root logger
explicitly, so this is also an equivalent of the last snippets:
The previous examples use the root logger behind the scenes, which is a
top-level logger where all messages are propagated to (unless otherwise
specified). Using ``logging`` helpers is merely a shortcut for getting the root
logger explicitly, so this is also an equivalent of the last snippets:
.. code-block:: python
@ -85,8 +85,8 @@ You can use a different logger just by getting its name with the
logger.warning("This is a warning")
Finally, you can ensure having a custom logger for any module you're working on
by using the ``__name__`` variable, which is populated with current module's
path:
by using the ``__name__`` variable, which is populated with the current
module's path:
.. code-block:: python
@ -284,9 +284,8 @@ filter out unwanted messages:
if match:
return False
A project-level filter may be attached to the root
handler created by Scrapy, this is a wieldy way to
filter all loggers in different parts of the project
A project-level filter may be attached to the root handler created by Scrapy.
This is a useful way to filter all loggers in different parts of the project
(middlewares, spider, etc.):
.. code-block:: python

View File

@ -14,15 +14,15 @@ typically you'll either use the Files Pipeline or the Images Pipeline.
Both pipelines implement these features:
* Avoid re-downloading media that was downloaded recently
* Specifying where to store the media (filesystem directory, FTP server, Amazon S3 bucket,
Google Cloud Storage bucket)
* Avoid re-downloading media that have been downloaded recently
* Specify where to store the media (filesystem directory, FTP server, Amazon S3
bucket, Google Cloud Storage bucket)
The Images Pipeline has a few extra functions for processing images:
* Convert all downloaded images to a common format (JPG) and mode (RGB)
* Convert all downloaded images to a common format (JPEG) and mode (RGB)
* Thumbnail generation
* Check images width/height to make sure they meet a minimum constraint
* Check images' width/height to make sure they meet a minimum constraint
The pipelines also keep an internal queue of those media URLs which are currently
being scheduled for download, and connect those responses that arrive containing
@ -45,15 +45,15 @@ this:
Scrapy scheduler and downloader (which means the scheduler and downloader
middlewares are reused), but with a higher priority, processing them before other
pages are scraped. The item remains "locked" at that particular pipeline stage
until the files have finish downloading (or fail for some reason).
until the files have finished downloading (or fail for some reason).
4. When the files are downloaded, another field (``files``) will be populated
with the results. This field will contain a list of dicts with information
about the downloaded files, such as the downloaded path, the original
scraped url (taken from the ``file_urls`` field), the file checksum and the file status.
The files in the list of the ``files`` field will retain the same order of
the original ``file_urls`` field. If some file failed downloading, an
error will be logged and the file won't be present in the ``files`` field.
about the downloaded files, such as the downloaded path, the original
scraped URL (taken from the ``file_urls`` field), the file checksum and the
file status. The files in the ``files`` field will retain the same order as
in the original ``file_urls`` field. If some file failed downloading, an
error will be logged and the file won't be present in the ``files`` field.
.. _images-pipeline:
@ -261,8 +261,8 @@ policy:
For more information, see `canned ACLs`_ in the Amazon S3 Developer Guide.
You can also use other S3-like storages. Storages like self-hosted `Minio`_ or
`Zenko CloudServer`_. All you need to do is set endpoint option in you Scrapy
settings:
`Zenko CloudServer`_. All you need to do is set the endpoint option in your
Scrapy settings:
.. code-block:: python
@ -406,13 +406,14 @@ specifies the delay in number of days:
The default value for both settings is 90 days.
If you have pipeline that subclasses FilesPipeline and you'd like to have
different setting for it you can set setting keys preceded by uppercase
class name. E.g. given pipeline class called MyPipeline you can set setting key:
If you have a pipeline that subclasses FilesPipeline and you'd like to have a
different setting for it, you can set setting keys preceded by the uppercase
class name. For example, given a pipeline class called MyPipeline you can set
the setting key:
MYPIPELINE_FILES_EXPIRES = 180
and pipeline class MyPipeline will have expiration time set to 180.
and the pipeline class MyPipeline will have an expiration time set to 180.
The last modified time from the file is used to determine the age of the file in days,
which is then compared to the set expiration time to determine if the file is expired.

View File

@ -7,8 +7,8 @@ Requests and Responses
.. module:: scrapy.http
:synopsis: Request and Response classes
Scrapy uses :class:`~scrapy.Request` and :class:`Response` objects for crawling web
sites.
Scrapy uses :class:`~scrapy.Request` and :class:`Response` objects for crawling
websites.
Typically, :class:`~scrapy.Request` objects are generated in the spiders and pass
across the system until they reach the Downloader, which executes the request
@ -325,7 +325,7 @@ Using errbacks to catch exceptions in request processing
--------------------------------------------------------
The errback of a request is a function that will be called when an exception
is raise while processing it.
is raised while processing it.
It receives a :exc:`~twisted.python.failure.Failure` as first parameter and can
be used to track connection establishment timeouts, DNS errors etc.

View File

@ -6,10 +6,10 @@ Scheduler
.. module:: scrapy.core.scheduler
The scheduler component receives requests from the :ref:`engine <component-engine>`
and stores them into persistent and/or non-persistent data structures.
It also gets those requests and feeds them back to the engine when it
asks for a next request to be downloaded.
The scheduler component receives requests from the :ref:`engine
<component-engine>` and stores them into persistent and/or non-persistent data
structures. It also gets those requests and feeds them back to the engine when
it asks for the next request to be downloaded.
Overriding the default scheduler

View File

@ -26,13 +26,14 @@ used with HTML. `CSS`_ is a language for applying styles to HTML documents. It
defines selectors to associate those styles with specific HTML elements.
.. note::
Scrapy Selectors is a thin wrapper around `parsel`_ library; the purpose of
this wrapper is to provide better integration with Scrapy Response objects.
Scrapy selectors are a thin wrapper around the `parsel`_ library; the
purpose of this wrapper is to provide better integration with Scrapy
Response objects.
`parsel`_ is a stand-alone web scraping library which can be used without
Scrapy. It uses `lxml`_ library under the hood, and implements an
easy API on top of lxml API. It means Scrapy selectors are very similar
in speed and parsing accuracy to lxml.
Scrapy. It uses the `lxml`_ library under the hood and implements an easy
API on top of the lxml API. This means Scrapy selectors have similar speed
and parsing accuracy to lxml.
.. _BeautifulSoup: https://www.crummy.com/software/BeautifulSoup/
.. _lxml: https://lxml.de/

View File

@ -4,7 +4,7 @@
Settings
========
The Scrapy settings allows you to customize the behaviour of all Scrapy
The Scrapy settings allow you to customize the behaviour of all Scrapy
components, including the core, extensions, pipelines and spiders themselves.
The infrastructure of the settings provides a global namespace of key-value mappings
@ -1058,12 +1058,12 @@ Default: ``True``
Whether or not to fail on broken responses, that is, declared
``Content-Length`` does not match content sent by the server or chunked
response was not properly finish. If ``True``, these responses raise a
``ResponseFailed([_DataLoss])`` error. If ``False``, these responses
are passed through and the flag ``dataloss`` is added to the response, i.e.:
``'dataloss' in response.flags`` is ``True``.
response was not properly finished. If ``True``, these responses raise a
``ResponseFailed([_DataLoss])`` error. If ``False``, these responses are passed
through and the flag ``dataloss`` is added to the response, i.e.: ``'dataloss'
in response.flags`` is ``True``.
Optionally, this can be set per-request basis by using the
Optionally, this can be set on a per-request basis by using the
:reqmeta:`download_fail_on_dataloss` Request.meta key to ``False``.
.. note::

View File

@ -14,14 +14,14 @@ and what data they extract from the web pages you're trying to scrape. It
allows you to interactively test your expressions while you're writing your
spider, without having to run the spider to test every change.
Once you get familiarized with the Scrapy shell, you'll see that it's an
Once you become familiar with the Scrapy shell, you'll see that it's an
invaluable tool for developing and debugging your spiders.
Configuring the shell
=====================
If you have `IPython`_ installed, the Scrapy shell will use it (instead of the
standard Python console). The `IPython`_ console is much more powerful and
If you have `IPython`_ installed, the Scrapy shell will use it instead of the
standard Python console. The `IPython`_ console is much more powerful and
provides smart auto-completion and colorized output, among other things.
We highly recommend you install `IPython`_, especially if you're working on
@ -31,10 +31,10 @@ for more info.
Scrapy also has support for `bpython`_, and will try to use it where `IPython`_
is unavailable.
Through Scrapy's settings you can configure it to use any one of
``ipython``, ``bpython`` or the standard ``python`` shell, regardless of which
are installed. This is done by setting the ``SCRAPY_PYTHON_SHELL`` environment
variable; or by defining it in your :ref:`scrapy.cfg <topics-config-settings>`::
Through Scrapy's settings you can configure it to use any one of ``ipython``,
``bpython`` or the standard ``python`` shell, regardless of which are
installed. This is done by setting the ``SCRAPY_PYTHON_SHELL`` environment
variable or by defining it in your :ref:`scrapy.cfg <topics-config-settings>`::
[settings]
shell = bpython

View File

@ -143,7 +143,7 @@ scheduler_empty
Sent whenever the engine asks for a pending request from the
:ref:`scheduler <topics-scheduler>` (i.e. calls its
:meth:`~scrapy.core.scheduler.BaseScheduler.next_request` method) and the
scheduler returns none.
scheduler returns None.
See :ref:`start-requests-lazy` for an example.
@ -154,12 +154,11 @@ Item signals
------------
.. note::
As at max :setting:`CONCURRENT_ITEMS` items are processed in
parallel, many deferreds are fired together using
:class:`~twisted.internet.defer.DeferredList`. Hence the next
batch waits for the :class:`~twisted.internet.defer.DeferredList`
to fire and then runs the respective item signal handler for
the next batch of scraped items.
At most :setting:`CONCURRENT_ITEMS` items are processed in parallel, many
deferreds are fired together using
:class:`~twisted.internet.defer.DeferredList`. Hence the next batch waits
for the :class:`~twisted.internet.defer.DeferredList` to fire and then runs
the respective item signal handler for the next batch of scraped items.
item_scraped
~~~~~~~~~~~~
@ -251,12 +250,12 @@ spider_closed
:param spider: the spider which has been closed
:type spider: :class:`~scrapy.Spider` object
:param reason: a string which describes the reason why the spider was closed. If
it was closed because the spider has completed scraping, the reason
is ``'finished'``. Otherwise, if the spider was manually closed by
calling the ``close_spider`` engine method, then the reason is the one
passed in the ``reason`` argument of that method (which defaults to
``'cancelled'``). If the engine was shutdown (for example, by hitting
:param reason: a string which describes the reason why the spider was
closed. If it was closed because the spider has completed scraping, the
reason is ``'finished'``. Otherwise, if the spider was manually closed
by calling the ``close_spider`` engine method, then the reason is the
one passed in the ``reason`` argument of that method (which defaults to
``'cancelled'``). If the engine was shut down (for example, by hitting
Ctrl-C to stop it) the reason will be ``'shutdown'``.
:type reason: str
@ -407,11 +406,11 @@ request_reached_downloader
.. signal:: request_reached_downloader
.. function:: request_reached_downloader(request, spider)
Sent when a :class:`~scrapy.Request` reached downloader.
Sent when a :class:`~scrapy.Request` reached the downloader.
This signal does not support :ref:`asynchronous handlers <signal-deferred>`.
:param request: the request that reached downloader
:param request: the request that reached the downloader
:type request: :class:`~scrapy.Request` object
:param spider: the spider that yielded the request
@ -445,10 +444,10 @@ bytes_received
.. versionadded:: 2.2
Sent by the HTTP 1.1 and S3 download handlers when a group of bytes is
received for a specific request. This signal might be fired multiple
times for the same request, with partial data each time. For instance,
a possible scenario for a 25 kb response would be two signals fired
with 10 kb of data, and a final one with 5 kb of data.
received for a specific request. This signal might be fired multiple times
for the same request, with partial data each time. For instance, a possible
scenario for a 25 KB response would be two signals fired with 10 KB of
data, and a final one with 5 KB of data.
Handlers for this signal can stop the download of a response while it
is in progress by raising the :exc:`~scrapy.exceptions.StopDownload`

View File

@ -4,10 +4,10 @@
Spider Middleware
=================
The spider middleware is a framework of hooks into Scrapy's spider processing
mechanism where you can plug custom functionality to process the responses that
are sent to :ref:`topics-spiders` for processing and to process the requests
and items that are generated from spiders.
Spider middleware is a framework of hooks into Scrapy's spider processing
mechanism where you can plug in custom functionality to process the responses
that are sent to :ref:`topics-spiders` for processing, and to process the
requests and items that are generated by spiders.
.. _topics-spider-middleware-setting:
@ -16,7 +16,7 @@ Activating a spider middleware
To activate a spider middleware component, add it to the
:setting:`SPIDER_MIDDLEWARES` setting, which is a dict whose keys are the
middleware class path and their values are the middleware orders.
middleware class paths and whose values are the middleware orders.
Here's an example:
@ -43,10 +43,10 @@ you want to insert the middleware. The order does matter because each
middleware performs a different action and your middleware could depend on some
previous (or subsequent) middleware being applied.
If you want to disable a builtin middleware (the ones defined in
:setting:`SPIDER_MIDDLEWARES_BASE`, and enabled by default) you must define it
in your project :setting:`SPIDER_MIDDLEWARES` setting and assign ``None`` as its
value. For example, if you want to disable the off-site middleware:
If you want to disable a built-in middleware (the ones defined in
:setting:`SPIDER_MIDDLEWARES_BASE` and enabled by default) you must define it
in your project :setting:`SPIDER_MIDDLEWARES` setting and assign ``None`` as
its value. For example, if you want to disable the off-site middleware:
.. code-block:: python

View File

@ -71,7 +71,7 @@ scrapy.Spider
:class:`~scrapy.downloadermiddlewares.offsite.OffsiteMiddleware` is
enabled.
Let's say your target url is ``https://www.example.com/1.html``,
Let's say your target URL is ``https://www.example.com/1.html``,
then add ``'example.com'`` to the list.
.. autoattribute:: start_urls
@ -92,8 +92,8 @@ scrapy.Spider
:class:`~scrapy.crawler.Crawler` object to which this spider instance is
bound.
Crawlers encapsulate a lot of components in the project for their single
entry access (such as extensions, middlewares, signals managers, etc).
Crawlers encapsulate a lot of components in the project for single-entry
access (such as extensions, middlewares, signal managers, etc).
See :ref:`topics-api-crawler` to know more about them.
.. attribute:: settings
@ -318,9 +318,8 @@ Spiders can access arguments in their `__init__` methods:
self.start_urls = [f"http://www.example.com/categories/{category}"]
# ...
The default `__init__` method will take any spider arguments
and copy them to the spider as attributes.
The above example can also be written as follows:
The default ``__init__`` method will take any spider arguments and copy them to
the spider as attributes. The above example can also be written as follows:
.. code-block:: python
@ -344,15 +343,13 @@ specify spider arguments when calling
process = CrawlerProcess()
process.crawl(MySpider, category="electronics")
Keep in mind that spider arguments are only strings.
The spider will not do any parsing on its own.
If you were to set the ``start_urls`` attribute from the command line,
you would have to parse it on your own into a list
using something like :func:`ast.literal_eval` or :func:`json.loads`
and then set it as an attribute.
Otherwise, you would cause iteration over a ``start_urls`` string
(a very common python pitfall)
resulting in each character being seen as a separate url.
Keep in mind that spider arguments are only strings. The spider will not do any
parsing on its own. If you were to set the ``start_urls`` attribute from the
command line, you would have to parse it on your own into a list using
something like :func:`ast.literal_eval` or :func:`json.loads` and then set it
as an attribute. Otherwise, you would cause iteration over a ``start_urls``
string (a very common Python pitfall) resulting in each character being seen as
a separate url.
A valid use case is to set the http auth credentials
used by :class:`~scrapy.downloadermiddlewares.httpauth.HttpAuthMiddleware`

View File

@ -5,10 +5,11 @@ Stats Collection
================
Scrapy provides a convenient facility for collecting stats in the form of
key/values, where values are often counters. The facility is called the Stats
Collector, and can be accessed through the :attr:`~scrapy.crawler.Crawler.stats`
attribute of the :ref:`topics-api-crawler`, as illustrated by the examples in
the :ref:`topics-stats-usecases` section below.
key/value pairs, where values are often counters. The facility is called the
Stats Collector, and can be accessed through the
:attr:`~scrapy.crawler.Crawler.stats` attribute of the
:ref:`topics-api-crawler`, as illustrated by the examples in the
:ref:`topics-stats-usecases` section below.
However, the Stats Collector is always available, so you can always import it
in your module and use its API (to increment or set new stat keys), regardless
@ -19,7 +20,7 @@ collecting stats in your spider, Scrapy extension, or whatever code you're
using the Stats Collector from.
Another feature of the Stats Collector is that it's very efficient (when
enabled) and extremely efficient (almost unnoticeable) when disabled.
enabled) and almost unnoticeable when disabled.
The Stats Collector keeps a stats table per open spider which is automatically
opened when the spider is opened, and closed when the spider is closed.
@ -30,7 +31,7 @@ Common Stats Collector uses
===========================
Access the stats collector through the :attr:`~scrapy.crawler.Crawler.stats`
attribute. Here is an example of an extension that access stats:
attribute. Here is an example of an extension that accesses stats:
.. code-block:: python

View File

@ -7,7 +7,7 @@ Telnet Console
==============
Scrapy comes with a built-in telnet console for inspecting and controlling a
Scrapy running process. The telnet console is just a regular python shell
Scrapy running process. The telnet console is just a regular Python shell
running inside the Scrapy process, so you can do literally anything from it.
The telnet console is a :ref:`built-in Scrapy extension

View File

@ -12,8 +12,8 @@ There are 3 numbers in a Scrapy version: *A.B.C*
* *A* is the major version. This will rarely change and will signify very
large changes.
* *B* is the release number. This will include many changes including features
and things that possibly break backward compatibility, although we strive to
keep these cases at a minimum.
and things that may break backward compatibility, although we strive to keep
these cases to a minimum.
* *C* is the bugfix release number.
Backward-incompatibilities are explicitly mentioned in the :ref:`release notes <news>`,
@ -39,8 +39,8 @@ API stability
API stability was one of the major goals for the *1.0* release.
Methods or functions that start with a single dash (``_``) are private and
should never be relied as stable.
Methods or functions that start with a single underscore (``_``) are private
and should never be relied upon as stable.
Also, keep in mind that stable doesn't mean complete: stable APIs could grow
new methods or functionality but the existing methods should keep working the