перейти к содержанию

Как парсить веб-страницы с помощью HTTPX и Python

Веб-скрапинг — это процесс автоматического извлечения данных с веб-сайтов. Это распространенный метод, используемый для сбора больших объемов данных для анализа, машинного обучения и многого другого. В Python есть множество отличных библиотек, которые упрощают парсинг веб-страниц. Одним из популярных вариантов является HTTPX.

HTTPX — мощный и современный HTTP-клиент для Python. Он был создан разработчиками Requests и во многом вдохновлен Requests, а также добавил новые функции, такие как поддержка HTTP/2.

В этом подробном руководстве мы рассмотрим, как эффективно парсить веб-сайты на Python с использованием HTTPX.

Начало работы с HTTPX

Для начала HTTPX можно установить через pip:

pip install httpx

Альтернативно, Поэзию можно использовать:

poetry add httpx

После установки HTTPX можно импортировать и использовать:

import httpx

response = httpx.get(‘https://example.com‘)
print(response.text)

Это приведет к отправке GET-запроса на сайт example.com и распечатке HTML-кода домашней страницы.

HTTPX имеет простой API и поддерживает все популярные команды HTTP, такие как GET, POST, PUT, DELETE, HEAD, OPTIONS и т. д.

Некоторые ключевые особенности включают:

  • Поддержка HTTP/1.1 и HTTP/2
  • Асинхронный API в стиле aiohttp
  • Пул соединений и поддержка активности
  • Поддержка прокси
  • Конфигурация тайм-аута
  • Сохранение файлов cookie
  • Знакомый API в стиле запросов

Далее давайте рассмотрим некоторые распространенные шаблоны использования.

Выполнение запросов с помощью HTTPX

Чтобы сделать запрос GET, httpx.get() можно использовать метод:

response = httpx.get(‘https://example.com‘)

Кроме того, httpx.post(), httpx.put(), httpx.delete()и т. д. можно использовать для других команд HTTP.

Такие параметры, как заголовки, файлы cookie, тайм-аут и т. д., можно передавать в качестве аргументов ключевого слова:

response = httpx.get(
  ‘https://httpbin.org/headers‘,
  headers = {
    ‘User-Agent‘: ‘MyBot 1.0‘
  },
  timeout = 10.0
)

Ответ содержит такие свойства, как status_code, headers, text, json()И т.д.:

print(response.status_code)
print(response.headers)
print(response.text)
json = response.json()

Вы также можете поэтапно передавать ответ, перебирая объект ответа.

Использование HTTPX-клиента

Для большинства задач очистки рекомендуется использовать постоянный httpx.Client пример.

Клиент обрабатывает такие вещи, как пул соединений, сеансы, файлы cookie и т. д., в рамках нескольких запросов.

import httpx

client = httpx.Client()

response = client.get(‘https://example.com‘)
print(response.text)

response = client.get(‘https://httpbin.org/cookies/set?name=foo‘)
print(response.text) 

response = client.get(‘https://httpbin.org/cookies‘)
print(response.json())

Здесь мы делаем несколько запросов, используя один и тот же клиент, который автоматически обрабатывает сохранение файлов cookie.

Вы также можете настроить такие параметры, как заголовки, прокси, аутентификация и т. д. при создании клиента:

client = httpx.Client(
  headers = {
    ‘User-Agent‘: ‘MyBot 1.0‘,
    ‘Authorization‘: ‘Bearer xxx‘   
  },
  proxies = ‘http://192.168.1.1:8181/‘,
  auth = (‘username‘, ‘password‘)
)

Теперь давайте посмотрим на асинхронные запросы.

Асинхронные запросы с HTTPX

Для асинхронного выполнения запросов в Python HTTPX предоставляет AsyncClient:

import httpx

async with httpx.AsyncClient() as client:
  response = await client.get(‘https://example.com‘) 

МЫ ИСПОЛЬЗУЕМ async with для инициализации клиента, await по запросу и автоматически закрыть клиент после этого.

Чтобы одновременно очистить несколько URL-адресов, мы можем использовать asyncio.gather():

import httpx
import asyncio

async def get_url(url):
  async with httpx.AsyncClient() as client:
    response = await client.get(url)
    return response.text

urls = [‘https://example.com‘, ‘https://httpbin.org‘, ‘https://python.org‘]

async def main():
  responses = await asyncio.gather(*[get_url(url) for url in urls])
  print(responses)

asyncio.run(main())

asyncio.gather() одновременно ожидает несколько сопрограмм и возвращает результаты в порядке ожидаемых.

Есть и другие варианты, например asyncio.as_completed() для их обработки по мере их завершения:

tasks = [get_url(url) for url in urls]

async def main():
  for result in asyncio.as_completed(tasks):
    print(await result)

Async IO позволяет одновременно получать несколько страниц, что полезно для ускорения очистки.

Далее давайте рассмотрим сбор данных из ответов HTML и JSON.

Парсинг ответов HTML и JSON

Для парсинга HTML мы можем использовать парсер, такой как Beautiful Soup, для извлечения данных:

from bs4 import BeautifulSoup

response = httpx.get(‘https://en.wikipedia.org/wiki/Python_(programming_language)‘)

soup = BeautifulSoup(response.text, ‘html.parser‘)

for link in soup.select(‘.toctext‘):
  print(link.text.strip())

При этом будет распечатано содержимое оглавления страницы Википедии.

Для ответов JSON HTTPX предоставляет встроенный .json() Метод:

response = httpx.get(‘https://api.github.com/repos/encode/httpx‘)

json = response.json()
print(json[‘description‘])

Ассоциация json= Параметр также можно использовать для сериализации данных JSON в запросах.

Вместе с парсером мы можем создавать парсеры для извлечения данных из API и веб-сайтов.

Обработка проблем и ошибок

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

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

Таймауты

Чтобы справиться с медленными ответами, вы можете указать собственный timeout параметр. По умолчанию — 5 секунд.

response = httpx.get(‘https://example.com‘, timeout=10.0) 

Если это значение превышено, httpx.TimeoutException происходит:

try:
  response = httpx.get(‘https://example.com‘, timeout=0.001)
except httpx.TimeoutException:
  print(‘Request timed out‘)

Для некоторых сайтов или страниц могут потребоваться более длительные тайм-ауты.

Ошибки HTTP

Для ошибок HTTP, таких как 400, 500 и т. д., response.raise_for_status() можно использовать метод:

try:
  response = httpx.get(‘https://httpbin.org/status/500‘)
  response.raise_for_status()
except httpx.HTTPStatusError:
  print(‘HTTP Error occurred‘)  

Это вызовет исключение для любого кода состояния 4xx или 5xx.

Повторные запросы

Чтобы добавить логику повтора, внешние пакеты, такие как tenacity может быть использован:

from tenacity import retry, stop_after_attempt

@retry(stop=stop_after_attempt(3))
def make_request():
  response = httpx.get(‘https://example.com‘)
  response.raise_for_status()
  return response.json()

data = make_request()

Здесь мы повторяем попытку до 3 раз для любого исключения. Также можно определить более продвинутую логику повторов.

Ограничения параллельных запросов

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

Ассоциация limits Параметр можно использовать для настройки таких параметров, как максимальное количество подключений:

client = httpx.AsyncClient(
  limits = httpx.Limits(max_connections=20)
)

Настройка этого параметра в зависимости от целевого сайта может помочь избежать ограничений.

Решив эти распространенные проблемы, можно создать более устойчивые скреперы.

Лучшие практики парсинга

Вот несколько советов по созданию эффективных парсеров с помощью HTTPX:

  • Используйте HTTPX-клиент – Клиенты обеспечивают объединение пулов соединений, сохранение файлов cookie и другие преимущества.

  • Соскребать вежливо – Ограничьте частоту запросов, чтобы избежать перегрузки серверов. Используйте случайные задержки и регулирование.

  • Обработка ошибок – Используйте блоки try/Exception, проверки состояния и повторные попытки для решения проблем.

  • Используйте асинхронный ввод-вывод – Парсинг страниц одновременно для повышения скорости. Но ограничьте параллелизм, чтобы избежать банов.

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

  • Используйте прокси – Меняйте разные прокси/IP-адреса для распределения запросов.

  • Кэширование и сохранение данных – Сохраняйте очищенные данные в файлы/базы данных, чтобы избежать повторного очистки.

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

Продвинутые методы парсинга

Давайте посмотрим на некоторые более продвинутые возможности очистки HTTPX.

Парсинг страниц аутентификации

Для очистки аутентифицированных страниц HTTPX поддерживает несколько типов аутентификации, таких как базовая, дайджест и аутентификация на предъявителя:

client = httpx.Client(
  auth = (‘username‘, ‘password‘)
)

response = client.get(‘https://api.example.com/users/me‘)

Учетные данные аутентификации автоматически сохраняются во всех запросах.

Обработка файлов cookie

Ассоциация cookies параметр можно использовать для отправки пользовательских файлов cookie:

client = httpx.Client(
  cookies = {
    ‘sessionId‘: ‘xxxx‘
  }
)

response = client.get(‘https://example.com/dashboard‘) 

А файлы cookie, установленные сервером, автоматически сохраняются в клиенте.

Потоковые ответы

Для больших ответов вы можете передавать их постепенно, повторяя объект ответа:

response = client.get(‘https://example.com/bigfile‘)
for chunk in response:
  process(chunk) 

Это позволяет избежать загрузки всего ответа в память.

Поддержка прокси

Для маршрутизации запросов через прокси-сервер proxies параметр можно использовать:

proxy = ‘http://192.168.0.1:8888‘ 

client = httpx.Client(proxies = {‘http‘: proxy, ‘https‘: proxy})

Ротация разных прокси помогает распределять запросы с разных IP-адресов.

Пользовательские заголовки

Вы можете подделать или рандомизировать заголовки запросов, как пользовательские агенты:

client = httpx.Client(headers = {
  ‘User-Agent‘: ‘MyBot 1.0‘ 
})

Это имитирует заголовки реального браузера.

Благодаря этим более продвинутым функциям можно создавать надежные парсеры с использованием HTTPX и Python.

Примеры скребков

Давайте теперь посмотрим на несколько примеров парсеров, созданных с использованием HTTPX.

Скребок API Reddit

Вот базовый парсер для Reddit API:

import httpx

client = httpx.Client()

subreddit = ‘python‘ 
listing = ‘hot‘
limit = 10

response = client.get(f‘https://www.reddit.com/r/{subreddit}/{listing}.json?limit={limit}‘)

data = response.json()[‘data‘]

for post in data[‘children‘]:
   title = post[‘data‘][‘title‘]
   score = post[‘data‘][‘score‘]
   print(f"{title} (Score: {score})")

При этом извлекаются данные о самых популярных сообщениях из субреддита Python. API возвращает JSON, который мы можем проанализировать.

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

Новости Статья Скребок

Вот простой парсер новостей, который извлекает статьи с сайта:

from bs4 import BeautifulSoup
import httpx

client = httpx.Client()

response = client.get("https://example.com/news")
soup = BeautifulSoup(response.text, ‘html.parser‘)

for article in soup.select(‘.article‘):

  title = article.select_one(‘.article-title‘).text
  content = article.select_one(‘.article-content‘).text

  print(title) 
  print(content)
  print() 

Это находит все .article элементы, извлекает поля заголовка и содержимого и печатает статьи.

Опять же, это можно расширить, чтобы очистить дополнительные поля, проанализировать даты, сохранить в базе данных и т. д.

Парсер результатов поиска

А вот пример парсера результатов поиска Google:

import httpx

query = "httpx python"
url = f"https://www.google.com/search?q={query}"

client = httpx.Client()
response = client.get(url)

from bs4 import BeautifulSoup
soup = BeautifulSoup(response.text, ‘html.parser‘)

for result in soup.select(‘.tF2Cxc‘):

  title = result.select_one(‘.DKV0Md‘).text
  link = result.select_one(‘.yuRUbf a‘)[‘href‘]

  print(title)
  print(link) 
  print()

Это выполняет поиск в Google по заданному запросу, анализирует ссылки/заголовки результатов и распечатывает их.

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

Эти примеры демонстрируют общие шаблоны парсинга HTTPX. Те же методы можно применить для создания парсеров для многих веб-сайтов и API.

Обзор

Подводя итог, HTTPX предоставляет мощный HTTP-клиент для создания веб-скребков Python. Вот некоторые ключевые моменты:

  • HTTPX имеет простой API-интерфейс в стиле запросов для выполнения запросов.

  • Поддержка асинхронности позволяет выполнять запросы одновременно.

  • Надежная обработка ошибок с таймаутами, повторными попытками и проверками статуса.

  • Легко очищайте HTML-страницы с помощью API Beautiful Soup и JSON.

  • Постоянные клиенты обеспечивают пул соединений, сеансы и обработку файлов cookie.

  • Передовые методы, такие как прокси, заголовки и аутентификация, позволяют использовать сложные парсеры.

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

HTTPX позволяет легко начать парсинг с помощью Python. Благодаря надежной обработке ошибок и асинхронному параллелизму можно разработать масштабируемые парсеры.

Попробуйте HTTPX в своем следующем проекте парсинга веб-страниц на Python!

Теги:

Присоединяйтесь к беседе

Ваш электронный адрес не будет опубликован. Обязательные поля помечены * *