Voltar ao blogue
Guias
Mihnea-Octavian ManolacheLast updated on Mar 31, 202611 min read

Os 3 melhores clientes HTTP em Python para web scraping

Os 3 melhores clientes HTTP em Python para web scraping

No que diz respeito ao web scraping, a variedade de clientes HTTP em Python disponíveis torna esta linguagem uma das opções mais populares. Mas o que são exatamente estes clientes HTTP e como podem ser utilizados para criar um web scraper? Bem, no artigo de hoje iremos abordar precisamente este tema. No final deste artigo, deverá ter uma compreensão sólida sobre:

  • O que é um cliente HTTP em geral
  • Quais são os melhores clientes HTTP em Python em 2022
  • Por que razão o Python é uma excelente escolha para web scraping
  • Como criar efetivamente um web scraper utilizando clientes HTTP

O que são clientes HTTP em Python e como utilizá-los

Para compreender melhor como a Internet comunica, é necessário familiarizar-se com o Protocolo de Transferência de Hipertexto (HTTP). No entanto, o nosso foco principal hoje reside nos clientes HTTP em Python. Por isso, vou partir do princípio de que já está familiarizado com o HTTP.

De um modo geral, um cliente HTTP refere-se a uma instância ou a um programa que facilita a comunicação com um servidor. Por exemplo, um navegador web pode ser considerado um cliente HTTP. No entanto, enquanto programadores, raramente usamos um navegador real ao construir uma aplicação, exceto quando estamos a trabalhar num web scraper ou quando estamos a fazer a nossa pesquisa.

Dito isto, quando nos referimos a clientes HTTP de uma forma mais programática, normalmente referimo-nos a um método ou a uma instância de uma classe utilizada para executar pedidos HTTP. Uma vez que o Python é, sem dúvida, uma das linguagens de programação mais populares (e também a minha favorita), hoje vamos discutir os melhores clientes HTTP em Python e também como implementá-los num projeto real.

Compreender o Protocolo HTTP

Antes de prosseguirmos, embora eu recomende consultar a documentação do HTTP, deixe-me repassar rapidamente alguns dos conceitos básicos do HTTP. Em primeiro lugar, o HTTP é talvez um dos protocolos de Internet mais utilizados. Usamo-lo todos os dias para trocar informações entre clientes e servidores. 

Para que isso seja possível, o HTTP utiliza métodos de pedidos. Estes métodos indicam a ação que um cliente pretende realizar num servidor. Por exemplo, se quiser obter alguma informação de um servidor, utilizaria o GET. Se quiser enviar algo para o servidor, utilizaria o POST. Aqui está uma lista dos métodos de pedidos HTTP mais comuns:

  • GET - recupera dados do servidor
  • HEAD - recuperar apenas o cabeçalho, sem o corpo (os dados propriamente ditos)
  • POST - Enviar alguma informação para o servidor
  • PUT - Enviar informações para o servidor e substituir todas as representações atuais do recurso
  • PATCH - Enviar informações para o servidor e modificar parcialmente o recurso
  • DELETE - Apagar o recurso do servidor

Porquê Python para pedidos HTTP

Em primeiro lugar, o Python tem uma excelente sintaxe e uma comunidade ainda melhor. Por isso, é perfeito para aprender. Eu próprio, quando comecei a programar, escolhi o Python. Na verdade, os clientes HTTP em Python foram das primeiras tecnologias com que me deparei. Mas isso é outro assunto. 

O meu objetivo para o artigo de hoje é garantir que saias não só com uma compreensão teórica básica, mas também com uma visão geral da implementação prática. 

E o Python é ótimo para ambos, por várias razões. Apenas para citar algumas:

  • Sintaxe - Escrever em Python é muito semelhante a escrever em inglês. Por isso, ler um script em Python irá ajudá-lo a relacionar conceitos teóricos com a sua implementação real. 
  • Suporte - O Python tem uma comunidade muito grande. Na maioria das vezes, se estiver com dificuldades, uma simples pergunta no StackOverflow revelará a resposta para o seu problema. 
  • Disponibilidade - A biblioteca de pacotes do Python está entre as mais extensas. Por exemplo, só no que diz respeito a clientes HTTP em Python, existem mais de uma dúzia de pacotes. Mas hoje vamos concentrar-nos nos mais populares. 

3(+1) Melhores Clientes HTTP em Python

Quando se trata de categorizar pacotes para chegar aos 3 melhores clientes HTTP para Python, acredito que seja tanto uma questão de funcionalidade como de preferência pessoal. Por isso, é correto dizer que o que se segue representa as minhas 3 principais bibliotecas de clientes HTTP para Python, em vez de uma classificação geral.

