Los ProxyError, SSLError & ConnectTimeout in Requests op
requests.exceptions.ProxyError is een symptoom, geen oorzaak. Hier is de uitzonderingshiërarchie ontcijferd, elke traceback gekoppeld aan de echte oplossing, en een gezondheidscontrolefunctie die mislukkingen classificeert en roteert rond dode exits.
requests.exceptions.ProxyError is een van de minst behulpzame foutmeldingen in Python: het treedt op bij een dode proxy, verkeerde inloggegevens, een slechte schema, een overbelaste exit en een firewallblokkade, allemaal met bijna identieke tracebacks. De truc om het snel op te lossen is te weten dat ProxyError geen hoofdoorzaak is — het is een categorie. In de Requests-bron, subklasseert ProxyError, SSLError en ConnectTimeout allemaal ConnectionError, en elk treedt op in een specifieke fase van de verzoeklevenscyclus. Lees de fase en je leest de oorzaak. Deze gids decodeert de uitzonderingshiërarchie, koppelt elke veelvoorkomende traceback aan de echte oplossing, en geeft je een gezondheidscontrolefunctie die mislukkingen classificeert en automatisch roteert rond stervende exits.
De requests uitzonderingshiërarchie
Elke proxy-gerelateerde fout in Requests stamt af van RequestException. De nuttige tak voor debugging is ConnectionError, omdat de drie uitzonderingen die je daadwerkelijk tegenkomt daaronder vallen:
- ProxyError — treedt op wanneer de verbinding met de proxy zelf mislukt: onbereikbare host, verkeerde poort, geweigerde verbinding of een afgewezen
407. Het bericht nestelt vaakKan geen verbinding maken met proxybinnen eenHTTPSConnectionPool(...)wrapper. - SSLError — de proxy is verbonden, maar de TLS-handshake met het doel is mislukt. In proxy-opstellingen is de gebruikelijke trigger
https://geschreven binnen dehttpssleutel in plaats vanhttp://. - ConnectTimeout — de proxy reageerde niet binnen het verbindingsvenster. Het is een subklasse van zowel
ConnectionErroralsTimeout, en is expliciet gedocumenteerd als veilig om opnieuw te proberen. - ReadTimeout — de proxy is verbonden en heeft het verzoek doorgestuurd, maar het doel was te traag om te reageren. Dit is een probleem met de kwaliteit van het doel of de exit, geen configuratiefout.
- MissingSchema / InvalidProxyURL — treedt op voor elke netwerkoproep wanneer de proxy-URL verkeerd is gevormd. Dit zijn pure typfouten.
Omdat de eerste drie een ouder delen, vangt één except requests.exceptions.ConnectionError ze allemaal voor retry-logica — terwijl het individueel vangen van de subklassen je laat loggen waarom elk is mislukt. De schone opstelling die de meeste van deze vermijdt, wordt behandeld in onze Python Requests proxy gids; deze post gaat over wat te doen zodra de traceback al op het scherm staat.
Een gewoonte bespaart meer tijd dan elke enkele oplossing: lees de traceback van onder naar boven. Requests omhult de onderliggende urllib3-fout, dus de bovenste frames beschrijven waar de oproep is gedaan en de onderste frames beschrijven wat er misging. De regel die je wilt is de binnenste Veroorzaakt door clausule — het noemt de concrete fout (een geweigerde verbinding, een certificaatmismatch, een geparseerde-poortfout) die de buitenste ProxyError of ConnectionError slechts opnieuw verhoogt. Zodra je die regel kunt lezen, is de rest van deze gids een opzoektafel.
De klassieke ValueError vóór ProxyError
De meest gezochte proxy traceback is niet eens een ProxyError — het is ValueError: ongeldige literal voor int() met basis 10, gegooid diep binnen urllib3. Het gebeurt wanneer je inloggegevens in de proxywaarde embedt zonder een schema, zodat de parser de tekst na de dubbele punt leest als een poortnummer:
# Broken — no scheme, so 'pass@host' is parsed as host:port
proxies = {"https": "user:pass@45.11.22.33:8000"}
# -> ValueError: invalid literal for int() with base 10: 'pass@45.11.22.33'
# Fixed — scheme in front, password URL-encoded if it has @ : or /
from urllib.parse import quote
pw = quote("p@ss:word", safe="")
proxies = {
"http": f"http://user:{pw}@gate.quantumproxies.io:PORT",
"https": f"http://user:{pw}@gate.quantumproxies.io:PORT",
}

