Dans ce didacticiel, vous allez apprendre :
- Qu’est-ce qu’un serveur proxy Python et comment il fonctionne.
- Étapes requises pour créer un serveur proxy HTTP en Python.
- Les avantages et les inconvénients de cette approche.
C’est parti !
Qu’est-ce qu’un serveur proxy Python ?
Un serveur proxy Python est une application Python qui sert d’intermédiaire entre les clients et Internet. Il intercepte les demandes des clients, les transmet aux serveurs cibles et renvoie la réponse au client. Ce faisant, il masque l’identité du client aux serveurs de destination.
Lisez notre article pour découvrir ce qu’est un serveur proxy et comment il fonctionne.
Les capacités de programmation de socket de Python facilitent la mise en œuvre d’un serveur proxy de base, permettant aux utilisateurs d’inspecter, de modifier ou de rediriger le trafic réseau. Les serveurs proxy sont parfaits pour la mise en cache, l’amélioration des performances et la sécurité en matière de web scraping.
Comment implémenter un serveur proxy HTTP en Python
Suivez les étapes ci-dessous et apprenez à créer un script de serveur proxy Python.
Étape 1 : initialisez votre projet Python
Avant de commencer, assurez-vous que Python 3+ est installé sur votre machine. Sinon, téléchargez le programme d’installation, exécutez-le et suivez-en les instructions.
Ensuite, utilisez les commandes ci-dessous pour créer un dossier python-http-proxy-server et initialiser un projet Python contenant un environnement virtuel :
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.
Formidable ! Vous avez tout ce dont vous avez besoin pour créer un serveur proxy HTTP en Python.
Étape 2 : initialisez un socket entrant
Tout d’abord, vous devez créer un serveur de socket Web pour accepter les demandes 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 de 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
# 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)
Cela initialise un serveur de socket entrant et le lie à l’adresse locale http://127.0.0.1:8888. Il permet ensuite au serveur d’accepter les connexions avec la méthode listen ().
Remarque : n’hésitez pas à modifier le numéro du port que le proxy Web doit écouter. Vous pouvez également modifier le script pour lire ces informations depuis la ligne de commande pour une flexibilité maximale.
Le socket provient de la bibliothèque standard Python. Vous aurez donc l’importation suivante en plus de votre script :
import socket
Pour vérifier que le serveur proxy Python a démarré comme prévu, enregistrez ce message :
print(f"Proxy server listening on port {port}...")
Étape 3 : acceptez les demandes 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 :
# 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()
Pour gérer plusieurs demandes 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 constater, le serveur proxy gère les demandes entrantes via la fonction personnalisée handle_client_request (). Découvrez comment il est défini dans les étapes suivantes.
Étape 4 : traitez les demandes entrantes
Une fois le socket client créé, vous devez l’utiliser pour :
- Lire les données des demandes entrantes.
- Extraire l’hôte et le port du serveur cible à partir de ces données.
- Les utiliser pour transmettre la demande du client au serveur de destination.
- Obtenir la réponse et la transférer 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")
# 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) met le socket client en mode non bloquant. Ensuite, utilisez recv() pour lire les données entrantes et les ajouter à la requête au format octet. Comme vous ne connaissez pas la taille des données de demande entrantes, vous devez les lire un morceau à la fois. Dans ce cas, un bloc de 1 024 octets a été spécifié. En mode non bloquant, si recv() ne trouve aucune donnée, cela 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 demande entrante, vous devez en extraire l’hôte et le port du serveur de destination :
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() extrait l’hôte et le port du serveur Web à partir du champ « Host: ». Dans ce cas, l’hôte est example.com et le port est 80 (car aucun port spécifique n’a été spécifié).
Étape 5 : transférez la demande du client et gérez la réponse
Compte tenu de l’hôte et du port cibles, vous devez maintenant transmettre la demande du client au serveur de destination. Dans handle_client_request(), créez un nouveau socket Web et utilisez-le pour envoyer la demande d’origine à la destination souhaitée :
# 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
Encore une fois, vous devez travailler un morceau à la fois, car vous ne connaissez pas la taille de la réponse. Lorsque les données sont vides, il n’y a plus de données à recevoir et vous pouvez mettre fin à l’opération.
N’oubliez pas de fermer les deux sockets que vous avez définis dans la fonction :
# close the 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 l’intégralité du code, de le lancer et de vérifier qu’il fonctionne comme prévu !
Étape 6 : assemblez le tout
Voici le code final du script de votre serveur 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
Le message suivant devrait s’afficher 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 enverrait une requête GET à 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"
}
Il s’agit de l’adresse IP du serveur proxy. Pourquoi ? Parce que l’endpoint /ip du projet HTTPBin renvoie l’adresse IP d’où provient la demande. Si vous utilisez le serveur localement, « origin » correspondra à votre adresse IP.
Remarque : le serveur proxy Python créé ici ne fonctionne qu’avec des destinations HTTP. L’étendre pour gérer les connexions HTTPS est assez difficile.
À présent, explorez le journal écrit par l’application Python de votre serveur proxy. Il doit 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 demande dans le format spécifié par le protocole HTTP. Il l’a ensuite transmis 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 adresses IP dans « origin » sont les mêmes !
Félicitations ! Vous venez d’apprendre à créer un serveur proxy HTTP en Python !
Avantages et inconvénients de l’utilisation d’un serveur proxy Python personnalisé
Maintenant que vous savez comment implémenter un serveur proxy en Python, vous êtes prêt(e) à découvrir les avantages et les limites de cette approche.
Avantages :
- Contrôle total : avec un script Python personnalisé comme celui-ci, vous avez un contrôle total sur le fonctionnement de votre serveur proxy. Pas d’activité douteuse ni de 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 afin d’améliorer les performances.
Inconvénients :
- Coûts d’infrastructure : la mise en place d’une architecture de serveur proxy n’est pas facile et coûte cher en termes de matériel ou de services VPS.
- Difficile à gérer : vous êtes responsable de la maintenance de l’architecture du proxy, en particulier de son évolutivité et de sa disponibilité. Il s’agit d’une tâche que seuls les administrateurs système expérimentés peuvent effectuer.
- Peu fiable : le principal problème avec cette solution est que l’adresse IP de sortie du serveur proxy ne change jamais. Par conséquent, les technologies anti-bot seront en mesure de bloquer l’IP et d’empêcher le serveur d’accéder aux requêtes souhaitées. En d’autres termes, le proxy finira par cesser de fonctionner.
Ces limites et inconvénients sont trop graves 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 gratuit et utilisez-le dans votre langage de programmation préféré. Par exemple, intégrez un proxy dans votre script Python avec des requêtes.
Notre vaste réseau de proxy 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. Dans le détail, vous avez appris à en créer un à partir de zéro à l’aide de sockets Web. Vous êtes désormais un maître des proxys en Python. Le principal problème avec cette approche est que l’adresse IP de sortie statique de votre serveur proxy finira par vous bloquer. Évitez cela avec les proxys rotatifs de Bright Data !
Bright Data contrôle les meilleurs serveurs proxy au monde, au service des entreprises du Fortune 500 et de plus de 20 000 clients. Son offre comprend un large éventail de types de proxy :
- Proxys de centre de données — Plus de 770 000 adresses IP de datacenters.
- Proxys résidentiels — Plus de 72 millions d’adresses IP résidentielles dans plus de 195 pays.
- Proxys de fournisseurs d’accès à Internet — Plus de 700 000 adresses IP de fournisseurs d’accès Internet.
- Proxys mobiles — Plus de 7 millions d’adresses IP mobiles.
Ce réseau proxy fiable, rapide et mondial est également à la base d’un certain nombre de services de web scraping permettant de récupérer facilement des données depuis n’importe quel site.
Aucune carte de crédit requise