1. Requests - Simplicidade Poderosa

O Requests é provavelmente um dos clientes HTTP mais preferidos na comunidade Python. Não faço exceção. Sempre que testo um novo web scraper, uso Python com o Requests. É tão fácil quanto dizer .get e tão poderoso quanto um navegador web real. 

Entre outras coisas, a biblioteca Requests oferece:

  • Verificação SSL 
  • Suporte a proxy para HTTPS
  • Persistência de cookies e sessões
  • Funcionalidade Keep-Alive
  • Autenticação personalizada

E estes são apenas alguns. Pode consultar a lista completa de funcionalidades aqui. Agora, deixe-me mostrar-lhe como trabalhar com requests:

import requests
r = requests.get("http://google.com")
print(r.test)

Como pode ver, com apenas 3 linhas de código, a biblioteca requests ajuda-nos a recolher o HTML de uma linha de um servidor. No exemplo acima, estamos a fazer um pedido GET ao servidor e a imprimir o resultado. Mas, como disse, esta biblioteca é muito mais versátil. Vamos construir um exemplo mais complexo, que utilize funcionalidades como proxies e pedidos POST:

import requests

def get_params(object):
    params = ''
    for key,value in object.items():
        if list(object).index(key) < len(object) - 1:
            params += f"{key}={value}."
        else:
            params += f"{key}={value}"
    return params

API_KEY = '<YOUR_API_KEY>'

TARGET_URL = 'https://httpbin.org/post'

DATA = {"foo":"bar"}

PARAMETERS = {
    "proxy_type":"datacenter",
    "device":"desktop"
}

PROXY = {
    "http": f"http://webscrapingapi.{ get_params(PARAMETERS) }:{ API_KEY }@proxy.webscrapingapi.com:80",
    "https": f"https://webscrapingapi.{ get_params(PARAMETERS) }:{ API_KEY }@proxy.webscrapingapi.com:8000"
}

response = requests.post(
    url=TARGET_URL,
    data=DATA,
    proxies=PROXY,
    verify=False
)

print(response.text)

Vamos ver o que estamos a fazer aqui:

  • Estamos a definir a função `get_params`, que recebe um objeto e o devolve como uma cadeia de parâmetros de URL.
  • Estamos a definir as nossas variáveis: undefinedundefinedundefinedundefinedundefined
  • Estamos a utilizar o método `post` do Requests para enviar uma solicitação HTTP POST.
  • Estamos a imprimir o corpo da resposta

2. HTTPX - Requests Reinventados

O HTTPX é relativamente novo no panorama. No entanto, num curto espaço de tempo, tornou-se um dos clientes HTTP para Python mais recomendados. Por exemplo, o Flask (um dos maiores frameworks web para Python) recomenda a utilização do HTTPX na sua documentação oficial. 

Quando disse que o HTTPX é o Requests reinventado, foi porque as duas bibliotecas são muito semelhantes em sintaxe. Na verdade, o HTTPX visa a compatibilidade total com o Requests. Existem apenas algumas pequenas diferenças de design entre os dois, que estão destacadas aqui

Eis como se apresenta uma solicitação POST básica no HTTPX:

import httpx

TARGET_URL = 'https://httpbin.org/post'

DATA = {"foo":"bar"}

r = httpx.post(
   url=TARGET_URL,
   data=DATA,
)

print(r.text)

Como pode ver, é principalmente o nome do pacote que estamos a alterar, em comparação com o exemplo do Requests. E, uma vez que são tão semelhantes, a questão permanece: porquê escolher o HTTPX em vez do Requests? Bem, para começar, o HTTPX é um dos poucos clientes HTTP para Python que oferece suporte assíncrono. Resumindo, o HTTPX é uma excelente escolha se quiser refatorar o seu código baseado no Requests. 

3. urllib3 - Ligações Thread Safe

O Python tem vários «urllibs», o que costuma confundir os programadores novatos. A principal diferença entre o urllib, o urllib2 e o urllib3 reside nas funcionalidades de cada pacote. O urllib foi o cliente HTTP original do Python, incluído na biblioteca padrão do Python 1.2. O urllib2 foi a versão atualizada, introduzida no Python 1.6 e destinada a substituir o urllib original.

No que diz respeito ao urllib3, porém, trata-se na verdade de um cliente HTTP de Python de terceiros. Apesar do nome, esta biblioteca não tem qualquer relação com os dois «antecessores». Além disso, há rumores na comunidade Python de que não há intenção de incluir o urllib3 na biblioteca padrão. Pelo menos num futuro próximo. 

Embora este pacote não esteja oficialmente ligado à biblioteca padrão do Python, muitos programadores utilizam-no porque oferece:

  • Segurança de threads
  • Verificação SSL/TLS do lado do cliente
  • Suporte a proxy para HTTP e SOCKS
  • Cobertura de testes completa

