O que é um Servidor Proxy Python

O Servidor Proxy Python permite o roteamento de solicitações HTTP/S por meio de uma vasta rede de IPs via código Python. Ele suporta recursos como rotação de IP, persistência de sessão e direcionamento geográfico.
13 min read
Ilustração do servidor proxy Python com laptop e servidores.

Neste tutorial, você aprenderá:

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:

  1. Leia os dados das solicitações de entrada.
  2. Extraia o host e a porta do servidor de destino desses dados.
  3. Use-o para encaminhar a solicitação do cliente para o servidor de destino.
  4. 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:

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.