Python является стандартным языком для веб-скрапинга, и на это есть веская причина: несколько строк requests и BeautifulSoup превращают живую веб-страницу в структурированные данные, которые можно сохранять, запрашивать и анализировать. Если вы умеете читать HTML и писать циклы, вы можете создать работающий скрапер уже сегодня.

В этом руководстве показано, как скрапить веб-сайт с помощью Python от начала до конца. Вы устанавливаете стандартный стек, получаете страницу, парсите её, выбираете нужные элементы, извлекаете чистые поля, обходите пагинацию в цикле и записываете результаты в CSV. В качестве примера используется публичный учебный сайт, поэтому каждый фрагмент кода реально работает. Затем следует честная часть: обычный requests не справляется со страницами, отрендеренными JavaScript, и блокируется при масштабировании, поэтому вы увидите, как Crawling API решает обе проблемы в одном вызове.

Что вы создадите

Небольшой Python-скрапер, который читает пагинированный список цитат с публичного учебного сайта, извлекает текст каждой цитаты, автора и теги, следует ссылке «следующая» до тех пор, пока страницы не закончатся, и сохраняет всё в файл CSV. Тот же паттерн, получить, затем распарсить, затем цикл, затем сохранить, является основой почти каждого скрапера, который вам когда-либо придётся писать.

Целью является quotes.toscrape.com, сайт, созданный специально для обучения скрапингу. Он статичен, хорошо структурирован и допускает скрапинг, поэтому позволяет сосредоточиться на технике, не борясь с блокировками с первой попытки.

Предварительные требования

Для начала вам не нужно многого.

Базовые знания Python. Вы должны уметь запускать скрипты и устанавливать пакеты с помощью pip. Циклов, функций и словарей будет достаточно.

Python 3.8 или выше. Проверьте свою версию командой python --version. Если у вас её нет, установите с python.org.

Это всё для первой части руководства. Две нужные библиотеки устанавливаются одной командой, что мы рассмотрим далее.

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

Создайте виртуальную среду, чтобы зависимости проекта оставались изолированными от остальной системы, а затем установите две библиотеки, которые выполняют работу.

bash
python --version

python -m venv scraper_env
source scraper_env/bin/activate

pip install requests beautifulsoup4

В Windows активируйте среду командой scraper_env\Scripts\activate вместо строки с source. Две зависимости обеспечивают прохождение руководства: requests получает страницу по HTTP, а beautifulsoup4 парсит возвращённый HTML, чтобы вы могли извлекать элементы по тегу и CSS-классу.

Шаг 1: Получение страницы

Каждый скрапинг начинается с одного HTTP-запроса. Отправьте GET на URL, проверьте, что код статуса равен 200, перед тем как делать что-либо ещё, и HTML страницы окажется в ваших руках.

python
import requests

url = "https://quotes.toscrape.com/page/1/"
headers = {"User-Agent": "Mozilla/5.0 (scraper tutorial)"}

response = requests.get(url, headers=headers, timeout=10)
if response.status_code == 200:
    print(response.text[:500])
else:
    print(f"Request failed: {response.status_code}")

Две небольшие привычки сразу же дают результат. Заголовок User-Agent делает ваш запрос похожим на запрос браузера, а не анонимного скрипта, что предпочитают многие сайты. Параметр timeout предотвращает зависание скрапера при зависании сервера. Запустите это, и вы должны увидеть первые 500 символов реального HTML в терминале. Это подтверждает работоспособность получения данных ещё до написания единого селектора.

Шаг 2: Парсинг HTML с помощью BeautifulSoup

Сырой HTML, это просто строка. Чтобы выбирать элементы, загрузите его в BeautifulSoup, который превращает разметку в дерево, которое можно запрашивать по имени тега и CSS-классу. Откройте страницу в браузере, щёлкните правой кнопкой мыши на цитате и выберите «Inspect», чтобы увидеть структуру: на этом сайте каждая цитата находится в div.quote, текст, в span.text, автор, в small.author, а теги, в a.tag.

python
from bs4 import BeautifulSoup

soup = BeautifulSoup(response.text, "html.parser")
quotes = soup.select("div.quote")

print(f"Found {len(quotes)} quotes on this page")

Аргумент html.parser указывает BeautifulSoup, какой движок использовать; он поставляется с Python и не требует дополнительной установки. Метод select принимает CSS-селектор и возвращает каждый совпадающий элемент в виде списка, поэтому div.quote даёт вам все десять блоков цитат на странице. Если вы предпочитаете find и find_all, они делают то же самое в стиле вызова методов вместо селекторов. Для более глубокого ознакомления с обоими подходами смотрите статью о том, как использовать BeautifulSoup в Python.

Шаг 3: Извлечение полей

