Was ist ein Python Proxy-Server?

Der Python-Proxy-Server ermöglicht das Routing von HTTP/S-Anfragen über ein riesiges Netzwerk von IP-Adressen per Python-Code. Er unterstützt Funktionen wie beispielsweise IP-Rotation, Sitzungspersistenz und Geotargeting.
13 min read
Python Proxy Server

In diesem Tutorial lernen Sie Folgendes:

Legen wir also los!

Was ist ein Python Proxy-Server?

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 wird die Identität des Clients gegenüber den Zielservern maskiert.

Lesen Sie unseren Artikel, um zu erfahren, was ein Proxy-Server ist und wie er funktioniert.

Die Socket-Programmierfunktionen von Python erleichtern die Implementierung eines einfachen Proxy-Servers, sodass Benutzer den Netzwerkverkehr überprüfen, ändern oder umleiten können. Proxy-Server eignen sich hervorragend zum Caching, zur Verbesserung der Leistung sowie zur Erhöhung der Sicherheit beim Web-Scraping.

Wie implementiert man einen HTTP-Proxy-Server in Python?

Folgen Sie den nachstehenden Anweisungen und lernen Sie, ein Python-Proxy-Server-Skript zu erstellen.

Schritt 1: Initialisieren Sie Ihr Python-Projekt

Stellen Sie zunächst sicher, dass Python 3+ auf Ihrem Computer installiert ist. Laden Sie ansonsten das Installationsprogramm herunter, führen Sie es aus und folgen Sie den Anweisungen des Installationsassistenten.

Verwenden Sie als Nächstes die folgenden Befehle, um einen python-http-proxy-server-Ordner zu erstellen und ein Python-Projekt mit einer darin enthaltenen 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 namens proxy_server.py.

Großartig! Nun haben Sie alles, was Sie benötigen, um einen HTTP-Proxy-Server in Python zu erstellen.

Schritt 2: Einen eingehenden Socket initialisieren

Dazu müssen Sie zuerst einen Websocket-Server für die Annahme eingehender Anfragen erstellen. Falls Ihnen dieses Konzept nicht bekannt ist – ein Socket ist eine Programmierabstraktion auf niedriger Ebene, 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 Web-Server 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)

Damit wird ein eingehender Socket-Server initialisiert und an die lokale Adresse http://127.0.0.1:8888 gebunden. Dann ermöglicht dies dem Server, Verbindungen mit der Methode listen() anzunehmen.

Hinweis: Sie können die Nummer des Ports ändern, auf den der Web-Proxy hören soll. Zudem können Sie das Skript auch so ändern, dass es diese Informationen von der Befehlszeile liest, um maximale Flexibilität zu erzielen.

Socket stammt aus der Python Standard-Bibliothek. Sie werden also den folgenden Import zusätzlich zu Ihrem Skript haben:

import socket

Um zu überwachen, ob der Python Proxy-Server wie erforderlich gestartet wurde, protokollieren Sie diese Nachricht:

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

Schritt 3: Client-Anfragen annehmen

Wenn ein Client eine Verbindung zum Proxy-Server herstellt, muss dieser einen neuen Socket erstellen, um die Kommunikation mit diesem bestimmten Client abzuwickeln. So können Sie dies 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 bearbeiten, sollten Sie Multithreading wie oben beschrieben verwenden. Denken Sie daran, Threading aus der Python-Standard-Bibliothek zu importieren:

import threading

Wie Sie sehen können, verarbeitet der Proxy-Server eingehende Anfragen über die benutzerdefinierte Funktion handle_client_request(). In den nächsten Schritten sehen Sie, wie dies definiert wird.

Schritt 4: Bearbeitung der eingehenden Anfragen

Sobald der Client-Socket erstellt wurde, müssen Sie ihn verwenden, um:

  1. die Daten der eingehenden Anfragen zu lesen.
  2. den Host und den Port des Zielservers aus diesen Daten zu extrahieren.
  3. die Client-Anfrage an den Zielserver weiterzuleiten.
  4. die Antwort zu erhalten und diese an den ursprünglichen Kunden 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) versetzt den Client-Socket in den nicht blockierenden Modus. Verwenden Sie dann recv(), um die eingehenden Daten zu lesen und sie im Byte-Format an die Anfrage anzuhängen. Da Sie die Größe der eingehenden Anforderungsdaten nicht kennen, müssen Sie sie Block für Block lesen. In diesem Fall wurde ein Block von 1024 Byte angegeben. Wenn recv() im nicht blockierenden Modus keine Daten findet, wird eine Fehlerausnahme ausgelöst. Somit markiert die Anweisung except das Ende des Vorgangs.

Notieren Sie sich die protokollierten Nachrichten, um zu verfolgen, was der Python Proxy-Server tut.

Nachdem Sie die eingehende Anfrage abgerufen haben, müssen Sie den Host und den 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'\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() extrahiert den Host und den Port des Webservers aus dem Feld „Host:“. In diesem Fall ist der Host example.com und der Port ist 80 (da kein bestimmter Port angegeben wurde). 

Schritt 5: Client-Anfrage weiterleiten und Antwort bearbeiten

