Ir para o conteúdo

Como fazer web scrape com HTTPX e Python

Web scraping é o processo de extração automática de dados de sites. É uma técnica comum usada para coletar grandes quantidades de dados para análise, aprendizado de máquina e muito mais. Em Python, existem muitas bibliotecas excelentes que facilitam o web scraping. Uma opção popular é HTTPX.

HTTPX é um cliente HTTP poderoso e moderno para Python. Ele foi criado pelos desenvolvedores por trás do Requests e se inspira muito no Requests, ao mesmo tempo que adiciona novas funcionalidades como suporte HTTP/2.

Neste guia abrangente, exploraremos como raspar sites de maneira eficaz em Python usando HTTPX.

Introdução ao HTTPX

Para começar, o HTTPX pode ser instalado via pip:

pip install httpx

Alternativamente, Poesia pode ser usada:

poetry add httpx

Depois de instalado, o HTTPX pode ser importado e usado:

import httpx

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

Isso fará uma solicitação GET para example.com e imprimirá o HTML da página inicial.

HTTPX tem uma API simples e suporta todos os verbos HTTP populares como GET, POST, PUT, DELETE, HEAD, OPTIONS, etc.

Alguns recursos principais incluem:

  • Suporte HTTP/1.1 e HTTP/2
  • API assíncrona estilo aiohttp
  • Pool de conexões e manutenção de atividade
  • Suporte de proxy
  • Configuração de tempo limite
  • Persistência de cookies
  • API familiar de estilo de solicitações

A seguir, vejamos alguns padrões de uso comuns.

Fazendo solicitações com HTTPX

Para fazer uma solicitação GET, o httpx.get() método pode ser usado:

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

Do mesmo modo, httpx.post(), httpx.put(), httpx.delete(), etc. podem ser usados ​​para outros verbos HTTP.

Parâmetros como cabeçalhos, cookies, tempo limite, etc. podem ser passados ​​como argumentos de palavras-chave:

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

A resposta contém propriedades como status_code, headers, text, json(), Etc.:

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

Você também pode transmitir a resposta de forma incremental iterando o objeto de resposta.

Usando um cliente HTTPX

Para a maioria das tarefas de raspagem, é recomendado usar um persistente httpx.Client instância.

O cliente lida com coisas como pool de conexões, sessões, cookies, etc. em várias solicitações.

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())

Aqui fazemos várias solicitações usando o mesmo cliente, que trata automaticamente da persistência de cookies.

Você também pode configurar opções como cabeçalhos, proxies, autenticação, etc. ao criar o cliente:

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

Agora vamos dar uma olhada nas solicitações de criação de forma assíncrona.

Solicitações assíncronas com HTTPX

Para fazer solicitações de forma assíncrona em Python, HTTPX fornece um AsyncClient:

import httpx

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

Usamos async with para inicializar o cliente, await na solicitação e feche automaticamente o cliente posteriormente.

Para extrair vários URLs simultaneamente, podemos usar 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() aguarda simultaneamente várias corrotinas e retorna resultados na ordem dos aguardáveis.

Existem também outras opções como asyncio.as_completed() para processá-los à medida que forem concluídos:

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

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

O Async IO permite a busca simultânea de várias páginas ao mesmo tempo, o que é útil para acelerar a extração.

A seguir, veremos a extração de dados de respostas HTML e JSON.

Raspar respostas HTML e JSON

Para raspagem de HTML, podemos usar um analisador como Beautiful Soup para extrair dados:

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())

Isso imprime o conteúdo do índice da página da Wikipedia.

Para respostas JSON, o HTTPX fornece um recurso integrado .json() método:

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

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

A json= O parâmetro também pode ser usado para serializar dados JSON em solicitações.

Junto com um analisador, podemos construir scrapers para extrair dados de APIs e sites.

Lidando com problemas e erros

Durante a raspagem, muitas vezes surgem problemas como erros de conexão, tempos limite, limites de taxa, etc.

HTTPX fornece exceções e ferramentas para tratá-las adequadamente.

Tempos limite

Para lidar com respostas lentas, você pode especificar um personalizado timeout parâmetro. O padrão é 5 segundos.

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

Se este valor for excedido, um httpx.TimeoutException ocorre:

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

Tempos limite mais longos podem ser necessários para determinados sites ou páginas.

Erros HTTP

Para erros HTTP como 400, 500 etc. response.raise_for_status() método pode ser usado:

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

Isso gerará uma exceção em qualquer código de status 4xx ou 5xx.

Tentando novamente solicitações

Para adicionar lógica de nova tentativa, pacotes externos como tenacity pode ser usado:

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()

Aqui tentamos novamente até 3 vezes em qualquer exceção. Lógica de nova tentativa mais avançada também pode ser definida.

Limites de solicitações paralelas

Ao fazer muitas solicitações em paralelo, você poderá encontrar limites de conexão.

A limits parâmetro pode ser usado para configurar opções como conexões máximas:

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