Теперь извлеките данные из каждого блока цитат. Пройдитесь по элементам в цикле, читайте текст из каждого дочернего элемента и собирайте чистый словарь для каждой цитаты. Обёртка селекторов в небольшой вспомогательный метод предотвращает прерывание всего запуска из-за одного отсутствующего поля.

python
def text_of(element, selector):
    el = element.select_one(selector)
    return el.get_text(strip=True) if el else None

def parse_quotes(soup):
    rows = []
    for quote in soup.select("div.quote"):
        tags = [t.get_text(strip=True) for t in quote.select("a.tag")]
        rows.append({
            "text": text_of(quote, "span.text"),
            "author": text_of(quote, "small.author"),
            "tags": ", ".join(tags),
        })
    return rows

Вспомогательный метод text_of одновременно делает две полезные вещи: запрашивает один элемент и возвращает None, когда элемент отсутствует, вместо того чтобы генерировать исключение при вызове .get_text() от ничего. Тегам нужно списочное включение, поскольку их несколько на цитату, а объединение их в одну строку делает каждую строку плоской и удобной для CSV. Вызовите parse_quotes(soup) и получите аккуратный список словарей, по одному на цитату.

Шаг 4: Следование по пагинации

Одна страница, это демонстрация; реальный список охватывает много страниц. Этот сайт ссылается на следующую страницу элементом li.next a, и когда он исчезает, вы достигаете конца. Поэтому цикл прост: получите текущую страницу, распарсите её, найдите следующую ссылку и повторите, пока следующей ссылки нет.

python
import time

BASE = "https://quotes.toscrape.com"

def scrape_all():
    all_rows = []
    next_url = f"{BASE}/page/1/"
    while next_url:
        response = requests.get(next_url, headers=headers, timeout=10)
        if response.status_code != 200:
            print(f"Stopped at {next_url}: {response.status_code}")
            break
        soup = BeautifulSoup(response.text, "html.parser")
        all_rows.extend(parse_quotes(soup))

        next_link = soup.select_one("li.next a")
        next_url = BASE + next_link["href"] if next_link else None
        time.sleep(1)
    return all_rows

Цикл while next_url выполняется, пока селектор следующей ссылки не возвращает ничего, после чего next_url становится None и цикл естественным образом завершается. Атрибут href на сайте является относительным, поэтому добавьте базовый URL, чтобы сделать его абсолютным. Вставка time.sleep(1) между страницами, не опциональная вежливость на реальной цели: регулирование темпа запросов является самым простым способом оставаться в рамках ограничений скорости сайта.

Шаг 5: Сохранение в CSV

Данные, которые существуют только в памяти, исчезают при завершении скрипта. Запишите их в файл CSV, чтобы можно было открыть в электронной таблице, загрузить в pandas или передать в то, что следует дальше. Встроенный модуль Python csv справляется с этим без дополнительных зависимостей.

python
import csv

def save_csv(rows, filename="quotes.csv"):
    if not rows:
        return
    with open(filename, "w", newline="", encoding="utf-8") as f:
        writer = csv.DictWriter(f, fieldnames=rows[0].keys())
        writer.writeheader()
        writer.writerows(rows)

if __name__ == "__main__":
    data = scrape_all()
    save_csv(data)
    print(f"Saved {len(data)} quotes to quotes.csv")

DictWriter сопоставляет ключи каждого словаря со столбцами CSV, поэтому заголовочная строка пишется сама из выбранных вами имён полей. Аргумент newline="" предотвращает появление пустых строк между записями в Windows, а encoding="utf-8" сохраняет кавычки и имена авторов с диакритическими знаками. Запустите скрипт и получите полный CSV всех цитат на всех страницах. Это полностью рабочий скрапер.

Где обычный requests перестаёт работать

Приведённый выше учебный сайт является статичным, и именно поэтому он хорош как первая цель. Реальные сайты редко бывают такими доброжелательными. Два типа проблем возникают в тот момент, когда вы направляете этот код на серьёзную цель, и ни одну из них нельзя решить корректировкой селекторов.

Страницы с JavaScript-рендерингом

Многие современные сайты отправляют почти пустую HTML-оболочку и строят видимый контент в браузере с помощью JavaScript. requests получает только эту начальную оболочку; скрипты он не запускает. Поэтому при парсинге ответа вы не находите никаких данных, которые видели в браузере, потому что они появляются только после выполнения JavaScript страницы. Обычный запрос просто не может их увидеть. Для полного понимания этой проблемы смотрите статью о том, как скрапить JavaScript-страницы с помощью Python.

Блокировки при масштабировании

