Веб-скрапинг с Scrapy в Python

10 сентября 2026
Автор

Антон Ларичев

Что такое Scrapy

Scrapy — это асинхронный фреймворк для веб-скрапинга и краулинга сайтов на Python. В отличие от библиотек requests + BeautifulSoup, Scrapy предоставляет полноценную архитектуру: встроенное управление очередями запросов, обработку ошибок, дросселирование и экспорт данных в различные форматы.

Fреймворк строится на Twisted — асинхронном сетевом движке, что позволяет обрабатывать сотни запросов параллельно без явного использования asyncio.

Курс по теме

Основы Python — курс

40 000+ студентов · рейтинг 4.8 · гарантия возврата 30 дней

Установка и создание проекта

Установите Scrapy через pip:

pip install scrapy

Создайте новый проект командой:

scrapy startproject bookstore
cd bookstore

Фреймворк генерирует следующую структуру:

bookstore/
    scrapy.cfg              # конфиг деплоя
    bookstore/
        __init__.py
        items.py            # модели данных
        middlewares.py      # промежуточные обработчики
        pipelines.py        # пайплайны обработки
        settings.py         # настройки проекта
        spiders/            # директория для пауков
            __init__.py

Первый паук

Паук (Spider) — класс, описывающий, как обходить сайт и извлекать данные. Создайте файл bookstore/spiders/books_spider.py:

import scrapy


class BooksSpider(scrapy.Spider):
    name = "books"  # уникальное имя паука
    start_urls = ["https://books.toscrape.com/"]

    def parse(self, response):
        for book in response.css("article.product_pod"):
            yield {
                "title": book.css("h3 a::attr(title)").get(),
                "price": book.css("p.price_color::text").get(),
                "rating": book.css("p.star-rating::attr(class)").get(),
                "availability": book.css("p.availability::text").getall(),
            }

        next_page = response.css("li.next a::attr(href)").get()
        if next_page:
            yield response.follow(next_page, self.parse)

Ключевые атрибуты класса:

  • name — идентификатор паука, используется при запуске
  • start_urls — список начальных URL, с которых стартует обход
  • parse — метод по умолчанию для обработки ответов

Метод parse принимает объект Response и должен возвращать либо словари/Item-объекты с данными, либо объекты Request для перехода на следующие страницы.

Селекторы CSS и XPath

Scrapy предоставляет два способа выборки элементов из HTML.

CSS-селекторы

def parse(self, response):
    # Получить текст элемента
    title = response.css("h1.product_title::text").get()

    # Получить значение атрибута
    image_url = response.css("div.item img::attr(src)").get()

    # Получить список всех совпадений
    tags = response.css("ul.breadcrumb li a::text").getall()

    # Работа с вложенными выборками
    for row in response.css("table.table tr"):
        label = row.css("th::text").get()
        value = row.css("td::text").get()
        yield {"label": label, "value": value}

Особенности синтаксиса Scrapy CSS:

  • ::text — псевдоэлемент для извлечения текстового содержимого
  • ::attr(name) — псевдоэлемент для извлечения атрибута
  • .get() — возвращает первое совпадение или None
  • .getall() — возвращает список всех совпадений

XPath-селекторы

XPath удобен для более сложных выборок с условиями:

def parse(self, response):
    # Текст заголовка
    title = response.xpath("//h1[@class='product_title']/text()").get()

    # Атрибут src у картинки внутри div
    image_url = response.xpath("//div[@class='item']//img/@src").get()

    # Элементы с условием по тексту
    in_stock = response.xpath(
        "//p[@class='availability'][contains(text(), 'In stock')]"
    ).get()

    # Переход к соседнему элементу
    price = response.xpath(
        "//th[text()='Price (excl. tax)']/following-sibling::td/text()"
    ).get()

Комбинирование подходов

def parse(self, response):
    for book in response.css("article.product_pod"):
        # CSS для простых выборок
        title = book.css("h3 a::attr(title)").get()
        # XPath для условных
        rating_class = book.xpath(".//p[contains(@class,'star-rating')]/@class").get()
        yield {"title": title, "rating_class": rating_class}

Модели данных — Items

Для структурированного хранения данных Scrapy использует Item — словарь с объявленными полями. Откройте bookstore/items.py:

import scrapy


class BookItem(scrapy.Item):
    title = scrapy.Field()
    price = scrapy.Field()
    rating = scrapy.Field()
    upc = scrapy.Field()
    availability = scrapy.Field()
    description = scrapy.Field()
    image_url = scrapy.Field()

Использование в пауке:

from bookstore.items import BookItem


