Qu’est-ce qu’un serveur proxy Python

Python Proxy Server permet le routage des requêtes HTTP/S via un vaste réseau d’IPs grâce au code Python. Il prend en charge la rotation d’IP, la persistance de session et le ciblage géographique.
14 min de lecture
Python Proxy Server illustration with laptop and servers.

Dans ce tutoriel, vous apprendrez :

  • Ce qu’est un serveur proxy Python et comment il fonctionne
  • Les étapes nécessaires pour créer un serveur proxy HTTP en Python.
  • Les avantages et inconvénients de cette approche.

Plongeons dans le sujet !

Qu’est-ce qu’un serveur proxy Python ?

Un proxy Python est une application Python qui agit comme intermédiaire entre les clients et Internet. Il intercepte les requêtes des clients, les transmet aux serveurs cibles, et renvoie la réponse au client. Ce faisant, il masque l’identité du client vis-à-vis des serveurs de destination. 

Lisez notre article pour approfondir ce qu’est un serveur proxy et comment il fonctionne. 

Les capacités de programmation réseau de Python facilitent l’implémentation d’un serveur proxy basique, permettant aux utilisateurs d’inspecter, modifier ou rediriger le trafic réseau. Les serveurs proxy sont excellents pour la mise en cache, l’amélioration des performances et le renforcement de la sécurité en matière de scraping web.

Comment implémenter un serveur proxy HTTP en Python

Suivez les étapes ci-dessous pour apprendre à créer un script de serveur proxy Python.

Étape 1 : Initialiser votre projet Python

Avant de commencer, assurez-vous d’avoir Python 3+ installé sur votre machine. Sinon, téléchargez l’installateur, exécutez-le, et suivez l’assistant d’installation.

Ensuite, utilisez les commandes ci-dessous pour créer un dossier python-http-proxy-server et initialiser un projet Python avec un environnement virtuel à l’intérieur : 

mkdir python-http-proxy-server

cd python-http-proxy-server

python -m venv env

Ouvrez le dossier python-http-proxy-server dans votre IDE Python et créez un fichier proxy_server.py vide.

Parfait ! Vous avez tout ce qu’il faut pour créer un serveur proxy HTTP en Python.

Étape 2 : Initialiser un socket entrant

D’abord, vous devez créer un serveur web socket pour accepter les requêtes entrantes. Si vous n’êtes pas familier avec ce concept, un socket est une abstraction de programmation de bas niveau qui permet un flux de données bidirectionnel entre un client et un serveur. Dans le contexte d’un serveur web, un socket serveur est utilisé pour écouter les connexions entrantes des clients. 

Utilisez les lignes suivantes pour créer un serveur web basé sur des sockets en Python :

port = 8888
# lier le serveur proxy à une adresse et un port spécifiques
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# accepter jusqu'à 10 connexions simultanées
server.bind(('127.0.0.1', port))
server.listen(10)

Ceci initialise un serveur socket entrant et le lie à l’adresse locale http://127.0.0.1:8888. Ensuite, il permet au serveur d’accepter des connexions avec la méthode listen().

Note : N’hésitez pas à changer le numéro du port sur lequel le proxy web doit écouter. Vous pouvez aussi modifier le script pour lire cette information depuis la ligne de commande pour une flexibilité maximale. 

socket provient de la bibliothèque standard Python. Vous aurez donc l’import suivant en haut de votre script :

import socket

Pour surveiller que le serveur proxy Python a démarré comme prévu, enregistrez ce message :

 print(f"Proxy server listening on port {port}...")

Étape 3 : Accepter les requêtes des clients

Lorsqu’un client se connecte au serveur proxy, celui-ci doit créer un nouveau socket pour gérer la communication avec ce client spécifique. Voici comment procéder en Python :

# écouter les requêtes entrantes

while True:

    client_socket, addr = server.accept()

    print(f"Accepted connection from {addr[0]}:{addr[1]}")

    # créer un thread pour gérer la requête client

    client_handler = threading.Thread(target=handle_client_request, args=(client_socket,))

    client_handler.start()

Pour gérer plusieurs requêtes clients simultanément, vous devez utiliser le multithreading comme ci-dessus. N’oubliez pas d’importer threading depuis la bibliothèque standard Python :

import threading

Comme vous pouvez le voir, le serveur proxy gère les requêtes entrantes via la fonction personnalisée handle_client_request(). Voyez comment elle est définie dans les étapes suivantes.

Étape 4 : Traiter les requêtes entrantes

Une fois le socket client créé, vous devez l’utiliser pour :

  1. Lire les données des requêtes entrantes.
  2. Extraire l’hôte et le port du serveur cible à partir de ces données.
  3. Les utiliser pour transmettre la requête client au serveur de destination.
  4. Récupérer la réponse et la transmettre au client d’origine.

Dans cette section, concentrons-nous sur les deux premières étapes. Définissez la fonction handle_client_request() et utilisez-la pour lire les données de la requête entrante :