Вторая стена, это защита от ботов. IP-адреса датацентров, повторяющиеся паттерны запросов и трафик, не похожий на реальный браузер, получают CAPTCHA или полную блокировку. Ваш скрапер может работать десять запросов, а затем начать возвращать 403 или пустые страницы. Добавление заголовков и задержек немного помогает, но при реальных объёмах вам нужны IP-адреса, воспринимаемые как настоящие пользователи, что одна машина обеспечить не может. Более глубокое руководство находится в статье о том, как скрапить сайты без блокировок.

Решение: рендеринг и ротация в одном вызове

Вы можете решить обе проблемы самостоятельно, запустив безголовый браузер для рендеринга JavaScript и поддерживая пул ротируемых жилых прокси для IP. Это работает, но соединение этих компонентов и поддержание их работоспособности составляет большую часть инженерных усилий, и они никак не связаны с данными, которые вам реально нужны.

Crawling API объединяет оба аспекта в одном запросе. Вы отправляете ему URL, он рендерит страницу в реальном браузере за доверенным ротируемым IP и возвращает готовый HTML для парсинга с тем же самым кодом BeautifulSoup, который вы уже написали. Установите официальный клиент рядом с уже имеющимися библиотеками.

bash
pip install crawlbase

Вот сравнение «до» и «после». Обычный запрос на страницу с интенсивным JavaScript возвращает оболочку; вызов Crawling API возвращает отрендеренную страницу. Слой парсинга ниже не изменяется вообще.

python
# Before: plain requests, breaks on JS pages and blocks
response = requests.get(url, headers=headers, timeout=10)
html = response.text

# After: Crawling API renders the page behind a trusted IP
from crawlbase import CrawlingAPI

api = CrawlingAPI({"token": "YOUR_CRAWLBASE_JS_TOKEN"})
options = {"ajax_wait": "true", "page_wait": 5000}
result = api.get(url, options)
html = result["body"].decode("utf-8") if result["status_code"] == 200 else None

# Same parser as before, unchanged
soup = BeautifulSoup(html, "html.parser")
rows = parse_quotes(soup)

Два параметра ожидания важны для цели с клиентским рендерингом. ajax_wait сообщает API ждать завершения асинхронной загрузки контента, а page_wait удерживает выполнение в течение фиксированного количества миллисекунд после загрузки, чтобы поздно появляющиеся элементы присутствовали перед захватом. Для сайтов с рендерингом в браузере используйте JavaScript-токен; для статичных страниц обычный токен быстрее. Главное, что html передаётся в ту же функцию parse_quotes, которую вы написали в шаге 3, поэтому переход на API, это замена одной строки, а не переписывание.

Which token to use

Crawlbase предлагает два типа токенов. Обычный токен получает статический HTML, который является всем, что нужно для сайта с цитатами. JavaScript (JS) токен сначала рендерит страницу в реальном браузере, что необходимо для любого сайта, строящего контент на стороне клиента. Если парсированные поля возвращаются пустыми на реальной цели, переключение на JS-токен обычно является решением.

Crawlbase Crawling API

Обычный requests не справляется с JavaScript-страницами и блокируется при масштабировании. Crawling API рендерит страницу в реальном браузере, ротирует жилые IP на стороне сервера и передаёт готовый HTML, чтобы уже написанный код BeautifulSoup продолжал работать на целях, которые голый запрос не может достичь. Попробуйте на бесплатном тарифе, прежде чем запускать собственный безголовый флот.

Полезные библиотеки Python для скрапинга

Стек из двух библиотек справляется с большинством статичных задач, но по мере роста потребностей стоит знать о нескольких других.

  • requests, рабочая лошадка HTTP-клиента для получения страниц. Простой, надёжный и правильный выбор по умолчанию для статичных целей.
  • BeautifulSoup парсит HTML и XML в навигируемое дерево. Он терпим к замусоренной разметке, которая всегда встречается на реальных страницах.
  • Selenium управляет реальным браузером, поэтому может рендерить JavaScript и взаимодействовать со страницами через клики и ввод. Мощный, но тяжёлый в работе и медленный при больших объёмах.
  • Scrapy, полноценный фреймворк для краулинга со встроенными параллелизмом, повторными попытками и конвейерами. Используйте его, когда один скрипт вырастает в реальный проект.
  • pandas не является скрапером, но именно сюда часто попадают скрапированные данные для очистки, анализа и экспорта в другие форматы.

Привычки, которые поддерживают скрапер в рабочем состоянии