Agora que abordámos a parte teórica, vamos ver o exemplo de implementação:

import urllib3,json

TARGET_URL = 'https://httpbin.org/post'

DATA = {"foo":"bar"}

http = urllib3.PoolManager()

encoded_data = json.dumps(DATA)

r = http.request('POST', TARGET_URL, body=encoded_data)

print(r.data.decode('utf-8'))

Vamos discutir as diferenças identificadas no urllib3, em comparação com o Requests:

  • `http` - uma instância do método `PoolManager`, que lida com os detalhes de segurança de threads e pool de conexões
  • `encoded_data` - uma string JSON convertida, contendo a carga útil que estamos a enviar
  • `r` - o pedido POST efetivo que estamos a fazer com a ajuda do urllib3. Aqui, estamos a utilizar o método `request` da instância `PoolManager`.

E, por fim, temos de descodificar os dados que estamos a receber em resposta à nossa solicitação. Como pode ver, há algumas coisas que estamos a fazer de forma diferente do que com o Requests.

Menção de Honra: http.client - Cliente HTTP Tradicional do Python

O http.client também faz parte da biblioteca padrão do Python. Tradicionalmente, não é utilizado diretamente pelos programadores. Por exemplo, o urllib utiliza-o, na verdade, como uma dependência, para lidar com pedidos HTTP e HTTPS. Incluí-o no nosso ranking porque acho que, como programadores, é bom conhecer a «estrutura» dos pacotes que estamos a utilizar.

Portanto, mesmo que não venha a criar um projeto real com o http.client, eis um exemplo de implementação que, tenho a certeza, o ajudará a compreender melhor como funcionam os clientes HTTP do Python:

import http.client

TARGET_URL = 'www.httpbin.org'

http = http.client.HTTPSConnection(TARGET_URL)
http.request("GET", "/get")

r = http.getresponse()

print(r.read().decode('utf-8'))

A instância `HTTPSConnection` aceita alguns parâmetros, que pode consultar aqui. No nosso exemplo, estamos apenas a definir o `method` e o `url` (ou, mais precisamente, o endpoint). Além disso, à semelhança do urllib3, o http.client devolve uma resposta codificada. Por isso, precisamos de a descodificar antes de a imprimir.

Caso de uso: Criar um scraper com Requests

Agora que sabemos como usar clientes HTTP, vamos atribuir a nós próprios um pequeno projeto. Será útil não só para aplicar o que aprendeu, mas também para acrescentar algum valor ao seu próprio portfólio de programação. 

Uma vez que os clientes HTTP em Python são frequentemente utilizados para recolher informações de servidores, a utilização mais comum destas tecnologias é a criação de um scraper web. Assim, daqui em diante, vamos concentrar-nos em como criar um scraper web utilizando clientes HTTP em Python. Como tenho um favorito pessoal — o requests —, vou utilizá-lo para este projeto. No entanto, pode usá-lo como ponto de partida e até ajustá-lo para utilizar algumas das outras tecnologias que discutimos. Sem mais delongas, vamos começar a programar:

1. Configuração do projeto

Vamos começar por criar um novo diretório no qual iremos guardar os ficheiros para o nosso web scraper. Agora, abra uma nova janela de terminal e use `cd` para entrar nesse diretório. Aqui, queremos iniciar um novo ambiente virtual. Se estiver num sistema operativo tipo UNIX, pode usar:

~ » python3 -m venv env && source env/bin/activate              

Agora basta criar um novo ficheiro Python que irá conter a nossa lógica e abri-lo no IDE da sua preferência. Se quiser usar o terminal, basta colar o seguinte comando:

~ » touch scraper.py && code .                                         

2. Instalação de dependências

Vamos usar o pip para instalar os pacotes de que precisamos para este projeto. Por enquanto, decidimos que vamos usar o Requests, mas isso não é suficiente para um web scraper. Um web scraper também implica no tratamento dos dados. Isto significa que precisamos de analisar o HTML recolhido dos servidores. Felizmente, a biblioteca do Python oferece uma grande variedade de pacotes. Para este projeto, porém, vamos usar o BeautifulSoup. Para instalar os pacotes, basta colar o seguinte comando:

~ » python3 -m pip install requests bs4                                    

3. Escrever a lógica

Vamos dividir o nosso código em duas secções: uma para a extração de dados e outra para a manipulação de dados. A primeira parte é coberta pelo pacote Requests, enquanto a segunda parte é coberta pelo BeautifulSoup. Sem mais delongas, vamos começar a programar, começando pela parte da extração:

import requests