def handle_client_request(client_socket):

    print("Received request:n")

    # lire les données envoyées par le client dans la requête

    request = b''

    client_socket.setblocking(False)

    while True:

        try:

            # recevoir les données du serveur web

            data = client_socket.recv(1024)

            request = request + data

            # Recevoir les données du serveur de destination d'origine

            print(f"{data.decode('utf-8')}")

        except:

            break

setblocking(False) définit le socket client en mode non bloquant. Ensuite, utilisez recv() pour lire les données entrantes et les ajouter à request au format bytes. Comme vous ne connaissez pas la taille des données de la requête entrante, vous devez les lire un bloc à la fois. Dans ce cas, un bloc de 1024 octets a été spécifié. En mode non bloquant, si recv() ne trouve aucune donnée, il déclenchera une exception d’erreur. Ainsi, l’instruction except marque la fin de l’opération.

Notez les messages enregistrés pour suivre ce que fait le serveur proxy Python.

Après avoir récupéré la requête entrante, vous devez en extraire l’hôte et le port du serveur de destination :

host, port = extract_host_port_from_request(request)

En particulier, voici à quoi ressemble la fonction extract_host_port_from_request() :

def extract_host_port_from_request(request):

    # obtenir la valeur après la chaîne "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)

    # s'il y a un port spécifique

    port_pos = host_string.find(":")

    # aucun port spécifié

    if port_pos == -1 or webserver_pos < port_pos:

        # port par défaut

        port = 80

        host = host_string[:webserver_pos]

    else:

        # extraire le port spécifique de la chaîne hôte

        port = int((host_string[(port_pos + 1):])[:webserver_pos - port_pos - 1])

        host = host_string[:port_pos]

    return host, port

Pour mieux comprendre ce que cela fait, considérez l'exemple ci-dessous. Voici ce que contient généralement la chaîne encodée d'une requête entrante :

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() extrait l’hôte et le port du serveur web depuis le champ “Host:”. Dans ce cas, host est example.com et port est 80 (aucun port spécifique n’a été spécifié). 

Étape 5 : Transmettre la requête client et gérer la réponse

Étant donné l’hôte et le port cibles, vous devez maintenant transmettre la requête client au serveur de destination. Dans handle_client_request(), créez un nouveau web socket et utilisez-le pour envoyer la requête originale à la destination souhaitée :

# créer un socket pour se connecter au serveur de destination d'origine

destination_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# se connecter au serveur de destination

destination_socket.connect((host, port))

# envoyer la requête originale

destination_socket.sendall(request)

Ensuite, préparez-vous à recevoir la réponse du serveur et à la propager au client d'origine :

# lire les données reçues du serveur

# un bloc à la fois et les envoyer au client

print("Received response:n")

while True:

    # recevoir les données du serveur web

    data = destination_socket.recv(1024)

    # Recevoir les données du serveur de destination d'origine

    print(f"{data.decode('utf-8')}")

    # plus de données à envoyer

    if len(data) > 0:

        # renvoyer au client

        client_socket.sendall(data)

    else:

        break

Encore une fois, vous devez travailler un bloc à la fois car vous ne connaissez pas la taille de la réponse. Lorsque data est vide, il n’y a plus de données à recevoir et vous pouvez terminer l’opération.

N’oubliez pas de fermer les deux sockets que vous avez définis dans la fonction :

# fermer les sockets

destination_socket.close()

client_socket.close()

Génial ! Vous venez de créer un serveur proxy HTTP en Python. Il est temps de voir le code complet, de le lancer et de vérifier qu’il fonctionne comme prévu !

Étape 6 : Tout assembler

Voici le code final de votre script de serveur proxy Python :

import socket

import threading

def handle_client_request(client_socket):

    print("Received request:n")

    # lire les données envoyées par le client dans la requête

    request = b''

    client_socket.setblocking(False)

    while True:

        try:

            # recevoir les données du serveur web

            data = client_socket.recv(1024)

            request = request + data

            # Recevoir les données du serveur de destination d'origine

            print(f"{data.decode('utf-8')}")

        except:

            break

    # extraire l'hôte et le port du serveur web depuis la requête

    host, port = extract_host_port_from_request(request)

    # créer un socket pour se connecter au serveur de destination d'origine

    destination_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    # se connecter au serveur de destination

    destination_socket.connect((host, port))

    # envoyer la requête originale

    destination_socket.sendall(request)

    # lire les données reçues du serveur

    # un bloc à la fois et les envoyer au client

    print("Received response:n")

    while True:

        # recevoir les données du serveur web

        data = destination_socket.recv(1024)

        # Recevoir les données du serveur de destination d'origine

        print(f"{data.decode('utf-8')}")

        # plus de données à envoyer

        if len(data) > 0:

            # renvoyer au client

            client_socket.sendall(data)

        else:

            break

    # fermer les sockets

    destination_socket.close()

    client_socket.close()