class BooksSpider(scrapy.Spider):
    name = "books"
    start_urls = ["https://books.toscrape.com/"]

    def parse(self, response):
        for book in response.css("article.product_pod"):
            item = BookItem()
            item["title"] = book.css("h3 a::attr(title)").get()
            item["price"] = book.css("p.price_color::text").get()
            item["rating"] = book.css("p.star-rating::attr(class)").get()
            detail_url = book.css("h3 a::attr(href)").get()
            yield response.follow(detail_url, self.parse_detail, cb_kwargs={"item": item})

        next_page = response.css("li.next a::attr(href)").get()
        if next_page:
            yield response.follow(next_page, self.parse)

    def parse_detail(self, response, item):
        item["upc"] = response.xpath(
            "//th[text()='UPC']/following-sibling::td/text()"
        ).get()
        item["description"] = response.css("#product_description ~ p::text").get()
        item["image_url"] = response.css("div.item img::attr(src)").get()
        yield item

Пайплайны обработки данных

Item Pipeline — цепочка обработчиков, через которую проходит каждый собранный Item. Откройте bookstore/pipelines.py:

import re


class PricePipeline:
    """Очищает цену от символа валюты и приводит к float."""

    def process_item(self, item, spider):
        price = item.get("price", "")
        cleaned = re.sub(r"[^\d.]", "", price)
        item["price"] = float(cleaned) if cleaned else None
        return item


class RatingPipeline:
    """Преобразует CSS-класс рейтинга в числовое значение."""

    RATING_MAP = {
        "One": 1, "Two": 2, "Three": 3, "Four": 4, "Five": 5
    }

    def process_item(self, item, spider):
        rating_class = item.get("rating", "")
        for word, value in self.RATING_MAP.items():
            if word in rating_class:
                item["rating"] = value
                return item
        item["rating"] = None
        return item


class DuplicateFilterPipeline:
    """Фильтрует дубликаты по UPC."""

    def open_spider(self, spider):
        self.seen_upcs = set()

    def process_item(self, item, spider):
        upc = item.get("upc")
        if upc in self.seen_upcs:
            from scrapy.exceptions import DropItem
            raise DropItem(f"Duplicate UPC: {upc}")
        self.seen_upcs.add(upc)
        return item

Подключите пайплайны в settings.py:

ITEM_PIPELINES = {
    "bookstore.pipelines.PricePipeline": 100,
    "bookstore.pipelines.RatingPipeline": 200,
    "bookstore.pipelines.DuplicateFilterPipeline": 300,
}

Число задаёт порядок выполнения — меньшее значение означает более раннее выполнение (диапазон 0–1000).

Настройки проекта

Основные параметры в settings.py:

# Задержка между запросами (в секундах)
DOWNLOAD_DELAY = 1

# Количество параллельных запросов
CONCURRENT_REQUESTS = 8
CONCURRENT_REQUESTS_PER_DOMAIN = 4

# Авторепрезентация бота
USER_AGENT = "Mozilla/5.0 (compatible; MyBot/1.0)"

# Соблюдение robots.txt
ROBOTSTXT_OBEY = True

# Автоматическое дросселирование (адаптация скорости под нагрузку сервера)
AUTOTHROTTLE_ENABLED = True
AUTOTHROTTLE_START_DELAY = 1
AUTOTHROTTLE_MAX_DELAY = 10

# Кэш HTTP-ответов
HTTPCACHE_ENABLED = True
HTTPCACHE_EXPIRATION_SECS = 3600
HTTPCACHE_DIR = ".scrapy/httpcache"

Запуск паука и экспорт данных

Запустить паука и сохранить результат:

# Вывод в JSON
scrapy crawl books -o books.json

# Вывод в JSONL (строка — один объект, удобно для больших данных)
scrapy crawl books -o books.jsonl

# Вывод в CSV
scrapy crawl books -o books.csv

# Запуск с передачей аргументов
scrapy crawl books -a category=mystery -o mystery_books.json

Передача аргументов в паук через __init__:

