O que é um Servidor Proxy Python

O Servidor Proxy Python permite o roteamento de solicitações HTTP/S através de uma vasta rede de IPs via código Python. Ele suporta recursos como rotação de IP, persistência de sessão e segmentação por geolocalização.
13 min de leitura
Python Proxy Server illustration with laptop and servers.

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:

  1. Ler os dados das solicitações recebidas.
  2. Extrair o host e a porta do servidor de destino desses dados.
  3. Usá-los para encaminhar a solicitação do cliente ao servidor de destino.
  4. 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:

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.