# Scrapy: start_requests() не вызывается, Crawled 0 pages

Source: https://ipvolt.com/ru/guides/scrapy-start-requests-not-called
Markdown: https://ipvolt.com/ru/guides/scrapy-start-requests-not-called.md
Language: ru

[ipvolt — главная](https://ipvolt.com/ru.md) / [Руководства](https://ipvolt.com/ru/guides.md) / Scrapy: start_requests() не вызывается, Crawled 0 pages

Диагностика
Опубликовано: 2026-10-10
6 мин чтения
Автор: ipvolt

В Scrapy 2.16 удалён Spider.start_requests(): паук пишет Crawled 0 pages и закрывается как finished без предупреждений. Решение: async def start(). Проверено на 2.19.

После обновления до Scrapy 2.16 или новее паук, в котором определён только `start_requests()`, открывается, не отправляет ни одного запроса и закрывается так, будто отработал:

```text
2026-10-09 12:33:33 [scrapy.core.engine] INFO: Spider opened
2026-10-09 12:33:33 [scrapy.extensions.logstats] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min)
2026-10-09 12:33:33 [scrapy.core.engine] INFO: Closing spider (finished)
2026-10-09 12:33:33 [scrapy.core.engine] INFO: Spider closed (finished)
```

Это строки движка и logstats из запуска на Scrapy 2.19.0. В журнале не было ни одной строки WARNING или ERROR, код выхода был 0, а `parse()` не вызывался. В итоговой статистике было `'finish_reason': 'finished'` и не было ключа `downloader/request_count`.

Причина: в Scrapy 2.16.0 (19 мая 2026 года) удалён метод `Spider.start_requests()`, устаревший с версии 2.13.0. В [примечаниях к версии](https://docs.scrapy.org/en/latest/news.html#release-2-16-0) сказано, что он «is removed and no longer called» ([pull request #7490](https://github.com/scrapy/scrapy/pull/7490)). Решение: переименовать метод в `async def start()`:

```python
# Before: Scrapy 2.16 and later never call this
def start_requests(self):
    yield scrapy.Request("https://example.com/", callback=self.parse)
```

```python
# After
async def start(self):
    yield scrapy.Request("https://example.com/", callback=self.parse)
```

После одной этой правки тот же паук обошёл свою страницу на Scrapy 2.16.0 и 2.19.0.

| Что вы видите на 2.16+ | Причина | Решение |
| --- | --- | --- |
| `Crawled 0 pages`, затем `Spider closed (finished)`, предупреждений нет | В пауке определён только `start_requests()` | Переименуйте его в `async def start()` |
| Обход идёт, но middleware больше не меняет стартовые запросы | В spider middleware определён только `process_start_requests()` | Добавьте `async def process_start()` |
| `TypeError: Middleware ... doesn't support asynchronous spider output`, код выхода 1 | В spider middleware синхронный `process_spider_output()` | Сделайте его асинхронным генератором |

Всё, что описано ниже, записано 9 октября 2026 года на Scrapy 2.19.0, Twisted 26.4.0 и Python 3.14.4 и повторено на Scrapy 2.16.0, 2.15.2 и 2.12.0. Паука, который только задаёт `start_urls` и не определяет ни одного из двух методов, изменение не затрагивает: он обошёл страницу на всех четырёх версиях.

## Замените start_requests() на async def start()

[`Spider.start()`](https://docs.scrapy.org/en/latest/topics/spiders.html#scrapy.Spider.start) — асинхронный генератор: `async def` с `yield`. Сами запросы остаются прежними. Две оплошности дают другой симптом: строку ERROR и по-прежнему ни одного запроса:

```text
TypeError: e1_start_not_async.LabSpider.start() must be an asynchronous generator, i.e. an async def method with yield statements, got <class 'generator'>
```

Это `def start()` без `async`. Если `async def start()` возвращает список, сообщение заканчивается на `got <class 'coroutine'>`. В обоих случаях Scrapy 2.19.0 закрыл паука с `'finish_reason': 'start_error'`, а код выхода остался 0.

Если паук переопределял `start_requests()`, чтобы добавить заголовки или meta к запросам из `start_urls`, сохраните цикл:

```python
async def start(self):
    for url in self.start_urls:
        yield scrapy.Request(
            url,
            headers={"Accept-Language": "en-AU"},
            meta={"page": 1},
            dont_filter=True,
        )
```

Стандартный `start()` — тот же цикл с `dont_filter=True` и больше ничем, поэтому оставьте этот аргумент. В лаборатории callback прочитал заголовок из `response.request.headers`, а `page` из `response.meta`.

Прокси по-прежнему задаётся в `meta`:

```python
async def start(self):
    yield scrapy.Request(
        "https://example.com/",
        meta={"proxy": "http://user:pass@proxy.example.com:8080"},
    )
```

В лаборатории запрос к адресу `http://` пришёл на локальный прокси с Basic-авторизацией с верными учётными данными. Руководство [Прокси в Scrapy 2.19](/ru/guides/scrapy-proxy-setup) описывает ротацию и переменные окружения прокси, а [Ошибка прокси 407](/ru/guides/fix-proxy-error-407) разбирает случай, когда прокси отклоняет учётные данные.

## Поддержка Scrapy ниже 2.13 и 2.16+: оба метода

Код, который должен работать по обе стороны от этого изменения, определяет оба метода. В примечаниях к версии сказано так: «Use start() instead, or both to maintain support for lower Scrapy versions.»

```python
async def start(self):
    for request in self.start_requests():
        yield request

def start_requests(self):
    yield scrapy.Request("https://example.com/", callback=self.parse)
```

Scrapy 2.12.0 вызвал `start_requests()`, а 2.15.2, 2.16.0 и 2.19.0 вызвали `start()`. Каждая версия обошла страницу, и 2.15.2 не вывел предупреждения об устаревании. Обратная ошибка так же незаметна: паук только с `async def start()` не отправил ни одного запроса на 2.12.0 и закрылся как finished.

## Spider middleware: process_start_requests() пропускается без предупреждения

В том же выпуске удалена поддержка `process_start_requests()` в spider middleware. Middleware из лаборатории выставлял `meta["tagged"] = True` на каждом стартовом запросе. На 2.19.0 с уже переведённым пауком страница была обойдена, callback вывел `tagged=None`, а предупреждений в журнале не было: метод ни разу не вызывался. Scrapy 2.15.2 останавливал ту же пару с `ValueError`, так что обновление превратило ошибку в тишину.

Замените его на [`process_start()`](https://docs.scrapy.org/en/latest/topics/spider-middleware.html#scrapy.spidermiddlewares.SpiderMiddleware.process_start):

```python
class TagStartMiddleware:
    async def process_start(self, start):
        async for item_or_request in start:
            if isinstance(item_or_request, scrapy.Request):
                item_or_request.meta["tagged"] = True
            yield item_or_request
```

С ним callback вывел `tagged=True` на 2.15.2, 2.16.0 и 2.19.0. Чтобы поддерживать и Scrapy ниже 2.13, оставьте рядом `process_start_requests()`. Когда оба метода были и в middleware, и в пауке, запрос был помечен на всех четырёх версиях.

## TypeError: Middleware doesn't support asynchronous spider output

Синхронный `process_spider_output()` отказывает громко. Обход останавливается до строки `Spider opened`, код выхода 1:

```text
TypeError: Middleware d2_mw_sync_output.StampMiddleware doesn't support asynchronous spider output. Its process_spider_output() method should be an async generator function or it should additionally define a process_spider_output_async() method.
```

`d2_mw_sync_output.StampMiddleware` — это middleware из лаборатории, на его месте будет имя вашего. Scrapy 2.15.2 запускал тот же middleware с предупреждением, что он «will stop working in a future version of Scrapy». Сделайте метод асинхронным генератором:

```python
class StampMiddleware:
    async def process_spider_output(self, response, result):
        async for item_or_request in result:
            if isinstance(item_or_request, dict):
                item_or_request["stamped"] = True
            yield item_or_request
```

В сигнатуре нет параметра `spider`. С ним 2.19.0 всё ещё запускал middleware и выводил `ScrapyDeprecationWarning: StampMiddleware.process_spider_output() requires a spider argument`. Middleware, в котором уже есть и синхронный `process_spider_output()`, и `process_spider_output_async()`, загрузился без ошибки, и 2.19.0 выполнил асинхронный метод. Если класс из сообщения относится к установленному пакету, поищите выпуск этого пакета, который поддерживает вашу версию Scrapy.

## Как узнать, какая версия Scrapy у вас запущена

```sh
scrapy version
python -m pip show scrapy
```

В лаборатории первая команда вывела `Scrapy 2.19.0`, а вторая `Version: 2.19.0`. Версию называет и первая строка журнала обхода: `Scrapy 2.19.0 started (bot: scrapybot)`. Выполняйте команды в том окружении, где идёт обход: в виртуальном окружении, образе контейнера или задаче CI.

Строка `requirements.txt`, в которой написано только `scrapy`, при следующей сборке установит самый новый выпуск. Если мигрировать сегодня нельзя, закрепите последнюю ветку выпусков, которая ещё вызывает `start_requests()`:

```text
scrapy>=2.15,<2.16
```

В лаборатории это дало 2.15.2: он обошёл страницу неизменённым пауком и вывел `ScrapyDeprecationWarning`, в котором назван класс паука и сказано, что метод «will stop being called in a future version of Scrapy». Считайте такое закрепление временным: в примечаниях к версии [2.17.0](https://docs.scrapy.org/en/latest/news.html#release-2-17-0) перечислены исправления уязвимостей. После миграции закрепите ту версию, которую вы проверили. На Python 3.15 без компилятора C команда `pip install --only-binary brotli scrapy` молча установила Scrapy 2.17.0 вместо 2.19.0; как это заметить, показано в статье [Python 3.15 для веб-скрапинга](/ru/blog/python-3-15-web-scraping).

## Как поймать обход без запросов в CI

При коде выхода 0 и `finish_reason`, равном `finished`, задача, которая проверяет только успешность обхода, проходит. Проверяйте число запросов:

```python
from scrapy.crawler import CrawlerProcess

from myproject.spiders.quotes import QuotesSpider


def test_spider_sends_requests():
    process = CrawlerProcess(settings={"LOG_LEVEL": "WARNING"})
    crawler = process.create_crawler(QuotesSpider)
    process.crawl(crawler)
    process.start()
    assert crawler.stats.get_value("downloader/request_count", 0) > 0
```

На 2.16.0 и 2.19.0 тест упал для паука со `start_requests()` с сообщением `AssertionError: assert 0 > 0` и прошёл после переименования. На 2.15.2 он прошёл для обоих. В лаборатории каждый процесс pytest выполнял один обход локального `python -m http.server`, поэтому направьте свой тест на тестовый стенд или на страницу, которую вам разрешено запрашивать.

Для задачи по расписанию возьмите то же число из статистики в конце журнала. В запуске, который отправлял запросы, есть запись `'downloader/request_count'`, а в неудачном её не было.

## Как это проверялось

Ubuntu 26.04.1, Python 3.14.4, всё на `127.0.0.1`: `python -m http.server` в роли цели и прямой прокси с Basic-авторизацией. В четырёх виртуальных окружениях стояли Scrapy 2.19.0 и 2.16.0 с Twisted 26.4.0, 2.15.2 с Twisted 25.5.0 и 2.12.0 с Twisted 24.11.0. Каждый из 17 пауков запускался на каждой версии командой `scrapy runspider` в новом интерпретаторе с чистым окружением: 68 запусков и ещё 8 запусков pytest. Аккаунт прокси не использовался.

[Архив лаборатории](https://ipvolt.com/downloads/scrapy-start-requests-not-called/scrapy-start-requests-lab.zip) содержит [скрипт запуска](https://ipvolt.com/downloads/scrapy-start-requests-not-called/run_lab.py), пауков, [проверку для pytest](https://ipvolt.com/downloads/scrapy-start-requests-not-called/ci/test_crawl.py), [README](https://ipvolt.com/downloads/scrapy-start-requests-not-called/README.md) с таблицей всех результатов и [записанные результаты](https://ipvolt.com/downloads/scrapy-start-requests-not-called/results.json).

Не проверялось: macOS и Windows, Scrapy 2.13, 2.14, 2.17 и 2.18, правила `CrawlSpider` и сторонние middleware.

Когда паук снова отправляет запросы, каждый из них расходует трафик прокси-тарифа. В статье [Сколько ГБ прокси вам нужно](/ru/blog/how-many-gb-of-proxies-do-i-need) измерено, сколько тратит загрузка одного HTML.

ipvolt — прокси-сервис для разработчиков, который пока не открыт; [запишитесь в список раннего доступа](https://ipvolt.com/#waitlist-closing), и вы получите одно письмо, когда он откроется.

## Источники и дополнительное чтение

- [Scrapy release notes: 2.16.0, deprecation removals](https://docs.scrapy.org/en/latest/news.html#release-2-16-0)
- [Scrapy documentation: Spider.start()](https://docs.scrapy.org/en/latest/topics/spiders.html#scrapy.Spider.start)
- [Scrapy documentation: SpiderMiddleware.process_start()](https://docs.scrapy.org/en/latest/topics/spider-middleware.html#scrapy.spidermiddlewares.SpiderMiddleware.process_start)
- [Scrapy documentation: SpiderMiddleware.process_spider_output()](https://docs.scrapy.org/en/latest/topics/spider-middleware.html#scrapy.spidermiddlewares.SpiderMiddleware.process_spider_output)
- [scrapy/scrapy pull request #7490: Drop start_requests() support](https://github.com/scrapy/scrapy/pull/7490)
- [scrapy/scrapy pull request #7504: Remove support for sync process_spider_output()](https://github.com/scrapy/scrapy/pull/7504)
- [Scrapy release notes: 2.17.0, security bug fixes](https://docs.scrapy.org/en/latest/news.html#release-2-17-0)

## Связанные руководства

- [Прокси в Scrapy 2.19: авторизация, ротация, ошибки](https://ipvolt.com/ru/guides/scrapy-proxy-setup.md)
- [Как исправить ошибку прокси 407, не гадая](https://ipvolt.com/ru/guides/fix-proxy-error-407.md)
- [Переменные окружения прокси: HTTP_PROXY и NO_PROXY](https://ipvolt.com/ru/guides/proxy-environment-variables.md)

## О ipvolt

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

[Читать оригинал на английском](https://ipvolt.com/guides/scrapy-start-requests-not-called.md)

## Узнайте, когда откроется доступ.

ipvolt · В разработке

Мы строим прокси-инфраструктуру для разработчиков и команд, работающих с данными. Оставьте email, чтобы получить уведомление, когда ipvolt будет готов.

Одно письмо, когда откроется доступ. Больше ничего.

[Получить ранний доступ](https://ipvolt.com/ru/guides/scrapy-start-requests-not-called#waitlist-closing)

[Конфиденциальность](https://ipvolt.com/privacy)

