Szybka odpowiedź
Ile jednoczesnych żądań mogę uruchomić przez proxy rezydencjalne?
Tyle, ile toleruje każda strona docelowa: CrawlRelay nalicza opłaty za ruch residential traffic za GB (0,50 USD/GB od 50 GB), nie za wątki ani połączenia. Dobierz współbieżność zgodnie z prawem Little'a, liczba żądań w locie = strony na sekundę × sekundy na stronę, potem ogranicz ją na hosta, zaczynając od 2 do 4. Zmniejsz, gdy pojawią się odpowiedzi 429 lub 503. Płatność w kryptowalutach, bez KYC.
Współbieżność w skrócie
| Rozliczenia | Za GB ruchu; wątki i połączenia nie są rozliczane |
|---|---|
| Reguła dobierania | Liczba żądań w locie = przepustowość × opóźnienie (prawo Little'a) |
| Punkt startowy na hosta | 2–4 równoległe żądania; 1 z opóźnieniem dla małych stron |
| Sygnały do zwolnienia | 429, 503, rosnące czasy odpowiedzi, strony z wyzwaniami |
| Matematyka pasma | Strony na sekundę × bajty na stronę × 3600 = bajty na godzinę |
| Średni czas odpowiedzi residential proxy | 0,93 s w sieci proxy, 3,21 s na rzeczywistych celach (Proxyway 2026) |
Jak dobrać współbieżność na podstawie potrzebnej przepustowości?
Użyj prawa Little'a: liczba żądań w locie to mnożnik tempa przez czas realizacji każdego żądania. Badanie Proxyway Proxy Market Research 2026 zmierzyło medianę 3,21 sekund na żądanie na rzeczywistych celach przez proxy rezydencjalne, więc planuj 2 do 4 sekund dla prawdziwych stron, a nie tylko ułamka sekundy sieci.
| Cel | Czas na żądanie | Żądania w locie |
|---|---|---|
| 5 stron na sekundę | 2 s | 10 |
| 20 stron na sekundę | 2 s | 40 |
| 20 stron na sekundę | 4 s | 80 |
| 100 stron na sekundę | 3 s | 300 |
Potem rozłóż te żądania na hosty. Czterdzieści żądań w locie przez 200 stron to delikatne obciążenie; czterdzieści żądań na jednej małej stronie to test obciążeniowy, na który nigdy się nie zgodziła.
Ile pasma zużywa taka przepustowość?
Pomnóż strony na sekundę przez bajty na stronę. Przy 20 stronach HTML na sekundę i 50 KB na stronę, crawl zużywa 1 MB na sekundę, co daje 3,6 GB na godzinę i około 86 GB na dzień. Przy 2 MB na wyrenderowaną stronę, ten sam ruch generuje 40 MB na sekundę.
Pomnóż wynik przez cenę za GB w swoim planie, 0,50 USD/GB od 50 GB po CrawlRelay, aby poznać koszt na godzinę. Przewodnik po kalkulatorze pasma podaje typowe wagi stron według typu.
Jakie limity na hosta powinienem zacząć stosować?
| Cel | Równoległe żądania na hosta | Opóźnienie między żądaniami | Uwagi |
|---|---|---|---|
| Mała strona (blog, lokalny sklep) | 1 | 2–5 s | Szanuj Crawl-delay w robots.txt, jeśli występuje |
| Średnia strona | 2–4 | 0,5–2 s | Monitoruj czasy odpowiedzi; zwalniaj, gdy się podwajają |
| Duża platforma z CDN | 4–16 | AutoThrottle lub adaptacyjne | Zwiększaj tylko jeśli współczynniki 429 i 5xx pozostają stabilne |
| Publiczne API | Udokumentowany limit | Jak w dokumentacji | Używaj klucza API i limitów, które dostawca Ci przydziela |
Jak automatycznie dostosować współbieżność?
Stosuj zasadę additive increase i multiplicative decrease, jak w TCP przy przeciążeniach: dodawaj jedno miejsce po serii poprawnych odpowiedzi, dziel limit na pół przy 429 lub 503. Poniższy limiter utrzymuje jeden limit na host, wstrzymuje każde żądanie do tego hosta na Retry-After opóźnienia, wykonuje jedno ponowienie i nigdy nie przekracza ustawionego maksimum. Błędy sieci zwracane są jako wartości, więc jedno przekroczenie czasu nie zatrzyma całej partii.
import asyncio
import random
from urllib.parse import urlsplit
import httpx
PROXY = "http://USERNAME-country-us:[email protected]:7777"
class AdaptiveLimit:
"""One per host: +1 slot after 20 clean responses, halve on 429/503, pause for Retry-After."""
def __init__(self, start=2, low=1, high=16):
self.limit, self.low, self.high = start, low, high
self.in_flight, self.clean = 0, 0
self.pause_until = 0.0
self.cond = asyncio.Condition()
async def __aenter__(self):
loop = asyncio.get_running_loop()
while True:
async with self.cond:
await self.cond.wait_for(lambda: self.in_flight < self.limit)
delay = self.pause_until - loop.time()
if delay <= 0:
self.in_flight += 1
return self
await asyncio.sleep(delay) # the host asked for a pause: wait outside the lock
async def __aexit__(self, *exc):
async with self.cond:
self.in_flight -= 1
self.cond.notify_all()
def record(self, status, retry_after=None):
if status in (429, 503):
self.limit, self.clean = max(self.low, self.limit // 2), 0
wait = int(retry_after) if retry_after and retry_after.isdigit() else 5 + random.random()
now = asyncio.get_running_loop().time()
self.pause_until = max(self.pause_until, now + wait)
elif status < 400:
self.clean += 1
if self.clean >= 20 and self.limit < self.high:
self.limit, self.clean = self.limit + 1, 0
gates = {}
def gate_for(url):
host = urlsplit(url).hostname
if host not in gates:
gates[host] = AdaptiveLimit()
return gates[host]
async def fetch(client, url, attempts=2):
gate = gate_for(url)
status = None
for _ in range(attempts):
try:
async with gate:
r = await client.get(url)
except httpx.HTTPError as exc:
return "error: %s" % type(exc).__name__
status = r.status_code
gate.record(status, r.headers.get("Retry-After"))
if status not in (429, 503):
break # the retry, if any, waits for the host's pause in __aenter__
return status
async def main(urls):
timeout = httpx.Timeout(30.0, connect=10.0)
async with httpx.AsyncClient(proxy=PROXY, timeout=timeout) as client:
print(await asyncio.gather(*(fetch(client, u) for u in urls)))
print({host: gate.limit for host, gate in gates.items()})
asyncio.run(main(["https://example.com/"] * 10))Co spowalnia crawl przez proxy oprócz celu?
- Negocjacje TLS. Nowe połączenie na żądanie, potrzebne dla nowego IP za każdym razem, oznacza handshake z serwerem przy każdej próbie. Utrzymuj sticky session z keep-alive dla wielu stron jednego serwisu, gdy różnorodność IP nie jest ważna.
- Wolne wyjścia. Proxy rezydencjalne i mobilne proxy 4G to prawdziwe domowe lub komórkowe łącza. Używaj timeoutów i ponawiaj próbę przez nowe połączenie zamiast czekać bez końca.
- Wąskie targetowanie. Małe miasto lub pojedynczy ASN mają mniej wyjść niż kraj. Celuj wąsko tylko tam, gdzie dane się zmieniają.
- Renderowanie. Headless browser zużywa znacznie więcej CPU, pamięci i transferu niż klient HTTP. Renderuj tylko strony wymagające JavaScript.
- DNS. Przy
socks5h://lub porcie HTTP rozstrzyganie DNS odbywa się po stronie proxy; nie potrzebujesz lokalnego cache resolvera dla celów.
Pytania o współbieżność
Czy CrawlRelay ogranicza liczbę wątków lub połączeń?
Plany są rozliczane za GB transferu, nie za wątki, połączenia czy żądania; podane plany nie definiują limitu wątków. Praktyczne limity zależą od tolerancji celu i możliwości Twojego komputera. Jeśli planujesz bardzo duży crawl, skontaktuj się z [email protected] najpierw, abyśmy mogli sprawdzić zasięg twojego targetowania.
Dlaczego crawl zwolnił po dodaniu więcej wątków?
Zazwyczaj dlatego, że cel zaczął throttling: rosną czasy odpowiedzi, pojawiają się kody 429 lub 503, a ponowienia powodują dodatkowe obciążenie. Inny powód to wyczerpanie CPU lub gniazd sieciowych na Twojej maszynie, szczególnie przy headless browserach. Mierz opóźnienia i błędy na host zanim dodasz więcej wątków.
Czy jedno połączenie na żądanie jest wolniejsze niż keep-alive?
Tak, każde żądanie oznacza jeden handshake TCP i TLS, zwykle kilkaset milisekund przez proxy rezydencjalne. To koszt posiadania nowego IP przy każdym żądaniu. Przy wielu stronach tego samego serwisu, gdzie IP nie ma znaczenia, sticky session z keep-alive jest szybsza i oszczędza transfer.
Jak respektować robots.txt crawl-delay w crawlerze współbieżnym?
Parsuj robots.txt raz na host, cachuj do 24 godzin według RFC 9309 i zamień wartość Crawl-delay na minimalną przerwę między żądaniami na hosta przy współbieżności 1. Pythonowe urllib.robotparser udostępnia to przez crawl_delay().
Skaluj na podstawie ceny za GB
0,50 USD/GB od 50 GB do 1 TB, transfer bez daty wygaśnięcia.