Веб-краулер на Python с Scrapy

Веб-краулеры (web crawlers, spiders) — программы для автоматического чтения информации с веб-страниц. Их можно применять для получения котировок, спортивных результатов, текстов из социальных сетей или цен с торговых площадок. Python и библиотека Scrapy упрощают создание таких программ: Scrapy быстрый, расширяемый и умеет переходить по страницам и извлекать данные.
Ниже — подробная инструкция с практическими примерами, рекомендациями по отладке и безопасной эксплуатации, а также набором вспомогательных материалов: чек-листы, критерии приёмки, мини-SOP, сценарии тестирования и дерево решений для выбора стратегии сбора.
Установка Scrapy
Scrapy — это библиотека Python для веб-скрейпинга и создания пауков. Рекомендуется устанавливать её в виртуальное окружение, чтобы не трогать глобальные системные зависимости.
Создание виртуального окружения и установка Scrapy (Linux/macOS):
mkdir crawler
cd crawler
python3 -m venv venv
. venv/bin/activate
pip install --upgrade pip
pip install scrapyWindows (PowerShell):
mkdir crawler
cd crawler
python -m venv venv
venv\Scripts\Activate.ps1
python -m pip install --upgrade pip
pip install scrapyПроверка установки:
scrapy
# ожидаемый вывод (сокращённый):
# Scrapy X.Y.Z - no active project
# Usage:
# scrapy [options] [args]
# Available commands:
# bench Run quick benchmark test
# fetch Fetch a URL using the Scrapy downloader
# genspider Generate new spider using pre-defined templates
# runspider Run a self-contained spider (without creating a project) Совет: используйте последнюю стабильную версию Python 3 и поддерживаемую версию Scrapy. На машинах CI обычно достаточно виртуального окружения и установки зависимостей через requirements.txt.
Как устроен паук Scrapy
Класс паука наследуется от scrapy.Spider и описывает: имя паука, список стартовых URL и метод parse для обработки ответа.
Пример простого паука (Python 3):
import logging
logging.getLogger('scrapy').setLevel(logging.WARNING)
import scrapy
class WikipediaSpider(scrapy.Spider):
name = 'wikipedia'
start_urls = ['https://en.wikipedia.org/wiki/Battery_(electricity)']
def parse(self, response):
# Здесь мы пока ничего не возвращаем
print('Страница загружена:', response.url)Запуск самодостаточного файла:
scrapy runspider spider1.pyВажно: в реальном проекте принято создавать проект Scrapy (scrapy startproject myproject) и держать пауков в папке spiders. Для быстрых экспериментов удобно использовать runspider.
Отключение лишних логов
Если вывод логов мешает, можно повысить уровень логирования для модуля scrapy:
import logging
logging.getLogger('scrapy').setLevel(logging.WARNING)Это простой способ видеть только предупреждения и ошибки.
Использование инспектора браузера для поиска селекторов
Веб-страница — это документ HTML, представленный деревом DOM. Инструменты разработчика в Chrome и других браузерах позволяют быстро найти нужный элемент:
- Откройте страницу в браузере
- Наведите курсор на элемент
- Правый клик → Inspect
Во вкладке Elements вы увидите DOM-дерево. Выбирайте селекторы (CSS или XPath) по структуре элементов.
Короткое определение: CSS-селектор — строка, которая описывает путь к элементам в дереве DOM (напр., div#mw-content-text > div > p).
Извлечение заголовка страницы
Чтобы получить заголовок статьи Wikipedia, используйте CSS-селектор для заголовка h1 с идентификатором firstHeading.
def parse(self, response):
title = response.css('h1#firstHeading::text').get()
print('Заголовок:', title)Разъяснение: response.css() возвращает объект селектора, ::text выбирает текстовое содержимое, .get() возвращает первое совпадение как строку. Аналогично есть .getall() (или в старых версиях .extract()) для списка совпадений.
Извлечение первого абзаца
Селектор для первого абзаца в основной части статьи:
div#mw-content-text > div > pПример кода, который собирает первый абзац в одну строку:
def parse(self, response):
first_p = response.css('div#mw-content-text>div>p').get() # вернёт HTML первого
# если нужно только текст внутри:
first_p_text = ''.join(response.css('div#mw-content-text>div>p')[0].css('::text').getall()).strip()
print(first_p_text)
Пояснение: иногда текст разбивается на несколько текстовых узлов (ссылки, сноски), поэтому удобно собрать список .getall() и соединить через ‘’.
Сбор данных в JSON
Scrapy умеет возвращать словари (yield {…}), которые затем можно автоматически экспортировать в JSON, CSV или другие форматы при запуске.
Пример: извлекаем все абзацы и возвращаем как JSON-объекты:
def parse(self, response):
for e in response.css('div#mw-content-text>div>p'):
text = ''.join(e.css('::text').getall()).strip()
if text:
yield {'para': text}Запуск и экспорт в файл:
scrapy runspider spider3.py -o output.jsonФормат JSON будет представлять собой массив объектов с полем para.
Извлечение нескольких полей и атрибутов
На странице IMDb таблица бокс-офиса может выглядеть как набор строк. Пример извлечения title, weekend, gross, weeks, image:
def parse(self, response):
for e in response.css('div#boxoffice>table>tbody>tr'):
yield {
'title': ''.join(e.css('td.titleColumn>a::text').getall()).strip(),
'weekend': ''.join(e.css('td.ratingColumn').getall()[0].css('::text')).strip() if e.css('td.ratingColumn') else None,
'gross': ''.join(e.css('td.ratingColumn')[1].css('span.secondaryInfo::text').getall()).strip() if len(e.css('td.ratingColumn'))>1 else None,
'weeks': ''.join(e.css('td.weeksColumn::text').getall()).strip(),
'image': e.css('td.posterColumn img::attr(src)').get(),
}Пояснение: для атрибутов картинок используйте ::attr(src); если нужно первое совпадение — .get().
Частые проблемы и способы их решения
- Динамический контент (JS): Scrapy по умолчанию не исполняет JavaScript. Решения: 1) искать API, 2) использовать headless-браузер (Playwright, Selenium) и интегрировать результаты в Scrapy, 3) смотреть сетевые запросы в DevTools.
- Защита от ботов: капчи, проверка заголовков, rate limiting. Смягчайте нагрузку: добавьте задержки, случайные user-agent, обработку ошибок.
- Изменяющаяся разметка: пишите устойчивые селекторы, тестируйте на разных версиях страницы.
Important: всегда уважайте robots.txt и условия использования сайтов. Некоторые сайты запрещают автоматический сбор данных — убедитесь, что вы имеете права на скрейпинг.
Альтернативные подходы
- requests + BeautifulSoup — для простых страниц без JS.
- Selenium/Playwright — для сложных страниц с рендерингом в браузере.
- API — если сайт предоставляет публичный API, его использование предпочтительнее.
Мини-методология разработки паука (шаблон)
- Исследуйте страницу в браузере, найдите нужные селекторы.
- Попробуйте извлечь данные в interactive shell (scrapy shell ‘URL’).
- Напишите минимальный spider с yield-структурой.
- Локально запускайте и проверяйте экспорт в JSON/CSV.
- Добавьте обработку ошибок, retries и логирование.
- Напишите тесты (unit/integration) для основных селекторов.
- Запустите в контролируемой среде и мониторьте.
Дерево решений: как выбрать стратегию сбора
flowchart TD
A[Нужно извлечь данные?] --> B{Страница статична?}
B -- Да --> C[requests + BeautifulSoup]
B -- Нет --> D{Есть публичный API?}
D -- Да --> E[Использовать API]
D -- Нет --> F[Selenium/Playwright или Scrapy+headless]
C --> G[Простой парсер]
E --> G
F --> GКонтрольные списки по ролям
Разработчик:
- выбрать селекторы и протестировать в scrapy shell
- написать spider и обработку ошибок
- оформить экспорт
Data Engineer:
- убедиться, что формат данных стабильный
- настроить хранение (S3, базу данных)
- настроить периодичность и мониторинг
QA:
- создать тесты на ключевые селекторы
- проверить поведение при пустых и частично заполненных страницах
Юрист/Compliance:
- проверить robots.txt и условия использования
- оценить наличие персональных данных и требования GDPR/локального законодательства
Критерии приёмки
- Скрипт успешно запускается и завершает обход стартовых URL
- Данные экспортируются в ожидаемый формат (JSON schema)
- Нет повторяющихся записей (если это важно)
- Лимит запросов и задержки настроены, чтобы не создавать чрезмерную нагрузку
- Обработка ошибок и повторных попыток настроена
Сценарии тестирования и примеры приемочных тестов
- Тест: стартовая страница недоступна → ожидание повторной попытки и корректный лог.
- Тест: селектор не найден → spider пропускает запись и логирует предупреждение.
- Тест: динамический контент → проверка fallback-стратегии (поиск API или headless-браузер).
- Тест: корректность JSON-схемы для выборки полей.
Безопасность и приватность
- Собирайте только необходимые данные. Не сохраняйте лишние персональные данные.
- Для данных, содержащих персональные сведения, согласуйте правовую основу (согласие, законный интерес и т.п.).
- Ограничивайте хранение персональных данных и защищайте хранилище (шифрование, доступ по ролям).
Notes: если вы планируете массовый сбор данных по пользователям из ЕС, проконсультируйтесь с юристом по GDPR.
SOP для запуска паука (короткая инструкция)
- Активировать виртуальное окружение.
- Обновить зависимости: pip install -r requirements.txt.
- Запустить паука: scrapy crawl wikipedia -o output.json.
- Проверить размер и целостность output.json.
- Если всё успешно — загрузить файл в хранилище и отметить задачу завершённой.
План отката (rollback)
- Если паук начал генерировать некорректные данные, остановить расписание.
- Откатить изменения к последней рабочей версии паука в системе контроля версий.
- Проверить журналы и при необходимости восстановить удалённые/повреждённые данные из бэкапа.
Советы по устойчивости и масштабированию
- Используйте middleware для ротации user-agent и прокси.
- Добавьте AutoThrottle для контроля скорости запросов.
- Горизонтальное масштабирование: запуск множества ведомых процессов с контролем за уникальными URL (очередь задач в Redis/БД).
Совместимость и миграция
- Проверьте поддержку Python 3.x для выбранной версии Scrapy.
- При миграции со старых версий Scrapy замените
.extract()на.getall()/.get()там, где это уместно.
Когда Scrapy не лучший выбор
- Если вам нужно рендерить сложный JavaScript и поддержка headless-браузера критична, возможно, лучше использовать Playwright или Selenium напрямую.
- Для очень простых одностраничных задач проще requests + BeautifulSoup.
Заключение
Scrapy — мощный инструмент для создания веб-краулеров на Python. Он хорошо подходит для массового сбора структурированных данных при корректной настройке селекторов и уважении правил сайтов. В этом руководстве приведены базовые примеры, практические рекомендации и дополнительные материалы для надёжной разработки, тестирования и эксплуатации пауков.
Краткий план дальнейших шагов:
- протестируйте селекторы в scrapy shell;
- напишите адаптивный парсер с обработкой ошибок;
- определите политику хранения и соответствие требованиям конфиденциальности;
- автоматизируйте и мониторьте работу паука.
Summary:
- Scrapy упрощает парсинг HTML и экспорт в JSON/CSV.
- Учитывайте robots.txt и правила сайтов.
- Для динамических страниц используйте headless-браузеры или ищите API.
- Пишите тесты и план отката до запуска в продакшен.