Ajustar esse parâmetro com base no site de destino pode ajudar a evitar limites.

Ao lidar com esses problemas comuns, raspadores mais resilientes podem ser construídos.

Práticas recomendadas de raspagem

Aqui estão algumas dicas para criar web scrapers eficazes com HTTPX:

  • Use um cliente HTTPX – Os clientes fornecem pooling de conexões, persistência de cookies e outros benefícios.

  • Raspe educadamente – Limite as taxas de solicitação para evitar sobrecarregar os servidores. Use atrasos aleatórios e limitação.

  • Lidar com erros – Use blocos try/except, verificações de status e novas tentativas para lidar com problemas.

  • Usar IO assíncrono – Raspe páginas simultaneamente para melhorar a velocidade. Mas limite a simultaneidade para evitar proibições.

  • Randomizar agentes de usuário – Gire strings aleatórias de agente de usuário para parecer mais humano.

  • Usar Proxies – Gire diferentes proxies/IPs para distribuir solicitações.

  • Cache e Persistência de Dados – Salve os dados copiados em arquivos/bancos de dados para evitar nova raspagem.

Seguindo práticas recomendadas como essas, raspadores mais robustos e de fácil manutenção podem ser construídos.

Técnicas Avançadas de Raspagem

Vejamos alguns recursos de raspagem mais avançados do HTTPX.

Raspando páginas de autenticação

Para extrair páginas autenticadas, o HTTPX oferece suporte a vários tipos de autenticação, como autenticação Básica, Digest e Bearer:

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

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

As credenciais de autenticação são persistidas automaticamente nas solicitações.

Manuseio de Cookies

A cookies parâmetro pode ser usado para enviar cookies personalizados:

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

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

E os cookies definidos pelo servidor são automaticamente persistidos no cliente.

Respostas de streaming

Para respostas grandes, você pode transmiti-las de forma incremental iterando o objeto de resposta:

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

Isso evita ter que carregar toda a resposta na memória.

Suporte Proxy

Para rotear solicitações através de um servidor proxy, o proxies parâmetro pode ser usado:

proxy = ‘http://192.168.0.1:8888‘ 

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

A rotação de diferentes proxies ajuda a distribuir solicitações de diferentes IPs.

Cabeçalhos personalizados

Você pode falsificar ou randomizar cabeçalhos de solicitação como agentes de usuário:

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

Isso imita os cabeçalhos de um navegador real.

Por meio desses recursos mais avançados, raspadores robustos podem ser construídos usando HTTPX e Python.

Exemplo de raspadores

Vejamos agora alguns exemplos de scrapers construídos com HTTPX.

Raspador de API do Reddit

Aqui está um raspador básico para a API do Reddit:

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})")

Isso busca dados nas principais postagens do subreddit Python. A API retorna JSON que podemos analisar.

Poderíamos estender esse raspador para extrair dados de vários subreddits, armazenar resultados em um banco de dados, etc.

Raspador de artigos de notícias

Aqui está um raspador de notícias simples que extrai artigos de um site:

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() 

Isto encontra tudo .article elementos, extrai os campos de título e conteúdo e imprime os artigos.

Novamente, isso poderia ser expandido para extrair campos adicionais, analisar datas, armazenar em um banco de dados, etc.

Raspador de resultados de pesquisa

E aqui está um exemplo de raspador para resultados de pesquisa do 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()

Isso pesquisa uma determinada consulta no Google, analisa os links/títulos dos resultados e os imprime.

Novamente, muitas melhorias poderiam ser feitas, como extração de contagens de resultados de pesquisa, campos adicionais, extração de páginas de resultados, detecção de captchas, etc.

Esses exemplos demonstram padrões comuns de raspagem com HTTPX. As mesmas técnicas podem ser aplicadas para construir scrapers para muitos sites e APIs.

Resumo

Para resumir, HTTPX fornece um cliente HTTP poderoso para construir web scrapers em Python. Aqui estão alguns pontos-chave:

  • HTTPX tem uma API simples no estilo de solicitações para fazer solicitações.

  • O suporte assíncrono permite fazer solicitações simultaneamente.

  • Tratamento robusto de erros com tempos limite, novas tentativas e verificações de status.

  • Raspe páginas HTML com APIs Beautiful Soup e JSON facilmente.

  • Clientes persistentes fornecem pooling de conexões, sessões e manipulação de cookies.

  • Técnicas avançadas como proxies, cabeçalhos e autenticação permitem scrapers sofisticados.

  • Siga as práticas recomendadas, como uso de otimização, atrasos aleatórios e agentes de usuário.

HTTPX facilita o início da raspagem com Python. Com tratamento robusto de erros e simultaneidade assíncrona, raspadores escalonáveis ​​podem ser desenvolvidos.

Experimente o HTTPX em seu próximo projeto de web scraping em Python!

Tags:

Junte-se à conversa

O seu endereço de e-mail não será publicado. Os campos obrigatórios são marcados com *