Скрапер, работающий один раз, сделать легко; тот, что продолжает работать, требует нескольких дисциплин. Они применимы независимо от того, используете ли вы обычные requests или управляемый API.

  • Регулируйте темп запросов. Небольшая задержка между запросами, как time.sleep(1) выше, удерживает вас в рамках ограничений скорости и вне списков блокировок. Интенсивная работа с сайтом в тесном цикле, самый быстрый способ получить ограничение.
  • Обрабатывайте ошибки. Страницы меняются, поля пропадают, серверы дают сбои. Проверяйте коды статусов, защищайте селекторы от None и оборачивайте рискованный парсинг, чтобы одна плохая страница не прерывала весь запуск.
  • Ожидайте дрейфа разметки. Имена классов и структуры меняются без предупреждения. Когда поле начинает возвращаться пустым, повторно проверьте живую страницу и обновите селектор. Периодическое обслуживание нормально, это не признак сломанного скрапера.
  • Уважайте цель. Читайте robots.txt сайта и его условия, держите объём разумным и собирайте только публичные данные.
Итоги

Ключевые выводы

  • Основной цикл: получить, распарсить, повторить, сохранить. requests получает HTML, BeautifulSoup извлекает поля, пагинация обходит страницы, а модуль csv сохраняет результат.
  • Проверяйте перед выбором. Откройте инструменты разработчика страницы, чтобы найти теги и классы, содержащие ваши данные, а затем сопоставьте каждое поле с CSS-селектором.
  • Регулируйте темп и защищайте код. Добавляйте задержку между запросами и возвращайте None при отсутствующих элементах, чтобы одна плохая страница не прерывала запуск.
  • Обычный requests имеет два ограничения. Он не может запускать JavaScript и блокируется при масштабировании, ни одно из этих ограничений нельзя исправить селекторами.
  • Crawling API решает оба в одном вызове. Он рендерит страницу за доверенным ротируемым IP и возвращает готовый HTML, чтобы ваш существующий парсер BeautifulSoup продолжал работать без изменений.

Часто задаваемые вопросы

Нужны ли оба requests и BeautifulSoup?

Для типичного статичного сайта да, и они естественным образом дополняют друг друга. requests получает страницу по HTTP и передаёт сырой HTML в виде строки; BeautifulSoup превращает эту строку в дерево, которое можно запрашивать по тегу и CSS-классу для извлечения отдельных полей. requests скачивает, BeautifulSoup извлекает.

Почему мои скрапированные данные пустые, если на странице явно есть контент?

Почти всегда потому, что сайт рендерит контент с помощью JavaScript. requests получает только начальную HTML-оболочку и не запускает скрипты, поэтому данные, которые вы видите в браузере, отсутствуют в том, что вы парсите. Сначала нужно отрендерить страницу, либо с помощью безголового браузера, либо с JavaScript-токеном Crawling API, прежде чем BeautifulSoup сможет найти поля.

Как скрапить несколько страниц?

Найдите ссылку или паттерн, который сайт использует для перехода на следующую страницу, и зациклите. Если есть кнопка «следующая», следуйте по её href до исчезновения, как показано в шаге 4. Если URL следуют числовому паттерну вроде /page/2/, вместо этого можно построить их в цикле с диапазоном. В любом случае добавляйте короткую задержку между страницами, чтобы оставаться вежливым и не получать блокировок.

Как избежать блокировки при скрапинге?

Регулируйте темп запросов с задержкой, отправляйте реалистичный заголовок User-Agent и варьируйте цели вместо интенсивного обхода одного пути. При масштабировании вам также нужны IP-адреса, выглядящие как реальные пользователи, что одна машина обеспечить не может. Маршрутизация через ротируемые жилые IP, через Crawling API или Smart AI Proxy, удерживает запуски с большим объёмом в рамках ограничений скорости.

Когда использовать Crawling API вместо обычного requests?

Используйте обычный requests для статичных целей с малым объёмом, где голый запрос возвращает данные, как учебный сайт в этом руководстве. Переходите на Crawling API, когда страница рендерится JavaScript, когда вас блокируют или запрашивают CAPTCHA, или когда нужно скрапить при объёме, который один IP не может выдержать. Поскольку API возвращает HTML, ваш существующий парсер не изменяется.

Законен ли веб-скрапинг с Python?

Скрапинг публичных данных в целом допустим, но это зависит от условий использования сайта, вашей юрисдикции и того, что вы делаете с данными. Проверьте robots.txt сайта и условия перед началом, избегайте персональных данных, охраняемых законами о конфиденциальности, такими как GDPR, и никогда не скрапьте контент за логином. Если сомневаетесь, собирайте только публичные данные и держите объём достаточно низким, чтобы не перегружать сервер.

Начать создавать

Обходите любой сайт в масштабе, без борьбы с инфраструктурой.

Crawlbase берёт на себя прокси, отпечатки и CAPTCHA, чтобы ваша команда выпускала конвейеры данных вместо поддержки обвязки краулинга. 1 000 запросов бесплатно, без карты.

Самообслуживание · Звонок отдела продаж не требуется · Доступны корпоративные объёмы краулинга