def scrape( url = None ):
   # if there is no URL, there is no need to use Python HTTP clients
   # We will print a message and stop execution
   if url == None:
       print('[!] Please add a target!')
       return

   response = requests.get( url ) 
   return response

Nesta secção, estamos a definir uma função com apenas um parâmetro: o URL de destino. Se o URL não for fornecido, vamos imprimir uma mensagem e interromper a execução. Caso contrário, estamos a usar o método get do Requests para devolver a resposta. Agora, sabemos que os clientes HTTP do Python abrangem mais métodos, por isso vamos adicionar um parâmetro condicional:

import requests

def scrape( method = 'get', url = None, data = None ):
   # if there is no URL, there is no need to use Python HTTP clients
   # We will print a message and stop execution
   if url == None:
       print('[!] Please add a target!')
       return

   if method.lower() == 'get':
       response = requests.get( url )

   elif method.lower() == 'post':
       if data == None:
           print('[!] Please add a payload to your POST request!')
           return
       response = requests.post( url, data )
      
   return response

Como pode ver, adicionámos mais alguns parâmetros à nossa função. O parâmetro `method` especifica qual o método que deve ser utilizado para o nosso pedido. O `data` representa a carga útil que estamos a enviar com o pedido POST. Por predefinição, o método é GET, pelo que o parâmetro `method` não é obrigatório. 

Desafio: Adicione mais métodos a esta função e enriqueça as capacidades do nosso scraper. Não só é divertido, como também é uma boa abordagem de aprendizagem. Além disso, poderá personalizar o código para o adicionar ao seu portfólio.

Até agora, abordámos a extração de dados. Vamos analisar o HTML e fazer algo com ele:

from bs4 import BeautifulSoup

def extract_elements(data = None, el = None):
   if data == None:
       print('[!] Please add some data!')
       return

   if el == None:
       print('[!] Please specify which elements you are targeting!')
       return

   soup = BeautifulSoup(data.text, 'html.parser')
   elements = soup.find_all(el)

   return elements

Mas um scraper da Web deve ser capaz de extrair dados mais específicos. Por exemplo, deve ser capaz de localizar e devolver elementos com base no seu seletor CSS. Vamos, então, adicionar a lógica que trata desta parte:

from bs4 import BeautifulSoup

def extract_elements(data = None, el = None, attr = None, attr_value = None):
   if data == None:
       print('[!] Please add some data!')
       return

   if el == None:
       print('[!] Please specify which elements you are targeting!')
       return

   soup = BeautifulSoup(data.text, 'html.parser')
   elements = soup.find_all(el, { attr : attr_value })

   return elements

O BeautifulSoup permite-nos extrair dados específicos com base nos seus atributos. Por isso, adicionámos aqui dois novos parâmetros que nos ajudarão a localizar e extrair elementos com base nos seus atributos.

Agora temos tudo o que precisamos. Resta apenas combinar as duas secções e teremos o nosso web scraper. Depois de montar o seu código, basta:

  • Crie uma nova variável que irá armazenar os dados extraídos com o Requests 
  • Imprima os elementos devolvidos pelo BeautifulSoup

Aqui estão as duas peças que faltavam no seu código:

data = scrape('GET', 'https://webscrapingapi.com')
print( extract_elements(data, 'ul') )

Tenho a certeza de que já percebeu o que tudo faz e que não há necessidade de tradução nesta altura. Tal como com o nosso scraper, desafio-o a experimentar a função `extract_elements` e a fazer com que ela faça mais do que simplesmente devolver elementos. 

Conclusão

Ao aprender um novo conceito de programação, penso que é melhor testar com as diferentes tecnologias disponíveis. No entanto, quando se trata de construir efetivamente a infraestrutura para um projeto mais abrangente, é melhor conhecer os pontos fortes e fracos de cada tecnologia antes de escolher uma. 

Espero que este artigo o tenha ajudado de alguma forma e que agora tenha uma compreensão sólida de como funcionam os clientes HTTP em Python. Também o encorajo a experimentar, pois tenho a certeza de que irá descobrir o pacote certo para si.

Sobre o autor
Mihnea-Octavian Manolache, Desenvolvedor Full Stack @ WebScrapingAPI
Mihnea-Octavian ManolacheDesenvolvedor Full Stack

Mihnea-Octavian Manolache é engenheiro Full Stack e DevOps na WebScrapingAPI, onde desenvolve funcionalidades do produto e mantém a infraestrutura que garante o bom funcionamento da plataforma.

Comece a construir

Pronto para expandir a sua recolha de dados?

Junte-se a mais de 2.000 empresas que utilizam a WebScrapingAPI para extrair dados da Web à escala empresarial, sem quaisquer custos de infraestrutura.