Получение HTML данных через HTTP-запросы средствами Python

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

Отправка HTTP-запросов к HTML страницам на Python

Для получения HTML кода веб-страницы и последующего парсинга в Python существует несколько способов. Наиболее популярным и простым решением является использование библиотеки requests в связке с BeautifulSoup. Однако в зависимости от задачи могут потребоваться другие инструменты.

Как получить HTML страницы с помощью библиотеки requests и обработать его BeautifulSoup?

Этот подход подходит для большинства сценариев: однократные запросы, работа с сессиями, обработка ошибок. Установка: pip install requests beautifulsoup4

import requests
from bs4 import BeautifulSoup

url = 'https://example.com'
response = requests.get(url)
response.raise_for_status()  # вызовет исключение при ошибке
soup = BeautifulSoup(response.text, 'html.parser')
print(soup.title.text)

Html запросы python (http-запросы к html)

Example Domain

содержимое страницы python (получение содержимого веб-страницы в python)

Пояснение: response.text содержит HTML как строку. BeautifulSoup преобразует её в дерево для удобного поиска элементов. Метод raise_for_status() помогает отловить неудачные коды ответа (4xx, 5xx).

Возможные проблемы и их решение:

  • ConnectionError – нет соединения с сервером; проверьте интернет или url.
  • Timeout – сервер долго отвечает; добавьте параметр timeout=(connect, read).
  • HTTPError – код ошибки (например, 404); используйте raise_for_status() или проверяйте response.status_code.
  • Неправильная кодировка – response.encoding = response.apparent_encoding для автоматического определения.

Как выполнить простой HTTP-запрос без сторонних библиотек?

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

import urllib.request

url = 'https://example.com'
with urllib.request.urlopen(url) as response:
    html = response.read().decode('utf-8')
    print(html[:100])
<!doctype html>
<html>
<head>
    <title>Example Domain</title>

Пояснение: urlopen возвращает файлоподобный объект. Данные читаются байтами, их нужно декодировать. Для настройки заголовков используйте Request объект.

Проблема: отсутствие автоматического следования редиректам (по умолчанию следует, но можно отключить). Также неудобно работать с куками и сессиями.

Как эффективно загрузить несколько HTML страниц одновременно?

Для асинхронных запросов применяется библиотека aiohttp. Она позволяет выполнять запросы конкурентно, ускоряя сбор данных.

import aiohttp
import asyncio

async def fetch(session, url):
    async with session.get(url) as response:
        return await response.text()

async def main():
    urls = ['https://example.com', 'https://httpbin.org/html']
    async with aiohttp.ClientSession() as session:
        tasks = [fetch(session, url) for url in urls]
        results = await asyncio.gather(*tasks)
        for html in results:
            print(len(html))

asyncio.run(main())
1256
321

Пояснение: aiohttp.ClientSession управляет соединениями. asyncio.gather запускает несколько запросов параллельно. Подходит для сбора контента с десятков страниц.

Типичная ошибка: RuntimeError при попытке запустить асинхронный код в неподходящем окружении. Используйте asyncio.run() в основном потоке. Также не забывайте закрывать сессию.

Как работать с современными протоколами и гибкими настройками тайм-аутов?

Библиотека httpx поддерживает HTTP/1.1 и HTTP/2, а также предоставляет удобный интерфейс с тайм-аутами и сессиями.

import httpx

with httpx.Client(http2=True, timeout=10.0) as client:
    response = client.get('https://example.com')
    print(response.http_version)
HTTP/2

Пояснение: Параметр http2=True включает поддержку HTTP/2. Тайм-аут задаётся в секундах. httpx также умеет автоматически следовать редиректам и управлять куками.

Проблема: httpx может не быть установлен по умолчанию, требуется pip install httpx[http2]. При большом количестве запросов асинхронная версия (httpx.AsyncClient) даст лучшую производительность.

Как сохранить состояние между запросами (авторизация, куки)?

Использование сессий позволяет сохранять куки, заголовки и параметры между запросами. Это необходимо для обхода login-форм или работы с API.

import requests

session = requests.Session()
session.headers.update({'User-Agent': 'Mozilla/5.0'})
session.get('https://example.com/login')  # получаем куки
# далее запросы будут использовать эти куки
response = session.get('https://example.com/dashboard')
print(response.status_code)
200

