Neste tutorial, você aprenderá:
- O que é um servidor proxy Python e como ele funciona..
- As etapas necessárias para construir um servidor proxy HTTP em Python.
- As vantagens e desvantagens dessa abordagem.
Vamos lá!
O que é um Servidor Proxy Python?
Um servidor proxy Python é uma aplicação Python que atua como intermediário entre clientes e a Internet. Ele intercepta solicitações dos clientes, as encaminha para os servidores de destino e envia a resposta de volta ao cliente. Ao fazer isso, mascara a identidade do cliente para os servidores de destino.
Leia nosso artigo para entender o que é um servidor proxy e como ele funciona.
As capacidades de programação de soquetes 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, melhoria de desempenho e aumento da segurança quando se trata de web scraping.
Como Implementar um Servidor Proxy HTTP em Python
Siga as etapas abaixo e aprenda como criar um script de servidor proxy Python.
Etapa 1: Inicializar 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 seu IDE Python e crie um arquivo vazio proxy_server.py.
Ótimo! Você tem tudo o que precisa para construir um servidor proxy HTTP em Python.
Etapa 2: Inicializar um Soquete de Entrada
Primeiro, você precisa criar um servidor de soquete da web para aceitar solicitações de entrada. Se você não estiver familiarizado com esse conceito, um soquete é uma abstração de programação de baixo nível que permite o fluxo de dados bidirecional entre um cliente e um servidor. No contexto de um servidor web, um soquete do servidor é usado para aguardar conexões de entrada dos clientes.
Use as linhas a seguir para criar um servidor da web baseado em soquete em Python:
port = 8888
# bind the proxy server to a specific address and port
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# accept up to 10 simultaneous connections
server.bind(('127.0.0.1', port))
server.listen(10)
Isso inicializa um servidor de soquete de entrada e o vincula ao endereço local http://127.0.0.1:8888. Em seguida, ele permite que o servidor aceite conexões com o método listen().
Observação: Sinta-se à vontade para alterar o número da porta que o servidor proxy da web deve ouvir. Você também pode modificar o script para ler essa informação da linha de comando para máxima flexibilidade.
O soquetevem 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 necessário, registre esta mensagem:
print(f"Proxy server listening on port {port}...")
Etapa 3: Aceitar Solicitações do Cliente
Quando um cliente se conecta ao servidor proxy, este precisa criar um novo soquete para lidar com a comunicação com esse cliente específico. É assim que você pode fazer isso em Python:
# listen for incoming requests
while True:
client_socket, addr = server.accept()
print(f"Accepted connection from {addr[0]}:{addr[1]}")
# create a thread to handle the client request
client_handler = threading.Thread(target=handle_client_request, args=(client_socket,))
client_handler.start()
Para lidar com várias solicitações de clientes simultaneamente, você deve usar a multithreading como mostrado 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 de entrada por meio da função personalizada handle_client_request(). Veja como ela é definida nas próximas etapas.
Etapa 4: Processar as Solicitações de Entrada
Depois que o soquete do cliente foi criado, você precisa usá-lo para:
- Leia os dados das solicitações de entrada.
- Extraia o host e a porta do servidor de destino desses dados.
- Use-o para encaminhar a solicitação do cliente para o servidor de destino.
- Obtenha a resposta e encaminhe-a para o cliente original.
Nesta seção, vamos nos concentrar nos dois primeiros passos. Defina a função handle_client_request() e use-a para ler os dados da solicitação de entrada:
def handle_client_request(client_socket):
print("Received request:\n")
# read the data sent by the client in the request
request = b''
client_socket.setblocking(False)
while True:
try:
# receive data from web server
data = client_socket.recv(1024)
request = request + data
# Receive data from the original destination server
print(f"{data.decode('utf-8')}")
except:
break
setblocking (False) define o soquete do cliente para o modo sem bloqueio. Em seguida, use recv () para ler os dados de entrada e adicioná-los à solicitação em formato de bytes. Como você não sabe o tamanho dos dados da solicitação de entrada, é necessário lê-los um chunk de cada vez. Neste caso, um chunk 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 final da operação.
Observe as mensagens registradas para acompanhar o que o servidor proxy Python está fazendo.
Após recuperar a solicitação de entrada, você precisa extrair o host e a porta do servidor de destino:
host, port = extract_host_port_from_request(request)
In particular, this is what the extract_host_port_from_request() function looks like:
def extract_host_port_from_request(request):
# get the value after the "Host:" string
host_string_start = request.find(b'Host: ') + len(b'Host: ')
host_string_end = request.find(b'\r\n', 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)
# if there is a specific port
port_pos = host_string.find(":")
# no port specified
if port_pos == -1 or webserver_pos < port_pos:
# default port
port = 80
host = host_string[:webserver_pos]
else:
# extract the specific port from the host string
port = int((host_string[(port_pos + 1):])[:webserver_pos - port_pos - 1])
host = host_string[:port_pos]
return host, port
To better understand what it does, consider the example below. This is what the encoded string of an incoming request usually contains:
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 da 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: Encaminhar a Solicitação do Cliente e Lidar com a Resposta
Dado o host e a porta de destino, agora é necessário encaminhar a solicitação do cliente para o servidor de destino. Em handle_client_request(), crie um novo soquete da web e use-o para enviar a solicitação original para o destino desejado:
# create a socket to connect to the original destination server
destination_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# connect to the destination server
destination_socket.connect((host, port))
# send the original request
destination_socket.sendall(request)
Then, get ready to receive the server response and propagate it to the original client:
# read the data received from the server
# once chunk at a time and send it to the client
print("Received response:\n")
while True:
# receive data from web server
data = destination_socket.recv(1024)
# Receive data from the original destination server
print(f"{data.decode('utf-8')}")
# no more data to send
if len(data) > 0:
# send back to the client
client_socket.sendall(data)
else:
break
Novamente, é necessário trabalhar um chunk de cada vez, já que você 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 soquetes que você definiu na função:
# close the 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 inteiro, iniciá-lo e verificar se ele funciona conforme o esperado!
Etapa 6: Juntar 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")
# read the data sent by the client in the request
request = b''
client_socket.setblocking(False)
while True:
try:
# receive data from web server
data = client_socket.recv(1024)
request = request + data
# Receive data from the original destination server
print(f"{data.decode('utf-8')}")
except:
break
# extract the webserver's host and port from the request
host, port = extract_host_port_from_request(request)
# create a socket to connect to the original destination server
destination_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# connect to the destination server
destination_socket.connect((host, port))
# send the original request
destination_socket.sendall(request)
# read the data received from the server
# once chunk at a time and send it to the client
print("Received response:\n")
while True:
# receive data from web server
data = destination_socket.recv(1024)
# Receive data from the original destination server
print(f"{data.decode('utf-8')}")
# no more data to send
if len(data) > 0:
# send back to the client
client_socket.sendall(data)
else:
break
# close the sockets
destination_socket.close()
client_socket.close()
def extract_host_port_from_request(request):
# get the value after the "Host:" string
host_string_start = request.find(b'Host: ') + len(b'Host: ')
host_string_end = request.find(b'\r\n', 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)
# if there is a specific port
port_pos = host_string.find(":")
# no port specified
if port_pos == -1 or webserver_pos < port_pos:
# default port
port = 80
host = host_string[:webserver_pos]
else:
# extract the specific port from the host string
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
# bind the proxy server to a specific address and port
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('127.0.0.1', port))
# accept up to 10 simultaneous connections
server.listen(10)
print(f"Proxy server listening on port {port}...")
# listen for incoming requests
while True:
client_socket, addr = server.accept()
print(f"Accepted connection from {addr[0]}:{addr[1]}")
# create a thread to handle the client request
client_handler = threading.Thread(target=handle_client_request, args=(client_socket,))
client_handler.start()
if __name__ == "__main__":
start_proxy_server()
Launch it with this command:
python proxy_server.py
Você deverá ver a seguinte mensagem no terminal:
Proxy server listening on port 8888...
Para garantir que o servidor funcione, execute uma solicitação de proxy com o cURL. Leia nosso guia para aprender 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 fará uma solicitação GET para o 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, “origem” corresponderá ao seu IP.
Observação: O servidor proxy Python construído 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 Python de servidor proxy. Deverá 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 informa que o servidor proxy recebeu a solicitação no formato especificado pelo protocolo HTTP. Em seguida, encaminhou-a para o 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 “origem” são os mesmos!
Parabéns! Você acabou de aprender como construir um servidor proxy HTTP em Python!
Prós e Contras do Uso de 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!
- Customização: O servidor proxy pode ser estendido para incluir recursos úteis, como registro e armazenamento em cache 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 VPS.
- Difícil de manter: Você é responsável por manter a arquitetura do proxy, especialmente sua escalabilidade e disponibilidade. Esta é uma tarefa que apenas administradores de sistema experientes podem enfrentar.
- Não confiável: O principal problema com esta solução é que o IP de saída estático do servidor proxy nunca muda. Como resultado, tecnologias anti-bot serão capazes de bloquear o IP e impedir o servidor de acessar as solicitações desejadas. Em outras palavras, o proxy eventualmente deixará de funcionar.
Essas limitações e desvantagens são muito ruins para usar um servidor proxy Python personalizado em um cenário de produção. A solução? Um provedor de proxy confiável como Bright Data! Crie uma conta, verifique sua identidade, obtenha um proxy gratuito e use-o em sua linguagem de programação favorita. Por exemplo, integre um proxy em seu script Python com requests.
Nossa enorme rede de proxy 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. Detalhadamente, você aprendeu como construir um do zero usando soquetes da web. Agora você se tornou um mestre em proxies em Python. O principal problema com esta abordagem é que o IP de saída estático do seu servidor proxy eventualmente será bloqueado. Evite isso com os proxies rotativos daBright Data!
A Bright Data controla os melhores servidores proxy do mundo, atendendo a empresas Fortune 500 e mais de 20.000 clientes. Sua oferta inclui uma ampla variedade de tipos de proxy:
- Proxies de datacenter — Mais de 770.000 IPs para data center.
- Proxies residenciais — Mais de 72 milhões de IPs residenciais em mais de 195 países.
- proxies de ISP — Mais de 700.000 IPs de ISP.
- Proxies móveis — Mais de 7 milhões de IPs móveis.
Essa rede proxy confiável, rápida e global também é a base de vários serviços de web scraping para recuperar dados de qualquer site sem esforço.