def extract_host_port_from_request(request):

    # obtenir la valeur après la chaîne "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)

    # s'il y a un port spécifique

    port_pos = host_string.find(":")

    # aucun port spécifié

    if port_pos == -1 or webserver_pos < port_pos:

        # port par défaut

        port = 80

        host = host_string[:webserver_pos]

    else:

        # extraire le port spécifique de la chaîne hôte

        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

    # lier le serveur proxy à une adresse et un port spécifiques

    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    server.bind(('127.0.0.1', port))

    # accepter jusqu'à 10 connexions simultanées

    server.listen(10)

    print(f"Proxy server listening on port {port}...")

    # écouter les requêtes entrantes

    while True:

        client_socket, addr = server.accept()

        print(f"Accepted connection from {addr[0]}:{addr[1]}")

        # créer un thread pour gérer la requête client

        client_handler = threading.Thread(target=handle_client_request, args=(client_socket,))

        client_handler.start()

if __name__ == "__main__":

    start_proxy_server()

Lancez-le avec cette commande :

python proxy_server.py

Vous devriez voir le message suivant dans le terminal :

Proxy server listening on port 8888...

Pour vous assurer que le serveur fonctionne, exécutez une requête proxy avec cURL. Lisez notre guide pour en savoir plus sur comment utiliser cURL avec un proxy.

Ouvrez un nouveau terminal et exécutez :

curl --proxy "http://127.0.0.1:8888" "http://httpbin.org/ip"

Cela ferait une requête GET vers la destination http://httpbin.org/ip via le serveur proxy http://127.0.0.1:8888.

Vous devriez obtenir quelque chose comme :

{

  "origin": "45.12.80.183"

}

C’est l’IP du serveur proxy. Pourquoi ? Parce que le point d’accès /ip du projet HTTPBin renvoie l’IP d’où provient la requête. Si vous exécutez le serveur localement, “origin” correspondra à votre IP. 

Note : Le serveur proxy Python construit ici fonctionne uniquement avec des destinations HTTP. L’étendre pour gérer les connexions HTTPS est assez délicat.

Maintenant, explorez le journal écrit par votre application de serveur proxy Python. Il devrait contenir :

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"

}

Cela vous indique que le serveur proxy a reçu la requête dans le format spécifié par le protocole HTTP. Ensuite, il l’a transmise au serveur de destination, a enregistré les données de réponse, et a renvoyé la réponse au client. Pourquoi en sommes-nous sûrs ? Parce que les IPs dans “origin” sont identiques !

Félicitations ! Vous venez d’apprendre à créer un serveur proxy HTTP en Python !

Avantages et inconvénients d’un serveur proxy Python personnalisé

Maintenant que vous savez comment implémenter un serveur proxy en Python, vous êtes prêt à découvrir les avantages et limites de cette approche.

Avantages :

  • Contrôle total : Avec un script Python personnalisé comme celui-ci, vous avez un contrôle total sur ce que fait votre serveur proxy. Aucune activité suspecte ou fuite de données !
  • Personnalisation : Le serveur proxy peut être étendu pour inclure des fonctionnalités utiles telles que la journalisation et la mise en cache des requêtes pour améliorer les performances.

Inconvénients :

  • Coûts d’infrastructure : Mettre en place une architecture de serveur proxy n’est pas facile et coûte beaucoup d’argent en termes de matériel ou de services VPS.
  • Difficile à maintenir : Vous êtes responsable du maintien de l’architecture du proxy, en particulier de sa scalabilité et de sa disponibilité. C’est une tâche que seuls des administrateurs système expérimentés peuvent gérer.
  • Peu fiable : Le principal problème de cette solution est que l’IP de sortie du serveur proxy ne change jamais. Par conséquent, les technologies anti-bot pourront bloquer l’IP et empêcher le serveur d’accéder aux requêtes souhaitées. En d’autres termes, le proxy finira par cesser de fonctionner.

Ces limitations et inconvénients sont trop importants pour utiliser un serveur proxy Python personnalisé dans un scénario de production. La solution ? Un fournisseur de proxy fiable comme Bright Data ! Créez un compte, vérifiez votre identité, obtenez un proxy, et utilisez-le dans votre langage de programmation préféré. Par exemple, intégrez un proxy dans votre script Python avec requests.

Notre immense réseau de proxys comprend des millions de serveurs proxy rapides, fiables et sécurisés dans le monde entier. Découvrez pourquoi nous sommes le meilleur fournisseur de serveurs proxy.

Conclusion

Dans ce guide, vous avez appris ce qu’est un serveur proxy et comment il fonctionne en Python. En détail, vous avez appris à en construire un à partir de zéro en utilisant des web sockets. Vous êtes désormais devenu un maître des proxys en Python. Le principal problème de cette approche est que l’IP de sortie statique de votre serveur proxy finira par vous faire bloquer. Évitez cela avec les proxys rotatifs de Bright Data !

Bright Data contrôle les meilleurs serveurs proxy au monde, servant des entreprises du Fortune 500 et 50,000+ clients. Son offre comprend une large gamme de types de proxy :

Ce réseau de proxy fiable, rapide et mondial est également à la base d’un certain nombre de services de scraping web pour récupérer sans effort des données de n’importe quel site.