Машинное обучение работает ровно настолько хорошо, насколько хороши данные за ним, а большинство данных, достойных изучения, находится в открытом интернете, а не в аккуратных CSV-файлах, которые вам кто-то передаёт. Объявления о товарах, цены, отзывы, вакансии, новости и обсуждения в социальных сетях генерируются непрерывно и в больших объёмах: это именно тот вид свежего, реального сигнала, который нужен модели. Это руководство показывает, как использовать веб-скрапинг для машинного обучения: почему веб-данные питают ML, как надёжно собирать их в масштабе, как разметить и структурировать, а также как подать в тренировочный конвейер: с работающим кодом на Python на каждом шаге.
Пошаговый пример ограничен публичными данными: страницами, которые любой может просматривать без входа в систему. Python собирает HTML через Crawlbase Crawling API, преобразует его в pandas dataframe, очищает и размечает строки, а затем выполняет базовую подготовку признаков, чтобы результат был готов для модели. Цель: воспроизводимый шаг сбора, который можно запускать по расписанию, потому что устаревший датасет: это модель, которая незаметно деградирует.
Почему веб-данные питают машинное обучение
Модели с учителем учатся на примерах, а интернет: крупнейший источник примеров. Три свойства делают его особенно ценным для ML. Он разнообразен: скрапинг множества сайтов даёт модели то разнообразие, которое необходимо для обобщения вместо заучивания особенностей одного источника. Он актуален: повторный запуск сборщика поддерживает тренировочный набор в соответствии с текущим состоянием мира, что особенно важно в быстро меняющихся областях: ценообразовании, спросе и настроениях. И он обширен: там, где вручную размеченный набор может содержать несколько тысяч строк, скрапер способен собрать сотни тысяч публичных записей.
Загвоздка в надёжности. Одноразовый скрипт, работающий на вашем ноутбуке сегодня, не является источником данных, на котором можно строить модель. Сайты рендерят контент на клиенте, меняют разметку и блокируют автоматический трафик, поэтому слой сбора должен быть надёжным прежде, чем что-либо ниже по цепочке приобретёт значение. Именно здесь ломаются ML-проекты на практике, и этому посвящена большая часть этого руководства.
Где собранные данные вписываются в конвейер ML
Собранные веб-данные появляются в нескольких точках проекта, и полезно чётко понимать, какую именно задачу вы решаете.
- Обучающие данные. Собранные строки становятся датасетом, на котором модель учится напрямую: в режиме обучения с учителем, без учителя или полуавтоматически.
- Инженерия признаков. Извлечённые поля (длина текста, тональность, изменения цен, количество по категориям) становятся входными признаками, повышающими предсказательную силу модели, обученной на других данных.
- Аугментация данных. Когда размеченный вручную набор слишком мал, собранные записи расширяют его объём и разнообразие, чтобы модель видела больше пространства признаков.
- Оценка. Свежесобранный срез, отложенный из тренировки, является реалистичным тестовым набором для проверки поведения модели на актуальных, «живых» данных.
Дальнейший текст строит небольшой, но полный конвейер «от сбора до признаков», который можно адаптировать к любому из перечисленных применений. О том, как само обучение работает после подготовки данных, читайте в статье AI model training explained.
Почему обычного запроса недостаточно в масштабе
Собрать одну страницу с помощью requests легко. Собрать сто тысяч страниц, надёжно, с сайтов, защищающихся от ботов,: вот где большинство самодельных сборщиков падает. Быстро проявляются две проблемы. Во-первых, многие страницы рендерят контент в браузере с помощью JavaScript, и «сырой» HTML, который вы получаете,: пустая оболочка. Во-вторых, коммерческие сайты быстро помечают автоматический трафик: IP датацентров и машинноподобные паттерны запросов блокируются задолго до того, как у вас наберётся достаточно строк для обучения.
Можно решить обе проблемы самостоятельно с помощью headless-браузера и пула ротируемых жилых прокси, но поддержание этого парка в рабочем состоянии: большая часть инженерных усилий. Crawling API сворачивает всё в один вызов: вы отправляете URL, он рендерит страницу за доверенным IP, ротирует адреса на своей стороне и возвращает готовый HTML. Если цель обслуживает чистую статическую разметку и вам нужны только разобранные поля, Crawling API возвращает структурированный JSON напрямую; для низкоуровневого транспорта с ротацией, которую вы контролируете сами, подходит Smart AI Proxy. В этом руководстве используется Crawling API, поскольку сбор датасетов обычно охватывает смешанные, защищённые сайты.
Crawlbase предлагает два типа токенов. Обычный токен получает статический HTML; токен JavaScript (JS) сначала рендерит страницу в реальном браузере. Если ваша цель рендерится на клиенте, используйте JS-токен, иначе обычный токен быстрее и дешевле. Выбирайте по источнику, а не один вариант для всей задачи.
Настройка проекта
Вам нужны Python 3 и pip. Проверьте оба, затем создайте проект и установите библиотеки, которые использует конвейер.
python --version pip --version mkdir ml-dataset && cd ml-dataset python -m venv .venv && source .venv/bin/activate pip install crawlbase beautifulsoup4 pandas scikit-learn
Четыре зависимости делают всю работу: crawlbase: клиент для Crawling API, beautifulsoup4 разбирает возвращаемый HTML, pandas хранит датасет как dataframe, а scikit-learn выполняет подготовку признаков в конце. Вам также потребуется аккаунт Crawlbase и токен, который вы получаете из панели управления после регистрации. Храните его в переменной среды, а не жёстко закодированным.
Шаг 1: сбор страниц через Crawling API
Начните со слоя сбора, так как всё, что идёт далее, зависит от того, чтобы он возвращал чистый HTML. Python-клиент оборачивает API в один вызов get. Для защищённых сайтов с клиентским рендерингом важны два параметра: ajax_wait указывает API ждать асинхронного контента, а page_wait задерживает выполнение на фиксированное количество миллисекунд после загрузки, чтобы появился контент с поздним рендерингом. Сборщик проверяет код статуса, чтобы заблокированная страница никогда не превращалась в пустую строку в датасете.
import os import time from crawlbase import CrawlingAPI # JS token renders the page in a real browser before returning HTML api = CrawlingAPI({"token": os.environ["CRAWLBASE_JS_TOKEN"]}) options = { "ajax_wait": "true", "page_wait": 5000, } def fetch_html(url): response = api.get(url, options) if response["status_code"] != 200: raise RuntimeError(f"fetch failed: {response['status_code']}") return response["body"].decode("utf-8") def collect(urls): pages = [] for url in urls: try: pages.append({"url": url, "html": fetch_html(url)}) except RuntimeError as err: print(f"skipping {url}: {err}") time.sleep(1) # pace requests so you stay unblocked return pages
Crawling API ротирует IP и рендерит страницу за вас, поэтому сборщик остаётся небольшим. time.sleep между запросами намеренный: паузы поддерживают здоровье длительного запуска. Для датасета реального размера вам потребуются тысячи URL, логика повторных попыток и конкурентность: это отдельная тема, рассматриваемая в статье о веб-скрапинге в большом масштабе.
Создание ML-датасета означает тысячи загрузок страниц с защищённых сайтов. Crawling API принимает токен, рендерит страницу в реальном браузере, ротирует жилые IP на своей стороне и отдаёт готовый HTML, позволяя вам не запускать собственный headless-парк и пул прокси. Сначала попробуйте на публичном источнике в бесплатном тарифе.
Шаг 2: разбор страниц в структурированные записи
Сырой HTML: не датасет. Следующий шаг превращает каждую страницу в плоскую запись с нужными для обучения полями. Этот пример использует страницу листинга товаров как источник и извлекает название, цену, рейтинг и текст отзыва, но форма применима везде: выберите поля, сопоставьте каждое с селектором, верните словарь. Небольшой вспомогательный метод превращает отсутствующий элемент в пустую строку, а не в сбой.
from bs4 import BeautifulSoup def text_or_empty(node, selector): el = node.select_one(selector) return el.get_text(strip=True) if el else "" def parse_products(page): soup = BeautifulSoup(page["html"], "html.parser") rows = [] for card in soup.select(".product-card"): rows.append({ "name": text_or_empty(card, ".title"), "price": text_or_empty(card, ".price"), "rating": text_or_empty(card, ".rating"), "review": text_or_empty(card, ".review-snippet"), "source": page["url"], }) return rows
Воспринимайте приведённые выше селекторы как начальный шаблон, а не как контракт: имена классов и атрибуты данных меняются без предупреждения, поэтому когда извлечение возвращает пустые поля, заново проинспектируйте живую страницу в браузерных dev tools и обновите их. Это обычное обслуживание для любого production-скрапера.
Шаг 3: создание pandas dataframe
Имея список записей, pandas создаёт dataframe в одну строку и предоставляет инструментарий для всего дальнейшего. Соберите, разберите и загрузите все строки, а затем посмотрите, что получилось, прежде чем доверять результату. Шаги deduplicate и dropna важнее, чем кажется: датасет, полный дублирующих или наполовину пустых строк, учит модель не тому.
import pandas as pd urls = [ "https://www.example.com/category/page/1", "https://www.example.com/category/page/2", ] records = [] for page in collect(urls): records.extend(parse_products(page)) df = pd.DataFrame(records) df = df.drop_duplicates(subset=["name", "source"]) df = df.dropna(subset=["name"]) print(df.shape) print(df.head()) df.to_csv("dataset_raw.csv", index=False)
Запись dataset_raw.csv на этом этапе создаёт контрольную точку: сбор медленный и rate-limited, поэтому никогда не хочется повторно скрапить только потому, что в последующем шаге очистки была ошибка. Загружайте CSV для остальной части конвейера и оставляйте сборщик как отдельную периодическую задачу.
Шаг 4: очистка и разметка строк
Собранные поля поступают как «грязные» строки: цена: "$118", рейтинг: "4.5 out of 5", отзыв: свободный текст. Модели нужны числа и целевой столбец, поэтому этот шаг нормализует сырые поля и выводит метку. Здесь метка: простой прокси настроения из рейтинга, что превращает неразмеченный скрап в датасет для классификации с учителем.
import re import pandas as pd df = pd.read_csv("dataset_raw.csv") def to_float(value): match = re.search(r"(\d+(?:\.\d+)?)", str(value)) return float(match.group(1)) if match else None df["price"] = df["price"].apply(to_float) df["rating"] = df["rating"].apply(to_float) df["review"] = df["review"].fillna("").str.strip() # derive a supervised label from the rating df = df.dropna(subset=["rating"]) df["label"] = (df["rating"] >= 4.0).astype(int) print(df["label"].value_counts()) df.to_csv("dataset_clean.csv", index=False)
Проверка value_counts по метке не опциональна. Собранные данные редко сбалансированы, и целевой столбец, где 95 процентов принадлежат одному классу, породит модель, которая выглядит точной, ничему не научившись. Если соотношение перекошено, восстановите баланс перед обучением путём ресемплирования или взвешивания классов. Подробное рассмотрение нормализации собранных полей для ML: в статье structure and clean web scraped data for AI and ML.
Шаг 5: подготовка признаков для модели
Последний шаг превращает чистый dataframe в числовую матрицу, на которой обучается модель. Текст нуждается в векторизации, а числовые столбцы выигрывают от масштабирования, поэтому ColumnTransformer из scikit-learn применяет нужное преобразование к каждому столбцу за один проход. Результат: матрица признаков X и вектор меток y, разделённые на обучающую и тестовую выборки, готовые к передаче любому оценщику.
import pandas as pd from sklearn.model_selection import train_test_split from sklearn.compose import ColumnTransformer from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.preprocessing import StandardScaler df = pd.read_csv("dataset_clean.csv").fillna({"review": ""}) features = df[["review", "price"]] y = df["label"] pre = ColumnTransformer([ ("text", TfidfVectorizer(max_features=5000), "review"), ("num", StandardScaler(), ["price"]), ]) X = pre.fit_transform(features) X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42, stratify=y ) print(f"train: {X_train.shape}, test: {X_test.shape}")
Отсюда X_train и y_train напрямую передаются в метод fit любого оценщика scikit-learn, а отложенная тестовая выборка даёт честную оценку точности. Аргумент stratify=y сохраняет баланс классов в обеих частях разбивки, что особенно важно при перекошенных метках. Цепочка «сбор → признаки»: переиспользуемая часть: замените селекторы и правило разметки, и те же пять шагов создадут датасет для другой задачи.
Поддержание работоспособности слоя сбора
Датасет, который можно пересобрать по запросу, ценнее разового дампа, поэтому сборщик должен продолжать работать по мере изменения целей. Несколько привычек поддерживают длительный запуск в здоровом состоянии.
- Паузы и ротация. Распределяйте запросы во времени и маршрутизируйте через ротируемые жилые IP, чтобы ни один адрес не превысил ограничение скорости. Crawling API управляет ротацией за вас; если вы строите собственный стек, именно на этой части нужно сосредоточиться.
- Читайте коды статусов. Запуск, начавший возвращать задачи-вызовы, сигнализирует о том, что текущий уровень скорости или IP-tier уже недостаточен. Воспринимайте это как сигнал и снижайте темп, а не продолжайте ретраить до блокировки.
- Контрольные точки с сырым HTML. Сохраняйте то, что загружаете, перед разбором, чтобы ошибка парсера никогда не стоила вам повторного скрапинга.
Полный план действий см. в статье how to scrape websites without getting blocked. А когда датасет готов, статья AI data extraction and how it works рассказывает о более автоматизированном превращении сложных страниц в структурированные поля.
Честная часть: этика и законность
Создание ML-датасета несёт те же обязанности, что и любой скрапинг, а то, разрешено ли это, зависит от условий использования каждого сайта, вашей юрисдикции и того, что вы делаете с данными. Собирайте только публичные данные, соблюдайте robots.txt каждого сайта и заявленные ожидания по скорости, а также держите объём запросов достаточно низким, чтобы не нагружать серверы.
Для ML особенно важны два момента. Никогда не собирайте персональные данные или что-либо, связанное с идентифицируемыми людьми, и будьте осторожны, чтобы производная метка или признак не восстанавливали их. И помните: модель наследует предвзятость своих обучающих данных: набор, собранный из одного региона, языка или платформы, порождает модель, которая плохо обобщается за его пределами. Для коммерческого повторного использования получайте разрешение или официальное соглашение о данных, а не предполагайте, что молчание означает согласие.
Ключевые выводы
- Веб-данные разнообразны, актуальны и обширны. Именно эти три свойства нужны модели для обобщения, и именно поэтому скрапинг питает так много ML-датасетов.
- Надёжность: трудная часть, а не парсинг. Рендерите клиентские страницы, ротируйте IP и делайте паузы между запросами, иначе сборщик упадёт раньше, чем наберётся достаточно строк.
- Crawling API сворачивает рендеринг и ротацию в один вызов. Используйте JS-токен для страниц с клиентским рендерингом и обычный токен для статических, выбирая по источнику.
- Очищайте и размечайте перед обучением. Нормализуйте «грязные» строки в числа, выведите целевой столбец и всегда проверяйте баланс классов.
- Подготовка признаков делает датасет готовым для модели. Векторизуйте текст, масштабируйте числа и разбейте на обучающую и тестовую выборки за один проход через scikit-learn transformer.
- Оставайтесь на публичных данных. Соблюдайте ToS и robots.txt, избегайте персональных данных и следите за предвзятостью, которую закладывает тренировочный набор.
Часто задаваемые вопросы
Используется ли веб-скрапинг в машинном обучении?
Да, активно. Возможность собирать большие объёмы публичных данных из множества источников позволяет создавать обучающие наборы, которые больше и разнообразнее, чем данные, размеченные вручную, что и помогает модели обобщаться. Скрапинг также поддерживает датасет актуальным, поэтому модели в быстро меняющихся областях: ценообразовании или анализе тональности: остаются согласованными с текущими условиями, а не учатся на устаревших снимках.
Как собирать веб-данные для датасета машинного обучения в масштабе?
Узкое место: надёжность, а не парсинг. Многие страницы рендерятся на клиенте, а большинство коммерческих сайтов блокируют автоматический трафик, поэтому для загрузки тысяч страниц без прерывания нужны рендеринг и доверенный ротируемый пул IP. Crawling API решает обе задачи за один вызов: отправьте URL, получите готовый HTML и разберите его в записи. Делайте паузы между запросами, сохраняйте контрольные точки с сырым HTML и держите сборщик как отдельную задачу от остальной части конвейера.
Нужен ли обычный token или JS-token?
Зависит от источника. Обычный токен загружает статический HTML и быстрее и дешевле, поэтому используйте его, когда страница уже содержит нужные данные. JS-токен сначала рендерит страницу в реальном браузере, что необходимо для сайтов с клиентским рендерингом, где обычный запрос возвращает пустую оболочку. Выбирайте по источнику, а не один вариант для всей задачи.
Как превратить собранные страницы в размеченный датасет?
Разберите каждую страницу в плоские записи, загрузите их в pandas dataframe, затем очистите и разметьте. Нормализуйте «грязные» поля в числа (удалите символы валют, извлеките рейтинги), удалите дубликаты и пустые строки, а затем выведите целевой столбец из поля, которому доверяете, например присвоив высокому рейтингу положительную метку. Всегда проверяйте баланс классов перед обучением, потому что собранные данные редко сбалансированы.
Как подготовить собранные данные как признаки для модели?
Преобразуйте каждый столбец в числовую форму, которую модель может читать. Векторизуйте текстовые поля с помощью TF-IDF, масштабируйте числовые столбцы, чтобы ни один признак не доминировал, и примените оба шага за один проход через scikit-learn ColumnTransformer. Разделите результат на обучающую и тестовую выборки, стратифицируя по метке для сохранения баланса классов, и матрица признаков готова для обучения любого оценщика.
Законно ли скрапить данные для машинного обучения?
Зависит от условий использования каждого сайта, вашей юрисдикции и цели. Строго придерживайтесь публичных данных, соблюдайте robots.txt и ожидания по скорости, никогда не собирайте персональные данные или что-либо, связанное с идентифицируемыми людьми. Помните, что модель наследует предвзятость своего тренировочного набора. Для коммерческого повторного использования получайте разрешение или официальное соглашение о данных, а не полагайтесь на скрапер.
Обходите любой сайт в масштабе, без борьбы с инфраструктурой.
Crawlbase берёт на себя прокси, отпечатки и CAPTCHA, чтобы ваша команда выпускала конвейеры данных вместо поддержки обвязки краулинга. 1 000 запросов бесплатно, без карты.
