Парсинг HTML с requests и BeautifulSoup: практическое руководство

Раздел: Веб-скрапинг -> Веб-скрапинг

Основное решение: базовый парсинг с requests и BeautifulSoup

Для извлечения данных из HTML страниц часто используется связка библиотек requests и BeautifulSoup. Первая загружает содержимое страницы, вторая разбирает HTML и позволяет удобно искать элементы. Ниже представлен классический подход.


import requests
from bs4 import BeautifulSoup

url = 'https://example.com'
response = requests.get(url)
soup = BeautifulSoup(response.text, 'html.parser')
# Поиск всех заголовков h2
titles = soup.find_all('h2')
for title in titles:
    print(title.get_text(strip=True))

библиотека beautifulsoup python (библиотека beautifulsoup в python)

После получения ответа от сервера (response.text) мы передаём его конструктору BeautifulSoup. Парсер html.parser встроен в стандартную библиотеку, но можно использовать и более быстрые lxml или html5lib. Метод find_all возвращает все найденные теги, а get_text очищает текст от вложенных тегов.

Возможные ошибки и их устранение:

  • Ошибка ConnectionError – проверьте интернет-соединение или правильность URL.
  • Код статуса 403 или 404 – сервер может блокировать запросы. Рекомендуется добавить заголовок User-Agent.
  • BeautifulSoup не находит элементы – возможно, контент загружается через JavaScript. В этом случае потребуется другой подход (см. варианты ниже).

Как обработать динамически загружаемый контент?

Если данные подгружаются через AJAX или JavaScript, requests не сможет их получить, так как он не исполняет JS. Альтернатива – использовать библиотеку requests для прямого запроса к API, которое отдаёт данные в формате JSON.


import requests
import json

api_url = 'https://api.example.com/data'
params = {'page': 1, 'limit': 10}
response = requests.get(api_url, params=params)
data = response.json()
for item in data['items']:
    print(item['name'])

Python requests beautifulsoup (веб-скрапинг с requests и beautifulsoup)

Таким образом мы получаем данные без парсинга HTML. Если API нет, приходится использовать Selenium или Playwright – это уже выходит за рамки данной статьи.

Проблема: могут потребоваться токены авторизации или куки. Их можно передать через параметр cookies или headers.

Как извлечь данные из нескольких страниц (пагинация)?

Часто информация распределена по множеству страниц. Можно организовать цикл по номерам страниц и собирать данные с каждой.


import requests
from bs4 import BeautifulSoup

base_url = 'https://example.com/products?page='
all_titles = []
for page in range(1, 6):
    url = base_url + str(page)
    resp = requests.get(url)
    soup = BeautifulSoup(resp.text, 'html.parser')
    items = soup.select('.product-title')
    for item in items:
        all_titles.append(item.get_text(strip=True))
print(f'Собрано {len(all_titles)} заголовков')

Html в файлах python (обработка html файлов в python)

Метод select использует CSS-селекторы, что удобно для поиска по классам. Обязательно проверяйте условие выхода из цикла (например, отсутствие кнопки "Следующая страница").

Типичная ошибка – перебор всех страниц без паузы. Это может привести к блокировке IP. Добавьте time.sleep(1) между запросами.

Как обрабатывать ошибки соединения или некорректный HTML?

Используйте исключения для устойчивости скрипта.


import requests
from bs4 import BeautifulSoup
from requests.exceptions import RequestException

url = 'https://example.com'
try:
    response = requests.get(url, timeout=5)
    response.raise_for_status()
    soup = BeautifulSoup(response.content, 'html.parser')
    # парсинг…
except RequestException as e:
    print(f'Ошибка запроса: {e}')
except Exception as e:
    print(f'Ошибка парсинга: {e}')

raise_for_status генерирует исключение при плохом статусе (4xx, 5xx). timeout предотвращает зависание. Также можно использовать try-except при поиске элементов, особенно если их может не быть.

Некорректный HTML (например, незакрытые теги) – BeautifulSoup обычно справляется, но если возникают ошибки, укажите более строгий парсер (lxml).

Расширенные примеры использования requests и BeautifulSoup

Пример 1: Парсинг таблицы с данными

Пример

import requests
from bs4 import BeautifulSoup

url = 'https://example.com/table'
resp = requests.get(url)
soup = BeautifulSoup(resp.text, 'html.parser')
table = soup.find('table', class_='data')
rows = table.find_all('tr')
for row in rows:
    cols = row.find_all('td')
    if cols:
        print([col.get_text(strip=True) for col in cols])
['Name', 'Age', 'City']
['Alice', '30', 'NYC']
['Bob', '25', 'LA']