Пояснение: Session хранит куки и заголовки. Это удобно для парсинга сайтов, требующих аутентификации.

Ошибка: сессия может быть заблокирована, если не отправлять правильные заголовки (например, Referer). Иногда требуется добавлять задержки между запросами, чтобы избежать блокировки.

Расширенные примеры HTTP-запросов и парсинга HTML

Скачивание всех изображений со страницы

Пример извлекает все теги <img>, получает их атрибуты src и скачивает файлы.

Пример
import requests
from bs4 import BeautifulSoup
from urllib.parse import urljoin
import os

url = 'https://example.com/gallery'
response = requests.get(url)
soup = BeautifulSoup(response.text, 'html.parser')
img_tags = soup.find_all('img')
os.makedirs('images', exist_ok=True)

for img in img_tags:
    src = img.get('src')
    if not src:
        continue
    full_url = urljoin(url, src)
    img_data = requests.get(full_url).content
    filename = os.path.join('images', os.path.basename(src))
    with open(filename, 'wb') as f:
        f.write(img_data)
        print(f'Сохранено {filename}')
Сохранено images/photo1.jpg
Сохранено images/photo2.png

Пояснение: urljoin корректно обрабатывает относительные ссылки. Для больших страниц лучше использовать асинхронную загрузку изображений.

Парсинг данных из таблицы HTML

Извлечение строк таблицы с последующей записью в CSV.

Пример
import requests
from bs4 import BeautifulSoup
import csv

response = requests.get('https://example.com/data')
soup = BeautifulSoup(response.text, 'lxml')
table = soup.find('table', class_='data-table')
rows = table.find_all('tr')

with open('output.csv', 'w', newline='', encoding='utf-8') as csvfile:
    writer = csv.writer(csvfile)
    for row in rows:
        cells = row.find_all(['th', 'td'])
        writer.writerow([cell.get_text(strip=True) for cell in cells])
(файл output.csv создан)

Пояснение: Используется парсер lxml (более быстрый, чем html.parser). Атрибут class_ в BeautifulSoup ищет класс CSS. Метод get_text(strip=True) удаляет лишние пробелы.

Многопоточная загрузка страниц с сохранением результатов

Применение модуля concurrent.futures для параллельного выполнения синхронных запросов.

Пример
import requests
from concurrent.futures import ThreadPoolExecutor, as_completed
from bs4 import BeautifulSoup

def fetch_title(url):
    try:
        resp = requests.get(url, timeout=5)
        resp.raise_for_status()
        soup = BeautifulSoup(resp.text, 'html.parser')
        return url, soup.title.string if soup.title else None
    except Exception as e:
        return url, str(e)

urls = [f'https://example.com/page{i}' for i in range(1, 11)]
with ThreadPoolExecutor(max_workers=5) as executor:
    future_to_url = {executor.submit(fetch_title, url): url for url in urls}
    for future in as_completed(future_to_url):
        url, result = future.result()
        print(f'{url}: {result}')
https://example.com/page1: Page One
https://example.com/page2: Page Two
...

Пояснение: ThreadPoolExecutor выполняет запросы в потоках. Пул из 5 потоков ускоряет загрузку. Обработка ошибок внутри функции предотвращает падение всего скрипта.

Использование прокси для обхода блокировок

Пример
import requests

proxies = {
    'http': 'http://user:password@proxy.example.com:8080',
    'https': 'http://user:password@proxy.example.com:8080'
}
try:
    response = requests.get('https://example.com', proxies=proxies, timeout=10)
    print(response.status_code)
except requests.exceptions.ProxyError:
    print('Не удалось подключиться через прокси')
200

Пояснение: Прокси настраиваются словарём. Для авторизации используйте http://user:pass@host:port. Ошибка ProxyError говорит о проблемах с прокси-сервером.

Работа с сессиями и куками в aiohttp

Пример
import aiohttp
import asyncio

async def main():
    async with aiohttp.ClientSession(cookies={'sessionid': 'abc123'}) as session:
        async with session.get('https://example.com/profile') as resp:
            print(await resp.text()[:200])

asyncio.run(main())
<!DOCTYPE html>...<h1>Profile</h1>

Пояснение: Куки передаются в конструктор сессии. Они будут отправляться при каждом запросе. Также можно обновлять куки из ответа.

HTTP-запросы к HTML - comments

En
Html запросы python (python)