Szybka odpowiedź
Jak korzystać z rotujących proxy rezydencjalnych w Pythonie?
Podaj http://USERNAME:[email protected]:7777 jako proxy zarówno dla http jak i https w requests, jako proxy= w httpx 0.26+, lub jako proxy= na każde żądanie w aiohttp. CrawlRelay proxy rezydencjalne rotują przy każdym nowym połączeniu i pozostają trwałe dzięki -session-<id> w nazwie użytkownika. Kosztują 1,50 USD za 1 GB i 0,50 USD/GB od 50 GB. Płatność w kryptowalutach, bez KYC.
Szybki przegląd konfiguracji Python
| requests | proxies={"http": url, "https": url}; ponowne próby przez urllib3 Retry |
|---|---|
| httpx | httpx.Client(proxy=url); stary argument proxies= usunięty w httpx 0.28 |
| aiohttp | session.get(url, proxy="http://USER:PASS@host:port"), dane uwierzytelniające w URL proxy |
| SOCKS5 | pip install "requests[socks]" lub httpx[socks], potem socks5h:// na porcie 7778 |
| Nowe IP przy każdym żądaniu | Wyłącz keep-alive: Connection: close, max_keepalive_connections=0 lub force_close=True |
| To samo IP dla całego przepływu | -session-<id>-ttl-<minutes> w nazwie użytkownika, do 30 minut |
| Schemat URL proxy | Zawsze http:// dla portu HTTP, nawet na stronach HTTPS |
Jak używać proxy z requests z ponownymi próbami uwzględniającymi Retry-After?
Zainstaluj HTTPAdapter z retry urllib3 Retry na Session. Próbuje ponownie przy błędach połączenia i podanych statusach, czeka z wykładniczym opóźnieniem i respektuje nagłówek Retry-After wysyłany przez serwery przy 429 i 503. Ustaw trust_env = False, by zmienna HTTPS_PROXY nie nadpisywała twojego proxy.
import os
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
USER = os.environ.get("CRAWLRELAY_USER", "USERNAME-country-us")
PASSWORD = os.environ.get("CRAWLRELAY_PASSWORD", "PASSWORD")
PROXY = "http://%s:%[email protected]:7777" % (USER, PASSWORD)
retry = Retry(
total=4,
connect=2,
backoff_factor=1.0, # waits of 0 s, 2 s, 4 s, 8 s (Retry-After wins on 429/503)
status_forcelist=(429, 502, 503, 504),
allowed_methods=frozenset({"GET", "HEAD"}),
respect_retry_after_header=True,
)
session = requests.Session()
session.trust_env = False # ignore HTTP(S)_PROXY from the environment
session.mount("http://", HTTPAdapter(max_retries=retry))
session.mount("https://", HTTPAdapter(max_retries=retry))
session.proxies = {"http": PROXY, "https": PROXY}
session.headers["User-Agent"] = "ExampleCrawler/1.0 (+https://example.com/crawler-info)"
session.headers["Connection"] = "close" # new connection per request, so usually a new exit IP
r = session.get("https://api.ipify.org", timeout=(10, 30))
print(r.status_code, r.text)Jak bezpiecznie zbudować docelowe URL proxy?
Generuj nazwę użytkownika w jednej funkcji i ją waliduj. Literówka w kodzie kraju lub znak niezakodowany w haśle powodują błąd 407 wyglądający jak problem z uwierzytelnianiem. Poniższy helper zachowuje udokumentowany porządek (USERNAME-country-{cc}-state-{state}-city-{city}-zip-{zip}-asn-{asn}-session-{id}-ttl-{minutes}), odrzuca nieznane klucze i koduje URL obu danych logowania.
import re
from urllib.parse import quote
ORDER = ("country", "state", "city", "zip", "asn", "session", "ttl")
VALUE = re.compile(r"^[a-z0-9_]+$")
def build_username(base, **params):
"""build_username("USERNAME", country="us", city="new_york", session="job1", ttl=30)"""
parts = [base]
for key in ORDER:
value = params.pop(key, None)
if value is None:
continue
value = str(value).lower()
if not VALUE.match(value):
raise ValueError("bad value for %s: %r" % (key, value))
if key == "ttl" and not 1 <= int(value) <= 30:
raise ValueError("ttl must be 1 to 30 minutes")
parts.append("%s-%s" % (key, value))
if params:
raise ValueError("unknown parameters: %s" % ", ".join(sorted(params)))
return "-".join(parts)
def proxy_url(username, password, socks=False):
scheme, port = ("socks5h", 7778) if socks else ("http", 7777)
return "%s://%s:%[email protected]:%d" % (
scheme, quote(username, safe=""), quote(password, safe=""), port)
print(proxy_url(build_username("USERNAME", country="us", state="ny", city="new_york", session="job1", ttl=20), "p@ss:word"))Jak uzyskać nowe IP przy każdym żądaniu w httpx?
Wyłącz keep-alive. httpx domyślnie korzysta z puli połączeń, a ponowne ich użycie utrzymuje to samo IP wyjściowe. Z max_keepalive_connections=0 każde żądanie otwiera nowe połączenie do bramki i zwykle wychodzi innym mobile proxy 4G. num_bytes_downloaded pokazuje liczbę bajtów odebranych na łączu, co najbliżej odpowiada rozliczeniu transferu.
import httpx
PROXY = "http://USERNAME-country-gb:[email protected]:7777"
# No keep-alive: every request opens a new connection, so it usually gets a new exit IP
with httpx.Client(
proxy=PROXY,
timeout=httpx.Timeout(30.0, connect=10.0),
limits=httpx.Limits(max_keepalive_connections=0),
follow_redirects=True,
) as client:
for _ in range(3):
r = client.get("https://api.ipify.org")
print(r.text, r.num_bytes_downloaded, "bytes on the wire")Jak równolegle robić scraping w asyncio, nie przeciążając jednej strony?
Ogranicz konkurencję per host, nie tylko globalnie. Globalny limit 50 sprawi, że wszystkie 50 żądań uderzy w ten sam serwis, gdy lista URL będzie posortowana po domenie. Semafor na hostname utrzymuje poziom grzeczności wobec każdego serwisu, a całość crawl pozostaje szybka. Zacznij od 2 do 4 na host i przeczytaj przewodnik po konkurencji zanim zwiększysz limit.
import asyncio
from collections import defaultdict
from urllib.parse import urlsplit
import httpx
PROXY = "http://USERNAME-country-us:[email protected]:7777"
PER_HOST = 4 # parallel requests per target host
host_slots = defaultdict(lambda: asyncio.Semaphore(PER_HOST))
async def fetch(client, url):
async with host_slots[urlsplit(url).hostname]:
try:
r = await client.get(url)
return url, r.status_code, r.num_bytes_downloaded
except httpx.HTTPError as exc:
return url, type(exc).__name__, 0
async def main(urls):
limits = httpx.Limits(max_connections=50, max_keepalive_connections=0)
timeout = httpx.Timeout(30.0, connect=10.0)
async with httpx.AsyncClient(proxy=PROXY, limits=limits, timeout=timeout) as client:
for result in await asyncio.gather(*(fetch(client, u) for u in urls)):
print(*result)
asyncio.run(main(["https://example.com/", "https://example.org/", "https://example.net/"]))Jak korzystać z proxy w aiohttp?
Podaj bramkę jako proxy= dla każdego żądania, z danymi uwierzytelniającymi w URL proxy. Działa to na każdej wersji aiohttp 3.x bez ostrzeżeń; BasicAuth i parametr proxy_auth= są przestarzałe od aiohttp 3.14 i zostaną usunięte w wersji 4.0. TCPConnector(force_close=True) zamyka połączenie po odpowiedzi, więc każde żądanie ma zwykle nowe wyjście. Bez tego aiohttp ponownie wykorzystuje połączenia, utrzymując to samo IP na jakiś czas.
import asyncio
import aiohttp
# Credentials go in the proxy URL; percent-encode a password that contains @ or :
PROXY = "http://USERNAME-country-fr:[email protected]:7777"
async def main():
# force_close=True: one connection per request, so each request usually gets a new exit IP
connector = aiohttp.TCPConnector(limit=20, force_close=True)
timeout = aiohttp.ClientTimeout(total=60, sock_connect=10)
async with aiohttp.ClientSession(connector=connector, timeout=timeout) as session:
for _ in range(3):
async with session.get("https://api.ipify.org", proxy=PROXY) as resp:
print(resp.status, await resp.text())
asyncio.run(main())Jak utrzymać jedno IP w wieloetapowym przepływie?
Wygeneruj jedną sesję na przepływ i umieść ją w nazwie użytkownika z TTL dłuższym niż przepływ. Keep-alive jest ok, chcesz to samo wyjście. Jeśli urządzenie proxy rezydencjalne przestanie działać, IP może się zmienić przed końcem TTL, więc zrób przepływ możliwy do wznowienia od ostatniego punktu kontrolnego.
import uuid
import requests
session_id = uuid.uuid4().hex[:10] # one ID per flow, lowercase letters and digits
user = "USERNAME-country-us-state-ca-session-%s-ttl-20" % session_id
proxy = "http://%s:[email protected]:7777" % user
with requests.Session() as s:
s.proxies = {"http": proxy, "https": proxy}
first = s.get("https://api.ipify.org", timeout=(10, 30)).text
second = s.get("https://api.ipify.org", timeout=(10, 30)).text
print(first, second, "same exit" if first == second else "exit changed")Ile ruchu generuje scraper w Pythonie na stronę?
Zmierz na 100 prawdziwych stron zanim kupisz transfer. Loguj num_bytes_downloaded w httpx lub długość skompresowanego ciała w własnym middleware i pomnóż przez liczbę stron. Żądania tylko HTML zwykle kosztują dziesiątki kilobajtów na stronę; przewodnik po transferze podaje liczby i koszt na 1000 stron przy cenach CrawlRelay.
Trzy zwyczaje zmniejszają ruch bez utraty efektów:
- Wysyłaj
Accept-Encoding: gzip, br(httpx i requests domyślnie wysyłają gzip), by strony płynęły skompresowane. - Pobieraj punkt JSON, który strona ładuje, jeśli dokumentacja lub regulamin na to pozwala, zamiast całego HTML.
- Pomijaj niezmienione strony z
If-None-MatchalboIf-Modified-Since:304 Not Modifiedkosztuje tylko kilkaset bajtów.
Python i proxy: pytania
requests, httpx czy aiohttp: którego użyć do scrapingu?
Używaj requests do prostych skryptów i małych crawlów. httpx sprawdzi się, gdy chcesz tę samą API w kodzie synchronicznym i asynchronicznym oraz HTTP/2 przez httpx[http2]. aiohttp to wybór przy bardzo dużej współbieżności w czystym asyncio. Przy crawl z harmonogramem, deduplikacją i pipeline'ami lepiej użyć Scrapy zamiast pisać te elementy samodzielnie.
Dlaczego dostaję błąd SSL: WRONG_VERSION_NUMBER z moim proxy w Pythonie?
Prawdopodobnie wpisałeś https:// przed adresem proxy. Port bramki 7777 obsługuje zwykły HTTP i otwiera tunel CONNECT dla stron HTTPS, więc w URL proxy musi być http:// w kluczach http i https. Zobacz stronę błędu.
Jak używać mobilne proxy 4G SOCKS5 w Pythonie?
Zainstaluj dodatek (pip install "requests[socks]" lub pip install "httpx[socks]"), potem użyj socks5h://USERNAME:[email protected]:7778. h wysyła nazwy hostów do proxy, więc rozwiązywanie DNS odbywa się na wyjściu i widzisz te same odpowiedzi regionalne, co lokalni użytkownicy.
Dlaczego mój obracający się proxy rezydencjalne zwraca ten sam IP dwa razy?
Klient ponownie użył połączenia keep-alive, a rotacja odbywa się na nowe połączenie. W requests wyślij Connection: close, ustaw max_keepalive_connections=0 w httpx lub użyj force_close=True w aiohttp. Sprawdź też, czy nazwa użytkownika nie zawiera session-, który celowo utrzymuje jeden IP.
Jak ustawić timeouty dla zapytań przez proxy?
Ustaw je jawnie: requests domyślnie timeouta nie ma. Timeout na połączenie około 10 sekund i odczyt 30 sekund pasują do stron HTML; wyjścia mobilne i wolne cele potrzebują 45-60 sekund. Po timeout spróbuj ponownie na nowym połączeniu, zamiast czekać dłużej. Zobacz read timeouts.
Czy mogę używać tych proxy w Jupyter lub z pandas?
Tak. Wszystko, co korzysta z requests lub httpx, przyjmie te same ustawienia proxy. Dla pandas.read_html lub read_csv w URL pobierz najpierw zawartość przez proxied requests, potem przekaż tekst lub bajty do pandas.
Uruchom swój skraper Python przez proxy rezydencjalne
1,50 USD za 1 GB, 0,50 USD/GB od 50 GB.