Een proxy gezondheidscontrole die mislukkingen classificeert
In plaats van te gokken, vang elke uitzonderingstype en zet het om in een eenvoudig-Engels oordeel. Deze functie retourneert het exit-IP bij succes en een gelabelde reden bij mislukking — plaats het voor elke scrape om te bevestigen dat de proxy leeft voordat je er verzoeken aan besteedt. Het werkt tegen elke geauthenticeerde gateway, inclusief residential proxies:
import requests
def check_proxy(proxies, url="https://httpbin.org/ip", timeout=(5, 20)):
try:
r = requests.get(url, proxies=proxies, timeout=timeout)
r.raise_for_status()
return True, r.json().get("origin")
except requests.exceptions.ProxyError as e:
return False, f"proxy unreachable or auth rejected: {e}"
except requests.exceptions.SSLError as e:
return False, f"TLS failed (https:// in the https key?): {e}"
except requests.exceptions.ConnectTimeout:
return False, "proxy did not answer within the connect window"
except requests.exceptions.ReadTimeout:
return False, "target too slow after connect (exit quality)"
except requests.exceptions.RequestException as e:
return False, f"other request error: {e}"
ok, detail = check_proxy(proxies)
print("OK" if ok else "FAIL", detail)
Wanneer curl werkt maar Python ProxyError gooit
Als dezelfde inloggegevens slagen in curl maar ProxyError in Python veroorzaken, overschrijft een omgevingsvariabele bijna altijd je dict. Requests leest HTTP_PROXY, HTTPS_PROXY en NO_PROXY uit de shell, en een verouderde bedrijfswaarde leidt elke oproep stilletjes om. Print session.proxies om te zien wat er echt wordt gebruikt, en schakel vervolgens de omgevingslookup volledig uit:
import requests
session = requests.Session()
session.trust_env = False # ignore HTTP_PROXY / HTTPS_PROXY from the shell
session.proxies = {
"http": "http://USER:PASS@gate.quantumproxies.io:PORT",
"https": "http://USER:PASS@gate.quantumproxies.io:PORT",
}
print(session.get("https://httpbin.org/ip", timeout=(5, 20)).json())
Een 407 die correcte inloggegevens overleeft wijst op een IP-whitelistplan dat wordt aangeroepen vanaf een niet-geregistreerd adres — de volledige lijst met oorzaken staat in onze 407 Proxy Authentication Required gids.
Intermitterende ProxyError: roteer, herstart niet
Het frustrerende geval is code die twintig minuten draait, dan ProxyError gooit, en dan weer werkt. Dat is geen bug in je script — het is een enkele exit-IP die sterft of midden in de run wordt beperkt. De oplossing is opnieuw proberen met rotatie: omhul de oproep, vang ConnectionError, en laat een roterende gateway je een nieuw IP geven bij de volgende poging. Via roterende proxies reist elke poging via een andere exit, zodat één dood adres nooit een verzoek twee keer kan laten mislukken:
import requests
def get_with_rotation(url, proxies, attempts=4):
last = None
for _ in range(attempts):
try:
r = requests.get(url, proxies=proxies, timeout=(5, 20))
if r.status_code not in (429, 500, 502, 503, 504):
return r
last = r.status_code
except requests.exceptions.ConnectionError as e: # Proxy/SSL/ConnectTimeout
last = e
raise RuntimeError(f"failed after {attempts} attempts: {last}")
Als ProxyError aanhoudt over veel nieuwe IP's, is het probleem verschoven van je pool naar het doel: je wordt geblokkeerd, niet losgekoppeld. Dat is een ander gevecht — zie de anti-ban checklist voor pacing, headers en sessiehygiëne.
Er is nog een onderscheid dat de moeite waard is om te internaliseren, omdat het verandert hoe je reageert. Een ProxyError of ConnectTimeout betekent dat het verzoek nooit is voltooid, dus het opnieuw proberen is veilig, zelfs voor een POST — er is niets aan de andere kant gebeurd. Een ReadTimeout, daarentegen, betekent dat het doel je verzoek heeft ontvangen en gewoon te lang duurde om te antwoorden; het opnieuw proberen van een niet-idempotente schrijf kan dubbel indienen. Wanneer je de retry-lus bouwt, behandel verbindingsfase-fouten als vrij opnieuw probeerbaar en leesfase-fouten als opnieuw probeerbaar alleen voor GET en HEAD. Die ene regel voorkomt de subtiele bug waarbij een wispelturige proxy één checkout in drie verandert.

Veelgestelde vragen
Wat veroorzaakt requests.exceptions.ProxyError: kan geen verbinding maken met proxy?
De proxyhost of -poort is verkeerd, de proxy is uitgeschakeld, of een firewall blokkeert de verbinding voordat een verzoek vertrekt. Verifieer het eindpunt met curl -x met dezelfde inloggegevens; als curl ook faalt, is de proxy onbereikbaar, en als curl slaagt, is een omgevingsvariabele of een verkeerd gevormd dict in je Python de boosdoener.
Waarom is ProxyError ingepakt in HTTPSConnectionPool?
Die wrapper noemt gewoon de verbinding pool die urllib3 gebruikte om het doel te bereiken — het is ruis rond het echte bericht genest binnenin. Lees de binnenste Veroorzaakt door clausule: Kan geen verbinding maken met proxy betekent een verbindingsfout, terwijl een 407 of SSL-bericht binnen dezelfde pool wijst op een authenticatie- of TLS/schema-probleem in plaats daarvan.
Hoe stop ik requests met het lezen van proxies uit de omgeving?
Stel session.trust_env = False in op je Session, of geef trust_env=False gelijkwaardig door, zodat Requests HTTP_PROXY en HTTPS_PROXY negeert. Dit is de oplossing wanneer een proxy in één shell werkt maar ProxyError in een andere gooit, of wanneer een bedrijfsvariabele een scraper kaapt die je niet hebt geconfigureerd om een proxy te gebruiken.
Is ConnectTimeout veilig om opnieuw te proberen?
Ja — de Requests-documentatie markeert ConnectTimeout als veilig om opnieuw te proberen omdat het verzoek nooit de server bereikte, dus er kon geen bijwerking hebben plaatsgevonden. Probeer het opnieuw, idealiter via een roterende gateway zodat de volgende poging een andere, snellere exit gebruikt. ReadTimeout is riskanter om blindelings opnieuw te proberen bij niet-idempotente methoden zoals POST.
Zodra je stopt met het behandelen van ProxyError als een enkele fout en begint het te lezen per levenscyclusfase, zijn de oplossingen mechanisch: schema-typfouten treden op voor het netwerk, verbindingsfouten noemen een dode proxy, SSL-fouten noemen een verkeerde sleutel, en intermitterende fouten willen rotatie, geen herstart. Schone exits verwijderen de meeste volledig.