Парсер веб-страниц на Python — это скрипт, который автоматически скачивает HTML страницы, находит в нём нужные элементы и вытаскивает структурированные данные: заголовки, цены, ссылки, даты, описания. Для простого старта обычно хватает двух инструментов: библиотека для HTTP-запросов и парсер HTML; стандартный html.parser входит в Python, а для удобной навигации по дереву документа часто используют Beautiful Soup.
На практике такой скрипт становится первым шагом к автоматизации сбора данных с досок объявлений, API застройщиков или риелторских CRM — ровно с этого я когда-то начинал, когда перестал обновлять сайты вручную и написал сборщик, который приводил разрозненные объявления к одному формату.
Зачем вообще нужен простой парсер
На практике парсер нужен не «ради парсинга», а ради экономии времени и снижения рутины. Если вы вручную мониторите объявления, цены, вакансии, каталоги или новости, скрипт может делать это за вас по расписанию и собирать данные в таблицу, JSON или базу.
Для real-world задач это особенно полезно, когда нужно:
- сравнивать цены на нескольких площадках;
- отслеживать новые объявления;
- собирать список ссылок и метаданных;
- находить изменения на странице;
- делать первичную аналитику по рынку.
В моём случае автоматизация поиска жилья началась ровно с этого: вместо бесконечного обновления досок объявлений появился небольшой сборщик, который забирал данные с разных страниц и приводил их к одному формату. Позже этот подход перерос в полноценный инструмент аналитики цен и оценки ликвидности, но фундамент остался тем же — простой парсер, который корректно собирает и нормализует данные.
Как устроен парсер в простом виде
У любого простого парсера есть три шага:
- Скачать HTML страницы.
- Разобрать структуру документа.
- Вытащить нужные поля и сохранить результат.
Схема выглядит так: GET-запрос → HTML → разбор дерева → структурированные данные.
Если страница статическая, этого часто достаточно. Если контент рисуется JavaScript’ом, простой запрос может вернуть только «оболочку» страницы без нужных данных, и тогда потребуется другой подход. На досках объявлений это встречается часто: блок с фильтрами и карточками может подгружаться через AJAX, поэтому перед написанием кода всегда проверяю, есть ли нужные цифры в исходном HTML.
Что выбрать: requests, Beautiful Soup или html.parser
Для простого парсера обычно используют связку:
requests— чтобы получить HTML страницы;- Beautiful Soup — чтобы удобно искать элементы по тегам, классам и атрибутам;
html.parser— стандартный встроенный HTML-парсер Python, который может использоваться как backend для разбора HTML.
Коротко по ролям
| Инструмент | Задача | Когда использовать |
|---|---|---|
requests |
Скачать HTML по URL | Почти всегда на старте |
| Beautiful Soup | Найти элементы на странице | Когда нужно быстро и понятно разбирать DOM |
html.parser |
Базовый HTML-парсер из стандартной библиотеки | Когда хочется без лишних зависимостей или как backend для обработки HTML |
Если нужен именно «простой парсер», чаще всего лучше начать с requests + Beautiful Soup. Это самый понятный путь для учебного и прикладного проекта. В своих первых сборщиках объявлений я использовал именно эту пару, и она до сих пор закрывает 80% задач по статическим страницам.
Подготовка окружения
Для начала установите нужные пакеты:
pip install requests beautifulsoup4
Дальше создайте файл, например parser.py, и проверьте, что Python корректно запускает скрипт.
Первый рабочий пример
Ниже базовый пример: скачиваем страницу, парсим HTML и достаём заголовок.
import requests
from bs4 import BeautifulSoup
url = "https://example.com"
response = requests.get(url, timeout=10)
response.raise_for_status()
soup = BeautifulSoup(response.text, "html.parser")
print(soup.title)
Что здесь происходит
requests.get()отправляет GET-запрос на страницу.timeout=10защищает от бесконечного ожидания.raise_for_status()сразу покажет ошибку, если сервер вернул неуспешный код.BeautifulSoup(..., "html.parser")создаёт объект дерева документа.soup.titleищет тег<title>.
Это минимальный скелет, на котором уже можно строить реальный сборщик данных. В продакшене я добавляю сюда обработку исключений и логирование, но для старта такой код вполне рабочий.
Как находить нужные элементы на странице
После загрузки страницы нужно понять, по какому признаку выделять нужные данные. Обычно используются:
- тег (
h1,a,div,span); - класс CSS;
- id;
- атрибуты;
- вложенность элементов.
Когда я разбираю страницы с объявлениями, чаще всего ориентируюсь на структуру карточки: каждая карточка — это родительский блок с повторяющимися внутренними элементами. Стабильный класс родителя позволяет вытащить все карточки одной командой.
Пример поиска ссылок
links = soup.find_all('a')
for link in links:
print(link.get('href'))
Пример поиска по классу
prices = soup.find_all(class_='price')
for price in prices:
print(price.get_text(strip=True))
Пример поиска одного элемента
title = soup.find('h1', class_='title')
if title:
print(title.get_text(strip=True))
Пошагово: как написать простой парсер страницы
Шаг 1. Определите, какие данные вам нужны
Не начинайте с кода. Сначала ответьте на вопрос: что именно нужно собрать?
- заголовок;
- цена;
- адрес;
- ссылка;
- дата публикации;
- описание.
Чем точнее список полей, тем проще потом найти селекторы. Для недвижимости я обычно фиксирую: цена, площадь, этаж, адрес, количество комнат, ссылка на объявление — это база для дальнейшей аналитики.
Шаг 2. Откройте страницу в браузере и посмотрите HTML
Используйте инструменты разработчика:
- найдите нужный блок;
- посмотрите его тег;
- проверьте классы и атрибуты;
- убедитесь, что данные действительно есть в исходном HTML, а не подгружаются через JavaScript.
Это критически важный момент: если данные генерируются на клиенте, простой HTTP-запрос их не увидит, и вы потратите время впустую.
Шаг 3. Напишите запрос и проверьте ответ
Сначала просто выведите HTML в консоль или сохраните его в файл.
import requests
url = "https://example.com"
response = requests.get(url, timeout=10)
response.raise_for_status()
print(response.text[:1000]) # первые 1000 символов
Шаг 4. Найдите стабильный селектор
Хрупкая ошибка новичка — парсить по слишком общим или случайным классам. Лучше искать:
- уникальный класс;
- стабильный атрибут;
- структуру карточки;
- родительский блок с повторяющимися элементами.
Шаг 5. Извлеките данные и приведите к единому формату
Например:
title = soup.find('h1', class_='title')
if title:
title_text = title.get_text(strip=True)
else:
title_text = None
Шаг 6. Сохраните результат
Для старта подойдут:
- CSV;
- JSON;
- SQLite;
- Google Sheets через API.
Я предпочитаю SQLite или CSV для локальной аналитики: их легко открыть в Pandas и сразу строить графики цен или распределение предложений по районам.
Практический пример: парсим карточки объявлений
Представим, что на странице есть карточки, у каждой:
- заголовок;
- цена;
- ссылка.
from urllib.parse import urljoin
import requests
from bs4 import BeautifulSoup
url = "https://example.com/listings"
response = requests.get(url, timeout=10)
response.raise_for_status()
soup = BeautifulSoup(response.text, "html.parser")
cards = soup.find_all('div', class_='listing-card')
for card in cards:
title_elem = card.find('h2', class_='listing-title')
price_elem = card.find('span', class_='listing-price')
link_elem = card.find('a', class_='listing-link')
title = title_elem.get_text(strip=True) if title_elem else None
price = price_elem.get_text(strip=True) if price_elem else None
link = urljoin(url, link_elem.get('href')) if link_elem else None
print(title, price, link)
Что важно в этом примере
urljoin()помогает собрать абсолютную ссылку из относительной.get_text(strip=True)убирает лишние пробелы.- Проверки на
Noneзащищают от падения скрипта, если элемент не найден.
Такая структура — типичная для досок объявлений. Когда я писал первый парсер для своей задачи, именно такие карточки и были основным источником данных. Позже пришлось добавить нормализацию цен (убирать «₽», пробелы, переводить в число), чтобы можно было сравнивать предложения между собой.
Типовые ошибки новичков
1. Парсинг без проверки статуса ответа
Если не вызвать raise_for_status(), можно случайно обработать страницу ошибки как обычный HTML. В результате в базу попадут пустые или некорректные записи, а вы этого даже не заметите.
2. Жёсткая привязка к одному классу
Сайт меняет классы, и парсер ломается. Лучше закладывать резервные варианты. Например, сначала пробовать один селектор, потом второй, и логировать, если оба не сработали.
3. Игнорирование JavaScript
Если нужные данные загружаются динамически, requests может не увидеть их в HTML. Тогда простой парсер не поможет без дополнительного инструмента. На многих современных сайтах объявлений карточки рендерятся на клиенте, поэтому приходится либо искать внутренние JSON-эндпоинты, либо использовать headless-браузер.
4. Слишком частые запросы
Если слать много запросов подряд, сайт может начать ограничивать доступ. В продакшене я всегда добавляю задержку между запросами (например, 1–2 секунды) и слежу за кодом ответа: если получаю 429 или 403, делаю паузу.
5. Отсутствие заголовков
Иногда серверы по-разному отвечают на запросы без User-Agent.
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
}
response = requests.get(url, headers=headers, timeout=10)
Как сделать парсер устойчивее
Чтобы скрипт не разваливался при первой же правке верстки, используйте несколько приёмов.
Чек-лист устойчивости
- добавляйте
timeout; - проверяйте
status_code; - обрабатывайте исключения;
- делайте проверки на
None; - сохраняйте «сырой» HTML для отладки;
- логируйте проблемные страницы;
- не завязывайтесь на один-единственный селектор.
Пример обработки ошибок
import requests
from bs4 import BeautifulSoup
def fetch_and_parse(url):
try:
response = requests.get(url, timeout=10)
response.raise_for_status()
soup = BeautifulSoup(response.text, "html.parser")
# ... парсинг
except requests.exceptions.RequestException as e:
print(f"Ошибка запроса: {e}")
except Exception as e:
print(f"Неожиданная ошибка: {e}")
Когда простой парсер не подойдёт
Есть ситуации, где базового подхода недостаточно:
- данные рендерятся только после выполнения JavaScript;
- сайт требует авторизацию;
- есть сложная защита от автоматизации (капча, fingerprinting);
- страница меняет структуру слишком часто;
- нужные данные приходят не в HTML, а через API.
В таких случаях лучше искать:
- внутренние JSON-эндпоинты;
- официальное API;
- выгрузки в открытом формате;
- headless-браузер для рендеринга.
В недвижимости часто есть официальные API у агрегаторов или застройщиков, но они бывают нестабильными или неполными. Поэтому приходится комбинировать: парсить статические куски, а динамические добирать через другие источники.
Мини-архитектура для реального проекта
Если парсер нужен не на один вечер, а как часть инструмента, удобно разделить код на части:
- модуль загрузки страниц;
- модуль разбора HTML;
- модуль нормализации данных;
- модуль сохранения;
- модуль логирования.
Такой подход упрощает поддержку. Если сломалась верстка, вы не переписываете весь проект целиком, а меняете только слой парсинга. В своей платформе для анализа недвижимости я придерживаюсь именно такой модульной схемы: каждый источник (доска, API застройщика, CRM) изолирован, а общий слой агрегации приводит всё к общей схеме с полями «цена», «площадь», «адрес», «ссылка».
Что проверить перед запуском парсера в продакшене
Быстрый чек-лист
- страница доступна без входа;
- нужные данные есть в HTML;
- селекторы не слишком хрупкие;
- настроен таймаут;
- предусмотрена обработка ошибок;
- есть ограничение частоты запросов;
- результат сохраняется в удобный формат;
- сделана проверка на пустые значения.
Вывод
Простой парсер веб-страниц на Python — это несложный, но очень практичный инструмент. В базовом варианте достаточно скачать HTML через requests, разобрать его через Beautiful Soup и достать нужные элементы через find() или find_all(). Для статических страниц этого уже хватает, а для более сложных задач можно постепенно наращивать архитектуру: добавить сохранение, логирование, обработку ошибок и работу с несколькими источниками.
Если задача связана с мониторингом объявлений, цен или изменений на сайте, начинать лучше именно с простого парсера: он быстрее всего даёт полезный результат и помогает понять структуру данных на практике. С этого начиналась и моя автоматизация сбора данных о недвижимости — и со временем выросла в систему, которая экономит часы ручного просмотра.
FAQ
Можно ли написать парсер только на стандартной библиотеке Python?
Да, для простых задач можно использовать встроенный html.parser, который предназначен для разбора HTML и XHTML. Но для удобного поиска элементов обычно проще взять Beautiful Soup.
Почему парсер не видит данные на странице?
Чаще всего причина в том, что данные подгружаются JavaScript’ом, а requests получает только исходный HTML, без отрисовки браузером. В таком случае нужно искать API или использовать браузерный рендеринг.
Какой минимальный набор библиотек нужен новичку?
Обычно достаточно requests и beautifulsoup4. Этого хватает, чтобы скачать страницу и разобрать HTML в большинстве простых сценариев.
Что лучше использовать для разбора HTML: `html.parser` или Beautiful Soup?
Если нужна базовая стандартная возможность — подойдёт html.parser. Если нужен удобный и быстрый способ навигации по дереву документа, чаще выбирают Beautiful Soup.
Нужно ли ставить защиту от ошибок сразу?
Да. Даже простой парсер должен проверять статус ответа, использовать таймаут и учитывать пустые элементы, иначе он быстро ломается на реальных страницах.