Здесь находим таблицу по классу, затем все строки и ячейки. Обратите внимание, что первая строка может быть заголовком (th).

Пример 2: Использование сессии для сохранения кук

Пример

import requests
from bs4 import BeautifulSoup

session = requests.Session()
session.headers.update({'User-Agent': 'Mozilla/5.0'})
# Авторизация
login_data = {'username': 'user', 'password': 'pass'}
session.post('https://example.com/login', data=login_data)
# Теперь запросы будут с куками
resp = session.get('https://example.com/profile')
soup = BeautifulSoup(resp.text, 'html.parser')
print(soup.find('h1').get_text())
Профиль пользователя

Сессия автоматически сохраняет куки, переданные сервером. Это удобно для сайтов с аутентификацией.

Пример 3: Извлечение всех ссылок и их текстов

Пример

import requests
from bs4 import BeautifulSoup
from urllib.parse import urljoin

url = 'https://example.com'
resp = requests.get(url)
soup = BeautifulSoup(resp.text, 'html.parser')
for a in soup.find_all('a', href=True):
    href = urljoin(url, a['href'])
    text = a.get_text(strip=True) or 'нет текста'
    print(f'{text} -> {href}')
Главная -> https://example.com/
Контакты -> https://example.com/contact

urljoin преобразует относительные ссылки в абсолютные. Фильтр href=True отбрасывает якоря без ссылок.

Пример 4: Обработка вложенных элементов и атрибутов

Пример

import requests
from bs4 import BeautifulSoup

url = 'https://example.com/articles'
resp = requests.get(url)
soup = BeautifulSoup(resp.text, 'lxml')
articles = soup.select('div.article')
for art in articles:
    title = art.find('h2').get_text(strip=True)
    date = art.find('span', class_='date')['data-time']
    link = art.find('a')['href']
    print(f'{title} | {date} | {link}')
Заголовок статьи | 2025-03-01 | /article/123

Используется комбинация select и find. Атрибуты извлекаются как из словаря (['data-time']).

Пример 5: Работа с кодировкой и нестандартным HTML

Пример

import requests
from bs4 import BeautifulSoup

url = 'https://example.com/old-site'
resp = requests.get(url)
resp.encoding = 'cp1251'  # принудительно
soup = BeautifulSoup(resp.text, 'html5lib')
# html5lib лучше справляется с некорректными тегами
content = soup.find('div', id='content').get_text()
print(content)
Текст с русскими символами

Иногда сервер не передаёт правильную кодировку, её можно задать явно. html5lib парсит даже битый HTML, но медленнее.

Пример 6: Постраничный обход с условием остановки

Пример

import requests
from bs4 import BeautifulSoup
import time

url = 'https://example.com/page/1'
all_items = []
page = 1
while True:
    resp = requests.get(url, headers={'User-Agent': 'Mozilla/5.0'})
    soup = BeautifulSoup(resp.text, 'lxml')
    items = soup.select('.item')
    if not items:
        break
    all_items.extend(items)
    next_page = soup.select_one('a.next')
    if not next_page:
        break
    url = urljoin(url, next_page['href'])
    page += 1
    time.sleep(1)
print(f'Собрано {len(all_items)} элементов')
Собрано 150 элементов

Цикл продолжается, пока есть элементы и ссылка на следующую страницу. Важно обновлять URL и делать паузу.

Пример 7: Извлечение изображений и их атрибутов

Пример

import requests
from bs4 import BeautifulSoup

url = 'https://example.com/gallery'
resp = requests.get(url)
soup = BeautifulSoup(resp.text, 'html.parser')
for img in soup.find_all('img'):
    src = img.get('src')
    alt = img.get('alt', 'нет описания')
    if src:
        print(f'{alt} -> {src}')
Фото 1 -> /images/1.jpg
Фото 2 -> /images/2.jpg

Используем get для безопасного получения атрибутов. Абсолютный путь можно получить через urljoin.

Пример 8: Парсинг JSONP или JavaScript-вызовов

Пример

import requests
import re

url = 'https://example.com/data?callback=myFunc'
resp = requests.get(url)
# Извлекаем JSON из callback
json_text = re.search(r'myFunc\((.*)\)', resp.text).group(1)
import json
data = json.loads(json_text)
print(data['result'])
{'key': 'value'}

Иногда сервер возвращает JSONP. После удаления обёртки мы получаем обычный JSON. Альтернатива – использовать параметр callback с пустым значением.

Веб-скрапинг с requests и BeautifulSoup - comments

En
Python requests beautifulsoup (python)