Angesichts des Zielhosts und -ports müssen Sie jetzt 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 einen Block nach dem anderen bearbeiten, da Sie die Größe der Antwort nicht kennen. Sobald das Datenfeld leer ist, müssen keine weiteren Daten empfangen werden und Sie können den Vorgang beenden.

Vergessen Sie nicht, die beiden in der Funktion definierten Sockets zu schließen:

# close the sockets

destination_socket.close()

client_socket.close()

Fantastisch! Sie haben gerade einen HTTP-Proxy-Server in Python erstellt. Nun ist es an der Zeit, den gesamten Code einzusehen, 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 Proxy-Server-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'\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

Sie sollten die 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 cURL mit einem Proxy verwendet wird.

Neues Terminal öffnen und ausführen:

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 Proxy-Server http://127.0.0.1:8888 stellen.

Sie sollten etwas erhalten wie:

{

  "origin": "45.12.80.183"

}

Dies ist die IP des Proxy-Servers. Warum? Weil der /ip-Endpunkt des HttpBin-Projekts die IP zurücksendet, von welcher die Anfrage stammt. Wenn Sie den Server lokal betreiben, entspricht „origin“ Ihrer IP. 

Hinweis: Der hier erstellte Python Proxy-Server funktioniert nur mit HTTP-Zielen. Es ist ziemlich schwierig, diesen derart zu erweitern, dass er HTTPS-Verbindungen handhaben kann.

Erkunden Sie nun das Protokoll, das von der Python-Anwendung Ihres Proxy-Servers geschrieben wurde. 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"

}

Daran können Sie erkennen, dass der Proxy-Server die Anfrage in dem vom HTTP-Protokoll angegebenen Format erhalten hat. Anschließend hat er sie an den Zielserver weitergeleitet, die Antwortdaten protokolliert und die Antwort an den Client zurückgesendet. Warum sind wir uns dessen sicher? Weil die IPs in „origin“ dieselben sind!

Herzlichen Glückwunsch! Sie haben gerade gelernt, wie man einen HTTP-Proxy-Server in Python baut!

Vor- und Nachteile der Verwendung eines benutzerdefinierten Python Proxy-Servers

Nun, da Sie wissen, wie Sie einen Proxy-Server in Python implementieren, sind Sie bereit, die Vorteile und Einschränkungen dieses Ansatzes zu erkennen.

Vorteile:

  • Totale Kontrolle: Mit einem benutzerdefinierten Python-Skript wie diesem haben Sie die volle Kontrolle darüber, was Ihr Proxy-Server tut. Da gibt es keine zwielichtigen Aktivitäten oder Datenlecks!
  • Anpassung: Der Proxy-Server kann um nützliche Funktionen wie etwa das Protokollieren und Zwischenspeichern von Anfragen erweitert werden, um die Leistung zu verbessern.

Nachteile:

  • Infrastrukturkosten: Die Einrichtung einer Proxy-Server-Architektur ist nicht einfach und erfordert eine hohe Investition für Hardware oder VPS-Dienste.
  • Schwer zu warten: Sie sind für die Wartung der Proxy-Architektur verantwortlich, insbesondere für die Skalierbarkeit und Verfügbarkeit des Proxys. 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 Proxy-Servers nie ändert. Infolgedessen können Anti-Bot-Technologien die IP blockieren und verhindern, dass der Server auf die gewünschten Anfragen zugreift. Dies bedeutet, dass der Proxy irgendwann nicht mehr funktionieren wird.

Diese Einschränkungen und Nachteile sind zu gravierend, um einen benutzerdefinierten Python Proxy-Server in einem Produktionsszenario zu verwenden. Die Lösung? Ein zuverlässiger Proxy-Anbieter wie Bright Data! Erstellen Sie ein Konto, bestätigen Sie Ihre Identität, holen Sie sich einen kostenlosen Proxy und verwenden Sie ihn in Ihrer bevorzugten Programmiersprache. Integrieren Sie beispielsweise einen Proxy in Ihr Python-Skript mit Anfragen.

Unser riesiges Proxy-Netzwerk umfasst Millionen von schnellen, zuverlässigen und sicheren Proxy-Servern weltweit. Finden Sie heraus, warum wir der beste Proxy-Server-Anbieter sind.

Fazit

In diesem Leitfaden haben Sie erfahren, was ein Proxy-Server ist und wie er in Python funktioniert. Sie haben Schritt für Schritt gelernt, wie Sie einen Proxy mithilfe von Web-Sockets von Grund auf neu erstellen können. Somit sind Sie nun ein Proxy-Meister in Python geworden. Das Hauptproblem bei diesem Ansatz ist, dass die statische Ausgangs-IP Ihres Proxy-Servers Sie irgendwann blockieren wird. Dies können Sie mit den rotierenden Proxys von Bright Data verhindern!

Bright Data steuert die besten Proxy-Server der Welt und bedient Fortune-500-Unternehmen sowie über 20.000 Kunden. Das Angebot umfasst eine Vielzahl von Proxy-Arten:

Dieses zuverlässige, schnelle und globale Proxy-Netzwerk ist auch die Grundlage für diverse Web-Scraping-Dienste, mit denen mühelos Daten von jeder Website abgerufen werden können.