Как собрать кастомный датасет через веб‑скрапинг — пример на IMDb

В эпоху решений на основе данных качественные датасеты — основа. Иногда готовых публичных наборов недостаточно. В таких случаях веб‑скрапинг помогает собрать кастомные данные прямо с сайтов. Эта статья показывает подход на примере IMDb (Internet Movie Database): от выбора источника до экспорта CSV и базовой валидации.
Содержание
- Обзор методов сбора данных
- Почему выбрать веб‑скрапинг
- Поиск и анализ источника (IMDb)
- Настройка окружения и зависимости
- Полный пример скрипта на Python
- Создание и очистка датасета в Pandas
- Альтернативные библиотеки и способы
- Этические и юридические аспекты, robots.txt
- Надёжность, масштабирование и безопасность
- Тесты, критерии приёмки и чек‑листы ролей
- Краткая справочная информация и рекомендации
Обзор методов сбора данных
Существуют разные подходы к сбору данных: ручной ввод, API, публичные датасеты и веб‑скрапинг. Коротко:
- Ручной ввод: подходит для небольших наборов и когда данные недоступны другими способами. Медленно и подвержено ошибкам.
- API: структурированный, часто актуальный доступ. Ограничения — квоты, авторизация, формат и покрытие данных.
- Публичные датасеты: экономят время, но могут не соответствовать точным требованиям или обновляться редко.
- Веб‑скрапинг: гибкий и масштабируемый способ извлечения данных со страниц, когда API нет или ограничен.
Важно: выбор метода зависит от задачи, объёма данных и юридических ограничений.
Почему выбирать веб‑скрапинг
Веб‑скрапинг даёт контроль над тем, какие поля собирать и как их структурировать. Вы можете объединять данные с нескольких страниц и сайтов, нормализовать поля под единую схему и автоматически обновлять датасет по расписанию. При этом нужен код, знание HTML и внимание к этике и законам.
Выбор и идентификация источника
Первый шаг — определить сайт с нужными данными. Убедитесь, что вы соблюдаете условия использования сайта. В примере используем IMDb. Откройте в браузере страницу с нужными списками — например, результаты по релизам за год — и воспользуйтесь инструментами разработчика, чтобы найти HTML‑элементы, содержащие нужные поля.
Для анализа DOM: правый клик → «Inspect», найдите теги, классы и атрибуты, которые содержат заголовки, рейтинги, описания и т. п.
В примере элемент заголовка фильма находится внутри класса lister-item-header. Анализируйте каждый признак, который будете извлекать.
Настройка окружения
Создайте изолированное виртуальное окружение. Установите нужные пакеты:
pip install requests beautifulsoup4 pandasИспользуемые библиотеки:
- requests — для HTTP‑запросов.
- beautifulsoup4 — для парсинга HTML и извлечения данных.
- pandas — для хранения, очистки и экспорта датасета.
- time и re — встроенные модули Python для пауз и регулярных выражений.
Полный код можно хранить в репозитории и документировать версии зависимостей (requirements.txt).
Пример скрипта: структура и ключевые функции
Ниже — минимальный рабочий пример. Код можно расширять: обработка исключений, логирование, многопоточность/асинхронность и т. д.
import requests
from bs4 import BeautifulSoup
import time
import pandas as pd
import reФункция для получения и парсинга HTML:
def get_soup(url, params=None, headers=None):
response = requests.get(url, params=params, headers=headers)
soup = BeautifulSoup(response.content, "html.parser")
return soupФункция извлечения полей из блока фильма (пример для структуры IMDb):
def extract_movie_data(movie):
title = movie.find("h3", class_="lister-item-header").find("a").text
rating = movie.find("div", class_="ratings-imdb-rating").strong.text
description = movie.find("div", class_="lister-item-content").find_all("p")[1].text.strip()
genre_element = movie.find("span", class_="genre")
genre = genre_element.text.strip() if genre_element else None
release_date = movie.find("span", class_="lister-item-year text-muted unbold").text.strip()
director_stars = movie.find("p", class_="text-muted").find_all("a")
directors = [person.text for person in director_stars[:-1]]
stars = [person.text for person in director_stars[-1:]]
movie_data = {
"Title": title,
"Rating": rating,
"Description": description,
"Genre": genre,
"Release Date": release_date,
"Directors": directors,
"Stars": stars
}
return movie_dataОсновная функция скрейпера с пагинацией и задержками:
def scrape_imdb_movies(year, limit):
base_url = "https://www.imdb.com/search/title"
headers = {"Accept-Language": "en-US,en;q=0.9"}
movies = []
start = 1
while len(movies) < limit:
params = {
"release_date": year,
"sort": "num_votes,desc",
"start": start
}
soup = get_soup(base_url, params=params, headers=headers)
movie_list = soup.find_all("div", class_="lister-item mode-advanced")
if len(movie_list) == 0:
break
for movie in movie_list:
movie_data = extract_movie_data(movie)
movies.append(movie_data)
if len(movies) >= limit:
break
start += 50 # IMDb показывает по 50 позиций на страницу
time.sleep(1) # Задержка, чтобы не нагружать сервер
return moviesЗапуск скрейпа (пример):
# Scrape 1000 movies released in 2023 (or as many as available)
movies = scrape_imdb_movies(2023, 1000)Создание датасета и предобработка
Собранные словари удобно конвертировать в DataFrame и затем очистить.
df = pd.DataFrame(movies)Пример базовой очистки:
df = df.dropna()
df['Release Year'] = df['Release Date'].str.extract(r'(\d{4})')
df['Release Year'] = pd.to_numeric(df['Release Year'], errors='coerce').astype('Int64')
df = df.drop(['Release Date'], axis=1)
df['Rating'] = pd.to_numeric(df['Rating'], errors='coerce')
df['Title'] = df['Title'].apply(lambda x: re.sub(r'\W+', ' ', x))Экспорт в CSV:
df.to_csv("imdb_movies_dataset.csv", index=False)Предпросмотр первых строк:
df.head()Проверка качества и валидация данных
Базовые проверки после сбора:
- Отсутствуют ли дубликаты (например, по Title + Release Year)?
- Соответствует ли тип столбцов ожидаемому (числа как float/int, даты как datetime)?
- Нет ли очевидных выбросов (рейтинг > 10, год в будущем)?
- Покрывают ли поля те признаки, которые требуются для анализа?
Пример быстрых проверок в Pandas:
assert df['Rating'].between(0, 10).all()
assert df['Release Year'].min() > 1800Критерии приёмки
- Набор содержит >= N записей (по вашей задаче).
- Все обязательные поля (Title, Rating, Release Year) заполнены в ≥95% записей.
- Типы столбцов корректны и позволяют аналитическую обработку.
Альтернативные библиотеки и подходы
Если сайт активно использует JavaScript или динамическую подгрузку, requests + BeautifulSoup может не подойти. Варианты:
- Selenium / Playwright — автоматизация браузера, выполняют JS. Подходит для сложных сценариев, но тяжелее в масштабировании.
- Scrapy — фреймворк для масштабируемого краулинга, встроенная поддержка пайплайнов и экспорта.
- Requests‑HTML — упрощённый рендеринг JS (ограниченно).
- API‑first подход — прежде чем скрапить, проверьте, нет ли неофициального/официального API.
Когда скрапинг не подходит: если сайт запрещает сбор данных в правилах, если данные доступны по платному API с неприемлемыми условиями, или если объём и частота запросов создают риск блокировки.
Этические и юридические аспекты
- Всегда проверяйте условия использования сайта (Terms of Service) и robots.txt. robots.txt указывает, какие пути разрешены для ботов, но не заменяет юридические условия.
- Не извлекайте персональные данные без правовой основы. Если собираете персональные данные пользователей, учитывайте требования локального законодательства (например, GDPR для EU).
- Уважайте нагрузку на сайт: добавляйте адекватные задержки, используйте кэширование и уменьшайте частоту запросов.
Важное: при сомнениях — спросите владельца сайта или используйте официальные каналы доступа к данным.
Техники надёжности, масштабирования и защиты
- Заголовки и сессии: используйте requests.Session() и задавайте корректный User‑Agent.
- Паузы и экспоненциальный бэкофф при ошибках (429, 503).
- Ротация прокси и IP — только при необходимости и с учётом правил сайта.
- Ограничение параллелизма: Scrapy и другие фреймворки позволяют контролировать скорость.
- Логирование и метрики: фиксируйте успешные и ошибочные запросы, время отклика и число записей.
- Хранение исходного HTML — полезно для репликации и отладки.
Рекомендуемая заглушка User‑Agent:
headers = {
"User-Agent": "MyDataCollectionBot/1.0 (+https://yourdomain.example)",
"Accept-Language": "en-US,en;q=0.9"
}Обработка JavaScript, CAPTCHAs и анти‑ботов
- Для JS‑рендеринга используйте Playwright или Selenium. Playwright легче масштабировать в headless‑режиме.
- Если сайт использует CAPTCHAs, интеграция обхода часто нарушает правила и может быть незаконной. Рассмотрите официальные API или договорённости.
Тесты и критерии приёмки
Набор тестов при автоматическом запуске:
- Smoke test: скрипт получает хотя бы одну страницу и извлекает 5 элементов.
- Schema test: структура колонок соответствует контракту (названия и типы).
- Consistency test: количество записей не падает кардинально от одной прогонки к другой.
- Regression: при изменении HTML страница, тесты должны сигнализировать о поломке парсинга.
Пример теста на уровень данных (pytest‑style):
def test_min_records(df):
assert len(df) >= 100Роли и чек‑листы
Developer (тот, кто пишет скрипт):
- Проанализировать DOM и составить селекторы
- Добавить обработку ошибок и логирование
- Написать unit‑тесты для extract_movie_data
Data Engineer:
- Описать схему датасета
- Настроить пайплайн ETL и расписание
- Обеспечить хранение исходного HTML и резервные копии
Data Analyst / Scientist:
- Проверить полноту и качество полей
- Добавить метрики покрытия данных
- Подготовить датасет для моделирования
Мини‑методология («быстрый» SOP)
- Определить поля и источники.
- Исследовать DOM и протестировать ручные запросы.
- Реализовать get_soup + extract функции.
- Добавить задержки, заголовки и обработку ошибок.
- Собрать небольшой сэмпл и проверить данные.
- Написать тесты и запустить на весь объём.
- Экспортировать CSV/паркет и документировать схему.
План действий при инциденте (runbook)
- Симптом: скрипт падает с пустым movie_list.
- Проверить HTTP‑код ответа и HTML (логировать response.status_code).
- Сохранить HTML для анализа и сравнить с эталоном.
- Проверить изменения селекторов в DOM.
- Если проблема на стороне сервера (5xx) — применить backoff и повтор.
- При 403/429 — уменьшить скорость, проверить User‑Agent, возможны блокировки.
Факты и численные подсказки (факт‑бокс)
- Стандартная страница IMDb для поиска содержит до 50 фильмов.
- Задержка 1–3 секунды между запросами обычно снижает риск временной блокировки.
- Сохранение исходного HTML для 1000 страниц потребует порядка десятков мегабайт (в зависимости от страницы).
Безопасность и конфиденциальность
- Храните ключи и учётные данные вне кода (переменные окружения, секреты CI).
- Удаляйте персональные данные, если они не нужны. При хранении PII соблюдайте законы о защите данных.
Короткая галерея пограничных случаев
- Пагинация поменяла параметр start → парсер перестаёт находить записи.
- Поле «рейтинг» отсутствует у малораскрученных фильмов → нужно обрабатывать None.
- Структура «директора/актеры» меняется для короткометражек → гибкий парсинг.
Словарь (1‑строчные определения)
- DOM — модель документа HTML, состоит из узлов и атрибутов.
- Selector — CSS/XPath выражение для поиска элементов в HTML.
- User‑Agent — строка, идентифицирующая клиента при HTTP‑запросе.
- Backoff — стратегия увеличения задержек при повторных ошибках.
Совместимость, миграция и примечания локализации
- При смене user‑agent и заголовков Accept‑Language результаты могут отличаться (локализованные названия). Для стабильности укажите Accept‑Language.
- При масштабировании на облако используйте контейнеры и очередь задач (Celery/Cloud Tasks).
Краткие рекомендации по SEO и публикации результата
- Документируйте схему CSV и примеры записей.
- Укажите источник и дату сбора данных в README.
- Соблюдайте лицензионные и юридические условия при публикации датасета.
Короткий вывод
Веб‑скрапинг — мощный инструмент создания кастомных датасетов. Он требует внимательности к DOM, управлению нагрузкой на сайт и соблюдению правил. Для устойчивых решений комбинируйте парсинг с тестами, логированием и политикой безопасности.
Что дальше (следующие шаги)
- Автоматизируйте сбор: запланируйте CRON/CI‑job.
- Добавьте мониторинг и оповещения на изменения структуры страниц.
- Рассмотрите переход на Scrapy или Playwright при усложнении задач.
Заметки: перед массовым сбором данных проверьте юридические ограничения и при необходимости запросите разрешение у владельцев сайта.