In diesem Tutorial lernen Sie:
- Was ein Python-Proxyserver ist und wie er funktioniert
- Die Schritte zum Erstellen eines HTTP-Proxyservers in Python.
- Die Vor- und Nachteile dieses Ansatzes.
Legen wir los!
Was ist ein Python-Proxyserver?
Ein Python-Proxy-Server ist eine Python-Anwendung, die als Vermittler zwischen Clients und dem Internet fungiert. Er fängt Anfragen von Clients ab, leitet sie an die Zielserver weiter und sendet die Antwort zurück an den Client. Dadurch verschleiert er die Identität des Clients gegenüber den Zielservern.
Lesen Sie unseren Artikel, um zu erfahren, was ein Proxyserver ist und wie er funktioniert.
Die Socket-Programmierungsfunktionen von Python machen es einfach, einen einfachen Proxyserver zu implementieren, mit dem Benutzer Netzwerkverkehr überprüfen, ändern oder umleiten können. Proxyserver eignen sich hervorragend zum Caching, zur Leistungsverbesserung und zur Erhöhung der Sicherheit beim Web-Scraping.
Wie man einen HTTP-Proxyserver in Python implementiert
Folgen Sie den nachstehenden Schritten und lernen Sie, wie man ein Python-Proxyserver-Skript erstellt.
Schritt 1: Initialisieren Sie Ihr Python-Projekt
Stellen Sie vor dem Start sicher, dass Python 3+ auf Ihrem Rechner installiert ist. Andernfalls laden Sie das Installationsprogramm herunter, führen Sie es aus und folgen Sie dem Installationsassistenten.
Verwenden Sie dann die folgenden Befehle, um einen python-http-proxy-server-Ordner zu erstellen und darin ein Python-Projekt mit einer virtuellen Umgebung zu initialisieren:
mkdir python-http-proxy-server
cd python-http-proxy-server
python -m venv env
Öffnen Sie den Ordner python-http-proxy-server in Ihrer Python-IDE und erstellen Sie eine leere Datei proxy_server.py.
Großartig! Sie haben alles, was Sie brauchen, um einen HTTP-Proxyserver in Python zu erstellen.
Schritt 2: Initialisieren Sie einen eingehenden Socket
Zuerst müssen Sie einen Web-Socket-Server erstellen, um eingehende Anfragen zu akzeptieren. Falls Ihnen dieses Konzept nicht vertraut ist: Ein Socket ist eine Low-Level-Programmierabstraktion, die einen bidirektionalen Datenfluss zwischen einem Client und einem Server ermöglicht. Im Kontext eines Webservers wird ein Server-Socket verwendet, um auf eingehende Verbindungen von Clients zu warten.
Verwenden Sie die folgenden Zeilen, um einen Socket-basierten Webserver in Python zu erstellen:
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)
Dies initialisiert einen eingehenden Socket-Server und bindet ihn an die lokale Adresse http://127.0.0.1:8888. Anschließend ermöglicht es dem Server, Verbindungen mit der Methode listen() zu akzeptieren.
Hinweis: Sie können die Portnummer, auf der der Web-Proxy lauschen soll, beliebig ändern. Sie können das Skript auch so anpassen, dass diese Information zur maximalen Flexibilität aus der Befehlszeile gelesen wird.
socket stammt aus der Python Standard Library. Sie benötigen also den folgenden Import am Anfang Ihres Skripts:
import socket
Um zu überwachen, dass der Python-Proxyserver wie erwartet gestartet wurde, protokollieren Sie diese Meldung:
print(f"Proxy server listening on port {port}...")
Schritt 3: Client-Anfragen akzeptieren
Wenn sich ein Client mit dem Proxyserver verbindet, muss dieser einen neuen Socket erstellen, um die Kommunikation mit diesem bestimmten Client zu übernehmen. So können Sie das in Python tun:
# 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()
Um mehrere Client-Anfragen gleichzeitig zu verarbeiten, sollten Sie, wie oben gezeigt, Multithreading verwenden. Vergessen Sie nicht, threading aus der Python Standard Library zu importieren:
import threading
Wie Sie sehen können, verarbeitet der Proxyserver eingehende Anfragen über die benutzerdefinierte Funktion handle_client_request(). Sehen Sie, wie sie in den nächsten Schritten definiert wird.
Schritt 4: Verarbeitung der eingehenden Anfragen
Sobald der Client-Socket erstellt wurde, müssen Sie ihn verwenden, um:
- Die Daten aus den eingehenden Anfragen zu lesen.
- Host und Port des Zielservers aus diesen Daten zu extrahieren.
- Diese zu verwenden, um die Client-Anfrage an den Zielserver weiterzuleiten.
- Die Antwort zu erhalten und an den ursprünglichen Client weiterzuleiten.
In diesem Abschnitt konzentrieren wir uns auf die ersten beiden Schritte. Definieren Sie die Funktion handle_client_request() und verwenden Sie sie, um die Daten aus der eingehenden Anfrage zu lesen:
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) setzt den Client-Socket in den nicht blockierenden Modus. Verwenden Sie dann recv(), um die eingehenden Daten zu lesen und sie im Byte-Format an request anzuhängen. Da Sie die Größe der eingehenden Anfragedaten nicht kennen, müssen Sie sie Stück für Stück lesen. In diesem Fall wurde ein Chunk von 1024 Bytes festgelegt. Im nicht blockierenden Modus löst recv() eine Fehlerausnahme aus, wenn es keine Daten findet. Daher markiert die except-Anweisung das Ende der Operation.
Beachten Sie die protokollierten Meldungen, um zu verfolgen, was der Python-Proxyserver tut.
Nach dem Abrufen der eingehenden Anfrage müssen Sie Host und Port des Zielservers daraus extrahieren:
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'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)
# 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() extrahiert den Host und Port des Webservers aus dem Feld “Host:”. In diesem Fall ist host example.com und port 80 (da kein bestimmter Port angegeben wurde).
Schritt 5: Die Client-Anfrage weiterleiten und die Antwort verarbeiten
Mit Host und Port des Ziels müssen Sie nun die Client-Anfrage an den Zielserver weiterleiten. Erstellen Sie in handle_client_request() einen neuen Web-Socket und verwenden Sie ihn, um die ursprüngliche Anfrage an das gewünschte Ziel zu senden:
# 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
Auch hier müssen Sie Stück für Stück arbeiten, da Sie die Größe der Antwort nicht kennen. Wenn data leer ist, gibt es keine weiteren Daten zu empfangen und Sie können die Operation beenden.
Vergessen Sie nicht, die beiden in der Funktion definierten Sockets zu schließen:
# close the sockets
destination_socket.close()
client_socket.close()
Ausgezeichnet! Sie haben gerade einen HTTP-Proxyserver in Python erstellt. Zeit, sich den gesamten Code anzusehen, ihn zu starten und zu überprüfen, ob er wie erwartet funktioniert!
Schritt 6: Alles zusammenfügen
Dies ist der endgültige Code Ihres Python-Proxyserver-Skripts:
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'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)
# 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
Sie sollten folgende Meldung im Terminal sehen:
Proxy server listening on port 8888...
Um sicherzustellen, dass der Server funktioniert, führen Sie eine Proxy-Anfrage mit cURL aus. Lesen Sie unseren Leitfaden, um mehr darüber zu erfahren, wie man cURL mit einem Proxy verwendet.
Öffnen Sie ein neues Terminal und führen Sie aus:
curl --proxy "http://127.0.0.1:8888" "http://httpbin.org/ip"
Dies würde eine GET-Anfrage an das Ziel http://httpbin.org/ip über den Proxyserver http://127.0.0.1:8888 stellen.
Sie sollten etwa Folgendes erhalten:
{
"origin": "45.12.80.183"
}
Das ist die IP des Proxyservers. Warum? Weil der /ip-Endpunkt des HTTPBin-Projekts die IP zurückgibt, von der die Anfrage kommt. Wenn Sie den Server lokal ausführen, entspricht “origin” Ihrer eigenen IP.
Hinweis: Der hier erstellte Python-Proxyserver funktioniert nur mit HTTP-Zielen. Ihn so zu erweitern, dass er HTTPS-Verbindungen unterstützt, ist ziemlich kompliziert.
Untersuchen Sie nun das von Ihrer Proxyserver-Python-Anwendung geschriebene Protokoll. Es sollte Folgendes enthalten:
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"
}
Dies zeigt Ihnen, dass der Proxyserver die Anfrage im vom HTTP-Protokoll vorgegebenen Format erhalten hat. Anschließend leitete er sie an den Zielserver weiter, protokollierte die Antwortdaten und sendete die Antwort zurück an den Client. Warum sind wir uns dessen sicher? Weil die IPs in “origin” identisch sind!
Glückwunsch! Sie haben gerade gelernt, wie man einen HTTP-Proxyserver in Python erstellt!
Vor- und Nachteile der Verwendung eines benutzerdefinierten Python-Proxyservers
Nachdem Sie nun wissen, wie man einen Proxyserver in Python implementiert, sind Sie bereit, die Vorteile und Einschränkungen dieses Ansatzes kennenzulernen.
Vorteile:
- Volle Kontrolle: Mit einem benutzerdefinierten Python-Skript wie diesem haben Sie die volle Kontrolle darüber, was Ihr Proxyserver tut. Keine fragwürdigen Aktivitäten oder Datenlecks!
- Anpassbarkeit: Der Proxyserver kann um nützliche Funktionen wie Protokollierung und Caching von Anfragen erweitert werden, um die Leistung zu verbessern.
Nachteile:
- Infrastrukturkosten: Der Aufbau einer Proxyserver-Architektur ist nicht einfach und kostet viel Geld für Hardware oder VPS-Dienste.
- Schwer zu warten: Sie sind für die Wartung der Proxy-Architektur verantwortlich, insbesondere für deren Skalierbarkeit und Verfügbarkeit. Dies ist eine Aufgabe, die nur erfahrene Systemadministratoren bewältigen können.
- Unzuverlässig: Das Hauptproblem bei dieser Lösung ist, dass sich die Ausgangs-IP des Proxyservers nie ändert. Dadurch können Anti-Bot-Technologien die IP blockieren und den Server daran hindern, auf die gewünschten Anfragen zuzugreifen. Mit anderen Worten: Der Proxy wird irgendwann nicht mehr funktionieren.
Diese Einschränkungen und Nachteile sind zu gravierend, um einen benutzerdefinierten Python-Proxyserver in einem Produktionsszenario zu verwenden. Die Lösung? Ein zuverlässiger Proxy-Anbieter wie Bright Data! Erstellen Sie ein Konto, verifizieren Sie Ihre Identität, holen Sie sich einen Proxy und verwenden Sie ihn in Ihrer bevorzugten Programmiersprache. Zum Beispiel können Sie einen Proxy mit requests in Ihr Python-Skript integrieren.
Unser riesiges Proxy-Netzwerk umfasst Millionen schneller, zuverlässiger und sicherer Proxyserver auf der ganzen Welt. Finden Sie heraus, warum wir der beste Proxyserver-Anbieter sind.
Fazit
In diesem Leitfaden haben Sie erfahren, was ein Proxyserver ist und wie er in Python funktioniert. Im Detail haben Sie gelernt, wie man einen solchen von Grund auf mit Web-Sockets erstellt. Sie sind nun ein Meister der Proxys in Python geworden. Das Hauptproblem bei diesem Ansatz ist, dass die statische Ausgangs-IP Ihres Proxyservers irgendwann zu einer Blockierung führen wird. Vermeiden Sie dies mit den rotierenden Proxys von Bright Data!
Bright Data kontrolliert die besten Proxyserver der Welt und bedient Fortune-500-Unternehmen und 50,000+ Kunden. Das Angebot umfasst eine breite Palette von Proxy-Typen:
- Datacenter-Proxys – 1,300,000+ Datacenter-IPs.
- Residential-Proxys – 400M+ Residential-IPs in mehr als 195 Ländern.
- ISP-Proxys – 1,300,000+ ISP-IPs.
Dieses zuverlässige, schnelle und globale Proxy-Netzwerk ist auch die Grundlage für eine Reihe von Web-Scraping-Diensten, um mühelos Daten von jeder Website abzurufen.