class BooksSpider(scrapy.Spider):
    name = "books"

    def __init__(self, category=None, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.category = category
        base_url = "https://books.toscrape.com"
        if category:
            self.start_urls = [f"{base_url}/catalogue/category/books/{category}/index.html"]
        else:
            self.start_urls = [f"{base_url}/"]

Отладка в интерактивной оболочке

Scrapy Shell позволяет тестировать селекторы без запуска полного краулера:

scrapy shell "https://books.toscrape.com/"

Внутри оболочки доступны переменные response, request, а также функции fetch() и view():

# Проверить CSS-селектор
response.css("article.product_pod h3 a::attr(title)").getall()

# Открыть страницу в браузере для визуальной проверки
view(response)

# Перейти на другой URL
fetch("https://books.toscrape.com/catalogue/a-light-in-the-attic_1000/index.html")

# Проверить XPath
response.xpath("//th[text()='UPC']/following-sibling::td/text()").get()

Пайплайн записи в базу данных

Пример пайплайна для сохранения данных в SQLite через sqlite3:

import sqlite3


class SQLitePipeline:
    def open_spider(self, spider):
        self.conn = sqlite3.connect("books.db")
        self.cursor = self.conn.cursor()
        self.cursor.execute("""
            CREATE TABLE IF NOT EXISTS books (
                upc TEXT PRIMARY KEY,
                title TEXT,
                price REAL,
                rating INTEGER,
                description TEXT
            )
        """)
        self.conn.commit()

    def close_spider(self, spider):
        self.conn.close()

    def process_item(self, item, spider):
        self.cursor.execute("""
            INSERT OR REPLACE INTO books (upc, title, price, rating, description)
            VALUES (?, ?, ?, ?, ?)
        """, (
            item.get("upc"),
            item.get("title"),
            item.get("price"),
            item.get("rating"),
            item.get("description"),
        ))
        self.conn.commit()
        return item

Итоги

Scrapy предоставляет полноценную экосистему для промышленного веб-скрапинга:

  • Асинхронные запросы через Twisted без ручного управления потоками
  • CSS и XPath селекторы для гибкого извлечения данных
  • Item и Item Pipeline для валидации, трансформации и сохранения
  • Встроенное дросселирование, кэширование и соблюдение robots.txt
  • Удобная Shell для интерактивной отладки селекторов

Для простых одностраничных задач достаточно requests + BeautifulSoup. Scrapy оправдывает себя при обходе многостраничных сайтов, необходимости параллельных запросов и построении пайплайнов обработки.

Чтобы глубже освоить Python и научиться создавать реальные проекты от простых скриптов до полноценных приложений, приходите на курс Python на PurpleSchool.

Стрелочка влевоuv — современный менеджер пакетов PythonPython rich: красивый вывод в терминалеСтрелочка вправо

Постройте личный план изучения Python до уровня Middle — бесплатно!

Python — часть карты развития Backend

  • step100+ шагов развития
  • lessons30 бесплатных лекций
  • lessons300 бонусных рублей на счет

Все гайды по Python

Почему Python выводит значение без команды printКак отправлять запросы с помощью requests в PythonКак работает команда print в PythonВозможности Python для автоматизации задачРабота с JSON в Python на примерахPython get — методы получения данныхКак находить и исправлять ошибки в PythonРабота с данными через API и внешние сервисыСтруктура и оформление кода PythonОсновы Django с PythonПолезные приёмы в Python для повседневной работыИспользование locals в Python для отладкиИнтеграция PHP и PythonКак выполнять HTTPS-запросы в PythonFastAPI Python — быстрый старт: создание REST API с нуляКак работать с API в Python
Ввод целого числа в PythonВедение логов в PythonУдаление данных в Python с помощью removeОбработка исключений с помощью try/except в PythonФункция super() в Python — как вызвать метод родителяСоздание собственных контекстных менеджеров в PythonРабота с символами программирования PythonРабота с переменной X в PythonРабота с классами в PythonКак скачать Python на компьютерПростая программа на Python для начинающихОсновы Python для тех, кто начинаетЧто нового в Python 3Поддерживается ли Python 2 и стоит ли его использоватьPython 1 — с чего начиналась история языкаКоманда python print - полное руководство по выводу данныхПравила именования переменных в PythonПользовательские исключения в PythonОписание объектов PythonНаследование классов в Python — основы и примерыОсновы Python coreМножественное наследование в Python — примеры и MROКонтекстный менеджер with в Python — как работает и зачем нуженКомментарии в Python — однострочные, многострочные и docstringКакой Python выбрать для установкиКак вывести целое число с помощью print в PythonКак установить Python на Windows macOS и LinuxКак пользоваться консолью PythonКак получить последний элемент в PythonКак найти значение в PythonКак настроить PythonКак использовать print для строк в PythonКак работает интерпретатор PythonИнструкция по работе с PythonБлок finally в обработке исключений PythonЦелые числа в PythonАбстрактные классы в Python — ABC и abstractmethod
Загрузка данных PythonУправление проектами на GitHub с PythonСоздание веб-приложений на Flask PythonСоздание бота на PythonСоздание интерфейсов Python QTСоздание игр с PygameСоздание GUI в PythonКак работать со словарями в PythonРабота с библиотеками через Python PackagingРабота со временем в Python при помощи модуля timePython name — особенности переменнойМатематические операции в Python с модулем mathPython listing — что это и как использоватьPytest — тестирование на Python: полное руководствоОбработка изображений с OpenCV PythonNumPy в Python — основы и применение в задачахМашинное обучение с PythonИспользование Anaconda с PythonМодуль contextlib в Python — утилиты для контекстных менеджеровБиблиотеки Python и их применение в проектах
Возврат значений из функции в PythonВложенные функции в PythonСоздание собственных декораторов в PythonРабота с функцией map в PythonЦикл while в Python и примеры использованияОбработка чисел, введённых через input в PythonОсновные операторы в Python с примерамиУсловные выражения if else в Python для начинающихКак выполняется вызов функций call в PythonПродвинутые генераторы в Python — send, throw, close и корутиныПозиционные и именованные аргументы в PythonОбъявление переменных и управление областью видимости в PythonПередача аргументов по ссылке и по значению в PythonПередача аргументов через args и kwargs в PythonОсновные методы Python и примеры их использованияОператор match/case в Python 3.10+ — основы структурного сопоставленияПаттерны match/case в Python — деструктуризация, guard и вложенные шаблоныПрактические примеры match/case в Python — реальные сценарии примененияЛокальные и глобальные переменные в PythonЧасто используемые команды PythonКлючевые слова global и nonlocal в PythonКак создавать функции в PythonКак работает сборщик мусора в PythonКак работает область видимости переменных в PythonКак работает функция callable в PythonКак работает функция any и all в PythonКак проверить тип переменной в PythonКак передать функцию как аргумент в PythonКак использовать функцию isinstance в PythonКак использовать функцию filter в PythonКак использовать функцию filter в PythonКак использовать функцию eval безопасно в PythonКак использовать декораторы в PythonИзменяемые и неизменяемые типы данных в PythonГенераторы и yield в Python — как создавать и использоватьГенераторные выражения в Python — синтаксис и примерыФункции в Python и способы их вызоваФункции как объекты в PythonЧто такое замыкания в PythonЧто делает функция reduce в PythonЧто делает функция id в PythonАргументы по умолчанию в PythonАнонимные функции и lambda в PythonАлгоритмы на Python — примеры и объяснение
Запись данных в PythonУстановка pip в PythonУправление зависимостями requirement в PythonУправление библиотеками с помощью Python PackagingУдаление пробелов с помощью strip в PythonСтруктурирование кода в PythonСоздание исполняемого файла Python в exeРазбор traceback в модуле PythonРазбор site-packages в PythonРазбор Program Files в PythonРабота с Unicode кодировками в PythonРабота с системными функциями Python sysРабота с папкой AppData в PythonРабота с модулем logging в PythonРабота с каталогами в PythonРабота с CSV в PythonВиртуальная среда venv в Python — создание и настройкаКак создать простое приложение на PythonИспользование pip в Python для установки пакетовМодули в Python и организация кода в проектеИмпорт модулей в Python и правила подключенияРабота с файлами в Python пошаговоЧто делает компилятор Python и как он работаетПолучение строки из модуля PythonПодключение файлов в Python с includeПеременные среды в PythonСборка проекта с помощью packaging в PythonНастройка Python сервераИспользование Python на UbuntuИспользование консоли PythonИспользование кодировок в PythonИнициализация пакетов PythonИмпорт модулей PythonИмпорт имен в PythonСреда IDLE Python и базовые возможностиЧтение и запись TXT в PythonЧтение файлов в Python с помощью open file
Удаление элементов из списка PythonТипы данных в Python — обзор и рекомендацииОсновные операции со строками в PythonМетоды str в Python и обработка текстаСписки в Python и их ключевые методыСоздание списков данных в PythonРабота со строками и символами в PythonРабота со столбцами в PythonРабота со списком значений в PythonРабота с таблицами в Python с помощью DataFrameРабота с RFR в PythonРабота с пробелами в PythonРабота с массивами в PythonРабота с кортежами tuple PythonРабота с координатами X и Y в PythonРабота с ключами в PythonРабота с элементами данных PythonРабота с двоичными числами PythonРабота с данными в PythonРабота с данными NumPy PythonРабота с большими числами в PythonРабота с битами в PythonРабота с байтами в PythonЧто такое значение в Python и как его определитьМножества в Python и операции с нимиИспользование range в Python для цикловПроверка на четность в PythonПроверка числа в PythonПреобразование типов в PythonПреобразование списка в строку PythonПреобразование числа в строку в PythonПостроение графиков в PythonОпределение индекса элемента в PythonОкругление чисел в PythonОбъединение списков в Python с помощью zipМножества в PythonМассивы в Python и отличие от списковМассив чисел в PythonКортежи данных в PythonКак вычислить сумму чисел в PythonКак получить остаток от деления в PythonКак найти следующее число в PythonИспользование Unicode в PythonТип int в Python и его особенностиИндекс списка в PythonФункции для работы со строками в PythonЭлементы Python и способы доступа к нимДоступ к элементам массива в PythonДеление чисел в PythonРабота с данными в Python на практикеКак работать с числами в Python
Открыть базу знаний

Лучшие курсы по теме

Иконка молнииНовый
изображение курса

Основы Python

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Nest.js с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.6
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Docker и Ansible

Антон Ларичев
AI-тренажерыAI-тренажеры
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее

Отправить комментарий