Neste tutorial, você aprenderá:
- O que é um servidor proxy Python e como funciona
- As etapas necessárias para construir um servidor proxy HTTP em Python.
- Os prós e contras dessa abordagem.
Vamos começar!
O Que É um Servidor Proxy Python?
Um Proxy Python é uma aplicação Python que atua como intermediária entre clientes e a Internet. Ele intercepta solicitações de clientes, as encaminha aos servidores de destino e envia a resposta de volta ao cliente. Ao fazer isso, ele mascara a identidade do cliente perante os servidores de destino.
Leia nosso artigo para entender o que é um servidor proxy e como funciona.
As capacidades de programação de sockets do Python facilitam a implementação de um servidor proxy básico, permitindo que os usuários inspecionem, modifiquem ou redirecionem o tráfego de rede. Servidores proxy são ótimos para caching, melhorar o desempenho e aumentar a segurança quando se trata de scraping de dados.
Como Implementar um Servidor Proxy HTTP em Python
Siga as etapas abaixo e aprenda a construir um script de servidor proxy Python.
Etapa 1: Inicialize Seu Projeto Python
Antes de começar, certifique-se de ter o Python 3+ instalado em sua máquina. Caso contrário, baixe o instalador, execute-o e siga o assistente de instalação.
Em seguida, use os comandos abaixo para criar uma pasta python-http-proxy-server e inicializar um projeto Python com um ambiente virtual dentro dela:
mkdir python-http-proxy-server
cd python-http-proxy-server
python -m venv env
Abra a pasta python-http-proxy-server em sua IDE Python e crie um arquivo proxy_server.py vazio.
Ótimo! Você tem tudo o que precisa para construir um servidor proxy HTTP em Python.
Etapa 2: Inicialize um Socket de Entrada
Primeiro, você precisa criar um servidor web socket para aceitar solicitações recebidas. Se você não está familiarizado com esse conceito, um socket é uma abstração de programação de baixo nível que permite o fluxo bidirecional de dados entre um cliente e um servidor. No contexto de um servidor web, um socket de servidor é usado para escutar conexões recebidas de clientes.
Use as linhas a seguir para criar um servidor web baseado em socket em Python:
port = 8888
# vincula o servidor proxy a um endereço e porta específicos
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# aceita até 10 conexões simultâneas
server.bind(('127.0.0.1', port))
server.listen(10)
Isso inicializa um servidor de socket de entrada e o vincula ao endereço local http://127.0.0.1:8888. Em seguida, habilita o servidor a aceitar conexões com o método listen().
Nota: Sinta-se à vontade para alterar o número da porta que o proxy web deve escutar. Você também pode modificar o script para ler essa informação da linha de comando para máxima flexibilidade.
socket vem da Biblioteca Padrão do Python. Portanto, você terá a seguinte importação no topo do seu script:
import socket
Para monitorar se o servidor proxy Python foi iniciado conforme esperado, registre esta mensagem:
print(f"Proxy server listening on port {port}...")
Etapa 3: Aceite Solicitações de Clientes
Quando um cliente se conecta ao servidor proxy, este precisa criar um novo socket para lidar com a comunicação com esse cliente específico. Veja como fazer isso em Python:
# escuta solicitações recebidas
while True:
client_socket, addr = server.accept()
print(f"Accepted connection from {addr[0]}:{addr[1]}")
# cria uma thread para lidar com a solicitação do cliente
client_handler = threading.Thread(target=handle_client_request, args=(client_socket,))
client_handler.start()
Para lidar com múltiplas solicitações de clientes simultaneamente, você deve usar multithreading como acima. Não se esqueça de importar threading da Biblioteca Padrão do Python:
import threading
Como você pode ver, o servidor proxy lida com solicitações recebidas através da função personalizada handle_client_request(). Veja como ela é definida nas próximas etapas.
Etapa 4: Processe as Solicitações Recebidas
Depois que o socket do cliente for criado, você precisa usá-lo para:
- Ler os dados das solicitações recebidas.
- Extrair o host e a porta do servidor de destino desses dados.
- Usá-los para encaminhar a solicitação do cliente ao servidor de destino.
- Obter a resposta e encaminhá-la ao cliente original.
Nesta seção, vamos focar nas duas primeiras etapas. Defina a função handle_client_request() e use-a para ler os dados da solicitação recebida:
def handle_client_request(client_socket):
print("Received request:n")
# lê os dados enviados pelo cliente na solicitação
request = b''
client_socket.setblocking(False)
while True:
try:
# recebe dados do servidor web
data = client_socket.recv(1024)
request = request + data
# Recebe dados do servidor de destino original
print(f"{data.decode('utf-8')}")
except:
break
setblocking(False) define o socket do cliente para o modo não bloqueante. Em seguida, use recv() para ler os dados recebidos e anexá-los à solicitação em formato de bytes. Como você não sabe o tamanho dos dados da solicitação recebida, você precisa lê-los um trecho de cada vez. Neste caso, um trecho de 1024 bytes foi especificado. No modo não bloqueante, se recv() não encontrar nenhum dado, ele levantará uma exceção de erro. Assim, a instrução except marca o fim da operação.
Observe as mensagens registradas para acompanhar o que o servidor proxy Python está fazendo.
Após recuperar a solicitação recebida, você precisa extrair o host e a porta do servidor de destino dela:
host, port = extract_host_port_from_request(request)
Em particular, é assim que a função extract_host_port_from_request() se parece:
def extract_host_port_from_request(request):
# obtém o valor após a string "Host:"
host_string_start = request.find(b'Host: ') + len(b'Host: ')
host_string_end = request.find(b'rn', host_string_start)
host_string = request[host_string_start:host_string_end].decode('utf-8')
webserver_pos = host_string.find("/")
if webserver_pos == -1:
webserver_pos = len(host_string)
# se houver uma porta específica
port_pos = host_string.find(":")
# nenhuma porta especificada
if port_pos == -1 or webserver_pos < port_pos:
# porta padrão
port = 80
host = host_string[:webserver_pos]
else:
# extrai a porta específica da string do host
port = int((host_string[(port_pos + 1):])[:webserver_pos - port_pos - 1])
host = host_string[:port_pos]
return host, port
Para entender melhor o que isso faz, considere o exemplo abaixo. Isto é o que a string codificada de uma solicitação recebida geralmente contém:
GET http://example.com/your-page HTTP/1.1
Host: example.com
User-Agent: curl/8.4.0
Accept: */*
Proxy-Connection: Keep-Alive
extract_host_port_from_request() extrai o host e a porta do servidor web do campo “Host:”. Neste caso, o host é example.com e a porta é 80 (já que uma porta específica não foi especificada).
Etapa 5: Encaminhe a Solicitação do Cliente e Trate a Resposta
Dado o host e a porta de destino, agora você precisa encaminhar a solicitação do cliente ao servidor de destino. Em handle_client_request(), crie um novo web socket e use-o para enviar a solicitação original ao destino desejado:
# cria um socket para conectar ao servidor de destino original
destination_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# conecta ao servidor de destino
destination_socket.connect((host, port))
# envia a solicitação original
destination_socket.sendall(request)
Em seguida, prepare-se para receber a resposta do servidor e propagá-la ao cliente original:
# lê os dados recebidos do servidor
# um trecho de cada vez e envia ao cliente
print("Received response:n")
while True:
# recebe dados do servidor web
data = destination_socket.recv(1024)
# Recebe dados do servidor de destino original
print(f"{data.decode('utf-8')}")
# não há mais dados para enviar
if len(data) > 0:
# envia de volta ao cliente
client_socket.sendall(data)
else:
break
Novamente, você precisa trabalhar um trecho de cada vez, já que não sabe o tamanho da resposta. Quando os dados estão vazios, não há mais dados para receber e você pode encerrar a operação.
Não se esqueça de fechar os dois sockets que você definiu na função:
# fecha os sockets
destination_socket.close()
client_socket.close()
Incrível! Você acabou de criar um servidor proxy HTTP em Python. Hora de ver o código completo, executá-lo e verificar se funciona como esperado!
Etapa 6: Junte Tudo
Este é o código final do seu script de servidor proxy Python:
import socket
import threading
def handle_client_request(client_socket):
print("Received request:n")
# lê os dados enviados pelo cliente na solicitação
request = b''
client_socket.setblocking(False)
while True:
try:
# recebe dados do servidor web
data = client_socket.recv(1024)
request = request + data
# Recebe dados do servidor de destino original
print(f"{data.decode('utf-8')}")
except:
break
# extrai o host e a porta do servidor web da solicitação
host, port = extract_host_port_from_request(request)
# cria um socket para conectar ao servidor de destino original
destination_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# conecta ao servidor de destino
destination_socket.connect((host, port))
# envia a solicitação original
destination_socket.sendall(request)
# lê os dados recebidos do servidor
# um trecho de cada vez e envia ao cliente
print("Received response:n")
while True:
# recebe dados do servidor web
data = destination_socket.recv(1024)
# Recebe dados do servidor de destino original
print(f"{data.decode('utf-8')}")
# não há mais dados para enviar
if len(data) > 0:
# envia de volta ao cliente
client_socket.sendall(data)
else:
break
# fecha os sockets
destination_socket.close()
client_socket.close()
def extract_host_port_from_request(request):
# obtém o valor após a string "Host:"
host_string_start = request.find(b'Host: ') + len(b'Host: ')
host_string_end = request.find(b'rn', host_string_start)
host_string = request[host_string_start:host_string_end].decode('utf-8')
webserver_pos = host_string.find("/")
if webserver_pos == -1:
webserver_pos = len(host_string)
# se houver uma porta específica
port_pos = host_string.find(":")
# nenhuma porta especificada
if port_pos == -1 or webserver_pos < port_pos:
# porta padrão
port = 80
host = host_string[:webserver_pos]
else:
# extrai a porta específica da string do host
port = int((host_string[(port_pos + 1):])[:webserver_pos - port_pos - 1])
host = host_string[:port_pos]
return host, port
def start_proxy_server():
port = 8888
# vincula o servidor proxy a um endereço e porta específicos
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('127.0.0.1', port))
# aceita até 10 conexões simultâneas
server.listen(10)
print(f"Proxy server listening on port {port}...")
# escuta solicitações recebidas
while True:
client_socket, addr = server.accept()
print(f"Accepted connection from {addr[0]}:{addr[1]}")
# cria uma thread para lidar com a solicitação do cliente
client_handler = threading.Thread(target=handle_client_request, args=(client_socket,))
client_handler.start()
if __name__ == "__main__":
start_proxy_server()
Execute-o com este comando:
python proxy_server.py
Você deve ver a seguinte mensagem no terminal:
Proxy server listening on port 8888...
Para garantir que o servidor funciona, execute uma solicitação de proxy com cURL. Leia nosso guia para saber mais sobre como usar cURL com um proxy.
Abra um novo terminal e execute:
curl --proxy "http://127.0.0.1:8888" "http://httpbin.org/ip"
Isso faria uma solicitação GET ao destino http://httpbin.org/ip através do servidor proxy http://127.0.0.1:8888.
Você deve obter algo como:
{
"origin": "45.12.80.183"
}
Esse é o IP do servidor proxy. Por quê? Porque o endpoint /ip do projeto HTTPBin retorna o IP de onde vem a solicitação. Se você estiver executando o servidor localmente, “origin” corresponderá ao seu IP.
Nota: O servidor proxy Python criado aqui funciona apenas com destinos HTTP. Estendê-lo para lidar com conexões HTTPS é bastante complicado.
Agora, explore o log escrito pela sua aplicação de servidor proxy Python. Ele deve conter:
Received request:
GET http://httpbin.org/ip HTTP/1.1
Host: httpbin.org
User-Agent: curl/8.4.0
Accept: */*
Proxy-Connection: Keep-Alive
Received response:
HTTP/1.1 200 OK
Date: Thu, 14 Dec 2023 14:02:08 GMT
Content-Type: application/json
Content-Length: 31
Connection: keep-alive
Server: gunicorn/19.9.0
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
{
"origin": "45.12.80.183"
}
Isso lhe diz que o servidor proxy recebeu a solicitação no formato especificado pelo protocolo HTTP. Em seguida, ele a encaminhou ao servidor de destino, registrou os dados da resposta e enviou a resposta de volta ao cliente. Por que temos certeza disso? Porque os IPs em “origin” são os mesmos!
Parabéns! Você acabou de aprender a construir um servidor proxy HTTP em Python!
Prós e Contras de Usar um Servidor Proxy Python Personalizado
Agora que você sabe como implementar um servidor proxy em Python, está pronto para ver os benefícios e limitações dessa abordagem.
Prós:
- Controle total: Com um script Python personalizado como este, você tem controle total sobre o que seu servidor proxy faz. Nenhuma atividade suspeita ou vazamento de dados ali!
- Personalização: O servidor proxy pode ser estendido para incluir recursos úteis, como registro e caching de solicitações, para melhorar o desempenho.
Contras:
- Custos de infraestrutura: Configurar uma arquitetura de servidor proxy não é fácil e custa muito dinheiro em termos de hardware ou serviços de VPS.
- Difícil de manter: Você é responsável por manter a arquitetura do proxy, especialmente sua escalabilidade e disponibilidade. Essa é uma tarefa que apenas administradores de sistemas experientes podem enfrentar.
- Não confiável: O principal problema dessa solução é que o IP de saída do servidor proxy nunca muda. Como resultado, tecnologias anti-bot poderão bloquear o IP e impedir que o servidor acesse as solicitações desejadas. Em outras palavras, o proxy eventualmente deixará de funcionar.
Essas limitações e desvantagens são graves demais para usar um servidor proxy Python personalizado em um cenário de produção. A solução? Um provedor de proxy confiável como a Bright Data! Crie uma conta, verifique sua identidade, obtenha um proxy e use-o em sua linguagem de programação favorita. Por exemplo, integre um proxy ao seu script Python com requests.
Nossa enorme rede de proxies envolve milhões de servidores proxy rápidos, confiáveis e seguros em todo o mundo. Descubra por que somos o melhor provedor de servidores proxy.
Conclusão
Neste guia, você aprendeu o que é um servidor proxy e como ele funciona em Python. Em detalhes, você aprendeu a construir um do zero usando web sockets. Agora você se tornou um mestre de proxies em Python. O principal problema dessa abordagem é que o IP de saída estático do seu servidor proxy acabará fazendo com que você seja bloqueado. Evite isso com os proxies rotativos da Bright Data!
A Bright Data controla os melhores servidores proxy do mundo, atendendo empresas da Fortune 500 e 50,000+ clientes. Sua oferta inclui uma ampla variedade de tipos de proxy:
- Proxies de datacenter – 1,300,000+ IPs de datacenter.
- Proxies residenciais – 400M+ IPs residenciais em mais de 195 países.
- Proxies ISP – 1,300,000+ IPs ISP.
Essa rede de proxies confiável, rápida e global também é a base de vários serviços de scraping de dados para recuperar dados de qualquer site sem esforço.