Geonode logo
Geonode Team

Geonode Team

Dikemas kini: 16 Oktober 2026

Diterbitkan: 16 Oktober 2026

Pengaturan Proxy aiohttp: Sesi, Otentikasi, dan Batasan Koneksi

aiohttp adalah klien yang Anda pilih ketika sebuah scraper membutuhkan ratusan permintaan yang sedang diproses dan Anda ingin memiliki kendali penuh atas event loop. Mengarahkan permintaan melalui proxy hanya memerlukan beberapa argumen kata kunci, tetapi pengaturan default terkait otentikasi, batas koneksi, dan batas waktu berbeda dari `requests` dan httpx, dan di situlah sebagian besar konfigurasi proxy aiohttp sering mengalami masalah. Pada akhir panduan ini, Anda akan memiliki `ClientSession` yang melakukan otentikasi terhadap gateway residensial yang berganti-ganti, menjalankan permintaan paralel yang dibatasi, mengalami timeout yang dapat diprediksi, dan melaporkan kesalahan proxy berdasarkan jenisnya. Contoh-contoh ini ditujukan untuk aiohttp 3.14 pada Python 3.11 atau yang lebih baru. Jika ada perubahan perilaku pada rilis terbaru, versi tersebut akan disebutkan secara khusus.

aiohttp adalah klien yang Anda pilih ketika sebuah scraper membutuhkan ratusan permintaan yang sedang diproses dan Anda ingin memiliki kendali penuh atas event loop. Mengarahkan permintaan melalui proxy hanya memerlukan beberapa argumen kata kunci, tetapi pengaturan default terkait otentikasi, batas koneksi, dan batas waktu berbeda dari requests dan httpx, dan di situlah sebagian besar konfigurasi proxy aiohttp sering mengalami masalah. Pada akhir panduan ini, Anda akan memiliki ClientSession yang melakukan otentikasi terhadap gateway residensial yang berganti-ganti, menjalankan permintaan paralel yang dibatasi, mengalami timeout yang dapat diprediksi, dan melaporkan kesalahan proxy berdasarkan jenisnya.

Contoh-contoh ini ditujukan untuk aiohttp 3.14 pada Python 3.11 atau yang lebih baru. Jika ada perubahan perilaku pada rilis terbaru, versi tersebut akan disebutkan secara khusus.

Mengonfigurasi proxy aiohttp pada sesi

Kata kunci proxy dapat digunakan baik per permintaan maupun pada tingkat sesi. Penggunaan per permintaan sesuai dengan API asli dan masih berfungsi; sedangkan pengaturan pada tingkat sesi adalah yang Anda butuhkan untuk scraper, karena hal ini menghilangkan kemungkinan adanya satu panggilan yang lolos dari proxy.

import asyncio
import aiohttp

PROXY = "http://USERNAME:PASSWORD@premium-residential.geonode.com:9000"

async def main() -> None:
    async with aiohttp.ClientSession(proxy=PROXY) as session:
        async with session.get("https://api.ipify.org?format=json") as resp:
            print(resp.status, await resp.json())

asyncio.run(main())

Port 9000 adalah gerbang HTTP(S) bergilir dari Geonode, sehingga setiap koneksi baru keluar dari alamat IP perumahan yang berbeda. Panggilan individual masih dapat mengganti pengaturan default dengan argumen proxy=-nya sendiri, yang berguna ketika satu permintaan harus dikirim secara langsung, misalnya ke titik akhir metrik internal.

Bentuk per-permintaan

async with aiohttp.ClientSession() as session:
    async with session.get("https://api.ipify.org?format=json", proxy=PROXY) as resp:
        print(await resp.json())

Gunakan ini ketika proxy bervariasi dari satu panggilan ke panggilan lainnya, seperti ketika manajer pool membagikan sesi sticky yang berbeda untuk setiap tugas.

Otentikasi: Kredensial URL atau proxy_headers

Ada dua cara yang didukung untuk mengirim kredensial proxy, serta satu cara yang sudah tidak direkomendasikan lagi namun mungkin masih Anda temui di tutorial-tutorial lama.

MetodeContohStatus
Kredensial dalam URL proxyproxy="http://user:pass@host:9000"Didukung
proxy_headers dengan encode_basic_authproxy_headers={"Proxy-Authorization": aiohttp.encode_basic_auth("user", "pass")}Didukung, direkomendasikan
proxy_auth=aiohttp.BasicAuth("user", "pass")Tidak direkomendasikan sejak versi 3.14, dihapus pada versi 4.0

Bentuk URL adalah yang paling singkat. Bentuk header menyembunyikan rahasia dari URL, yang penting jika URL proxy muncul dalam log atau pesan pengecualian. aiohttp.encode_basic_auth mengembalikan string Basic ... yang sudah jadi:

import asyncio
import os
import aiohttp

PROXY = "http://premium-residential.geonode.com:9000"

async def main() -> None:
    proxy_headers = {
        "Proxy-Authorization": aiohttp.encode_basic_auth(
            os.environ["GEONODE_USER"], os.environ["GEONODE_PASS"]
        )
    }
    async with aiohttp.ClientSession(proxy=PROXY, proxy_headers=proxy_headers) as session:
        async with session.get("https://api.ipify.org?format=json") as resp:
            print(await resp.json())

asyncio.run(main())

Jika basis kode Anda masih menggunakan proxy_auth=aiohttp.BasicAuth(...), fitur ini tetap berfungsi pada versi 3.14 dengan DeprecationWarning. Lakukan migrasi sekarang: parameter dan kelas tersebut dihapus pada aiohttp 4.0, dan penggantinya hanya memerlukan perubahan satu baris kode.

Permintaan paralel dengan asyncio.gather dan Semaphore

Alasan menggunakan aiohttp adalah koncurrency, dan alasan koncurrency dapat menyebabkan proxy gagal adalah karena secara default tidak ada batasan yang mengaturnya. asyncio.gather dengan lebih dari seribu coroutine akan mencoba membuka seribu koneksi; konektor membatasi jumlah tersebut hingga 100, dan sisanya masuk ke antrian di dalam aiohttp, yang memang lebih baik daripada tidak ada sama sekali, tetapi tidak memberi Anda kendali atas pengaturan kecepatan. Semaphore membuat batasan tersebut eksplisit dan memungkinkan Anda menyesuaikannya secara independen dari konektor.

import asyncio
import aiohttp

PROXY = "http://USERNAME:PASSWORD@premium-residential.geonode.com:9000"
CONCURRENCY = 20

async def fetch(session: aiohttp.ClientSession, sem: asyncio.Semaphore, url: str) -> tuple[str, int]:
    async with sem:
        async with session.get(url) as resp:
            await resp.read()
            return url, resp.status

async def main() -> None:
    urls = [f"https://httpbin.org/get?i={i}" for i in range(100)]
    sem = asyncio.Semaphore(CONCURRENCY)
    connector = aiohttp.TCPConnector(limit=CONCURRENCY, ttl_dns_cache=300)
    timeout = aiohttp.ClientTimeout(total=30, connect=10, sock_read=20)
    async with aiohttp.ClientSession(
        proxy=PROXY, connector=connector, timeout=timeout
    ) as session:
        results = await asyncio.gather(
            *(fetch(session, sem, u) for u in urls), return_exceptions=True
        )
    ok = sum(1 for r in results if not isinstance(r, Exception))
    print(f"{ok}/{len(urls)} succeeded")

asyncio.run(main())

return_exceptions=True mencegah satu permintaan yang gagal membatalkan seluruh batch. Sesuaikan semafor dengan batas konektor sehingga tidak ada pihak yang secara diam-diam mengantrekan pekerjaan yang tidak dapat diproses oleh pihak lain.

TCPConnector: limit dan limit_per_host

Konektor mengelola kumpulan soket. Ada dua parameter yang penting untuk lalu lintas proxy aiohttp.

limit adalah jumlah total koneksi simultan, dengan nilai default 100. limit_per_host membatasi koneksi ke satu kelompok (host, port, is_ssl), dengan nilai default 0 yang berarti tidak terbatas. Melalui proxy, setiap permintaan ke setiap target berbagi host dan port proxy, sehingga batasan per-host berlaku untuk gateway, bukan situs tujuan. Untuk beban kerja proxy murni, atur limit sesuai dengan jumlah koneksi simultan yang diinginkan dan biarkan limit_per_host pada 0; atur limit_per_host hanya jika sesi yang sama juga berkomunikasi langsung dengan server asal dan Anda ingin melindunginya.

connector = aiohttp.TCPConnector(limit=30, limit_per_host=10, ttl_dns_cache=300)

ttl_dns_cache=300 menyimpan jawaban DNS gateway selama lima menit, bukan sepuluh detik seperti default, yang menghilangkan satu putaran resolver dari sebagian besar koneksi baru. Sesi mengambil alih kepemilikan konektor dan menutupnya saat keluar; tetapkan connector_owner=False hanya jika Anda sengaja berbagi satu konektor di antara sesi-sesi.

ClientTimeout sebagai pengganti waktu tunggu default 5 menit

Waktu tunggu total default aiohttp adalah 300 detik. Angka ini cukup longgar untuk permintaan langsung, namun terlalu lama untuk lalu lintas proxy, di mana koneksi keluar dari jaringan perumahan yang macet seharusnya segera gagal sehingga Anda dapat mencoba lagi menggunakan alamat IP lain.

timeout = aiohttp.ClientTimeout(total=30, connect=10, sock_read=20)

total membatasi keseluruhan permintaan termasuk pengalihan (redirect) dan pengunduhan isi (body). connect membatasi waktu untuk memperoleh koneksi dari pool ditambah proses handshake TCP dan proxy. sock_read membatasi jeda antara byte yang berurutan, yang mendeteksi target yang menerima koneksi lalu mengirimkan data secara bertahap. Serahkan objek ini ke sesi untuk menggunakan nilai default, atau ke permintaan tunggal untuk menggantikannya. Kedaluwarsa memicu asyncio.TimeoutError, yang dibahas di bawah ini.

trust_env dan proxy lingkungan

Berbeda dengan requests, aiohttp mengabaikan HTTP_PROXY, HTTPS_PROXY, dan NO_PROXY kecuali Anda mengaktifkannya dengan trust_env=True. Jika diaktifkan, aiohttp akan menggunakan urllib.request.getproxies(), menghormati no_proxy, dan juga membaca kredensial proxy dari ~/.netrc jika ada.

async with aiohttp.ClientSession(trust_env=True) as session:
    async with session.get("https://api.ipify.org?format=json") as resp:
        print(await resp.json())

Untuk scraper yang harus selalu menggunakan gateway tertentu, biarkan trust_env dinonaktifkan dan atur proxy= secara eksplisit. Dengan demikian, proxy perusahaan pada CI runner tidak dapat membajak perutean, dan no_proxy tidak dapat secara diam-diam mengecualikan suatu target.

SOCKS5 dengan aiohttp-socks

aiohttp tidak memiliki dukungan SOCKS bawaan, sehingga proxy aiohttp melalui SOCKS5 memerlukan konektor pihak ketiga. Paket aiohttp-socks menyediakan ProxyConnector yang menggantikan TCPConnector dan menangani proses handshake SOCKS. Instal dengan pip install aiohttp-socks. Port bergilir SOCKS5 dari Geonode adalah 11000:

import asyncio
import aiohttp
from aiohttp_socks import ProxyConnector

SOCKS_URL = "socks5://USERNAME:PASSWORD@premium-residential.geonode.com:11000"

async def main() -> None:
    connector = ProxyConnector.from_url(SOCKS_URL)
    async with aiohttp.ClientSession(connector=connector) as session:
        async with session.get("https://api.ipify.org?format=json") as resp:
            print(await resp.json())

asyncio.run(main())

Jangan menggabungkan ProxyConnector dengan proxy= pada sesi; konektor sudah merutekan semuanya. ProxyConnector menerima kata kunci limit dan limit_per_host yang sama seperti TCPConnector.

Keterbatasan TLS-in-TLS

aiohttp berkomunikasi dengan proxy HTTP dan meneruskan permintaan ke target https:// melalui proxy tersebut menggunakan perintah CONNECT. Namun, yang tidak dapat dilakukannya secara andal adalah terhubung ke proxy itu sendiri melalui TLS, yaitu URL proxy https://, karena TLS-in-TLS bergantung pada dukungan transportasi asyncio yang bervariasi di antara rilis Python. Dokumentasi secara eksplisit menyatakan bahwa hal ini tidak boleh diasumsikan. Dalam praktiknya, hal ini tidak menjadi masalah bagi Geonode: gateway dapat diakses melalui HTTP biasa di port 9000/10000 dan lalu lintas https:// Anda tetap dienkripsi dari ujung ke ujung di dalam terowongan CONNECT, jadi pertahankan skema URL proxy sebagai http://.

Sesi tetap untuk alur multi-langkah

Port yang berganti-ganti memberikan alamat IP baru untuk setiap koneksi. Untuk alur masuk atau daftar berhalaman, Anda memerlukan alamat IP yang sama selama beberapa menit, yang disediakan oleh Geonode pada port tetap HTTP(S) 10000 melalui nama pengguna:

import secrets
import aiohttp

def sticky_proxy(country: str = "us", lifetime: int = 600) -> str:
    session_id = secrets.token_hex(4)  # 8 alphanumeric characters
    user = f"USERNAME-country-{country}-session-{session_id}-lifetime-{lifetime}"
    return f"http://{user}:PASSWORD@premium-residential.geonode.com:10000"

session = aiohttp.ClientSession(proxy=sticky_proxy())

Masa berlaku berkisar antara 180 hingga 86.400 detik. Penargetan berdasarkan negara bagian dan kota tidak dapat digabungkan, dan asn memerlukan penargetan berdasarkan negara. Cara untuk melakukan rotasi sesuai permintaan adalah dengan membuat ID sesi baru.

Diagnostik: arti dari masing-masing pengecualian

PengecualianArtiSolusi umum
aiohttp.ClientProxyConnectionErrorTidak dapat membuka koneksi TCP ke host proxyPeriksa nama host, port, dan firewall; tingkatkan batas waktu connect
aiohttp.ClientHttpProxyError dengan .status == 407Proksi menolak kredensial selama CONNECTPerbaiki nama pengguna atau kata sandi; periksa tata bahasa opsi nama pengguna
aiohttp.ClientHttpProxyError dengan 502/503Gateway tidak dapat membuat terowonganCoba lagi dengan ID sesi baru; pastikan port sesuai dengan protokol
asyncio.TimeoutErrorBidang ClientTimeout telah kedaluwarsaCoba lagi dengan alamat IP baru; sesuaikan sock_read
aiohttp.ClientConnectorErrorKegagalan koneksi umum (DNS, ditolak)Periksa .os_error

ClientProxyConnectionError merupakan subkelas dari ClientConnectorError, dan ClientHttpProxyError merupakan subkelas dari ClientResponseError, jadi tangani yang spesifik terlebih dahulu.

import asyncio
import aiohttp

PROXY = "http://USERNAME:PASSWORD@premium-residential.geonode.com:9000"

async def fetch_ip(session: aiohttp.ClientSession) -> str | None:
    try:
        async with session.get("https://api.ipify.org?format=json") as resp:
            resp.raise_for_status()
            return (await resp.json())["ip"]
    except aiohttp.ClientHttpProxyError as e:
        if e.status == 407:
            print("proxy auth failed: check credentials")
        else:
            print("proxy returned", e.status)
    except aiohttp.ClientProxyConnectionError as e:
        print("cannot reach proxy:", e)
    except asyncio.TimeoutError:
        print("timed out through proxy")
    return None

async def main() -> None:
    timeout = aiohttp.ClientTimeout(total=30, connect=10, sock_read=20)
    async with aiohttp.ClientSession(proxy=PROXY, timeout=timeout) as session:
        print(await fetch_ip(session))
    await asyncio.sleep(0.25)  # let SSL transports close before the loop stops

asyncio.run(main())

Perhatikan bahwa untuk target http:// biasa, kode status 407 dari proxy muncul sebagai respons biasa, bukan sebagai pengecualian; oleh karena itu, periksa juga resp.status di sana.

Penutupan yang Lancar

Saat sebuah sesi ditutup, asyncio mungkin masih dalam proses membongkar koneksi SSL yang mendasarinya. Jika loop peristiwa berhenti lebih dulu, Anda akan mendapatkan kode kesalahan ResourceWarning: unclosed transport. Solusi yang didokumentasikan adalah jeda singkat setelah blok async with sesi: await asyncio.sleep(0) untuk sesi HTTP biasa, dan await asyncio.sleep(0.25) ketika sesi tersebut melakukan permintaan HTTPS, yang melalui terowongan CONNECT hampir selalu terjadi. Contoh di atas sudah mencakup hal tersebut.

Kesalahan umum

Membuat sesi baru untuk setiap permintaan

ClientSession mengelola kumpulan koneksi dan penyimpanan cookie. Membuka sesi baru untuk setiap permintaan akan menggagalkan fitur keep-alive ke gateway, melipatgandakan proses handshake proxy, dan memicu peringatan penutupan pada setiap panggilan. Buatlah satu sesi untuk setiap worker dan gunakan sesi tersebut secara bergantian.

Melupakan bahwa proxy adalah satu-satunya "host"

Jika limit_per_host diatur ke angka yang rendah, setiap permintaan yang diproksi akan dihitung terhadap entri (host, port) tunggal di gateway, sehingga batasan 5 per host akan membatasi seluruh scraper Anda menjadi 5 koneksi. Atur limit sebagai gantinya, atau biarkan limit_per_host tetap di 0.

Mengandalkan batas waktu default

Lima menit cukup lama sehingga koneksi keluar perumahan yang macet akan menahan slot semafor selama seluruh jendela waktu tersebut. Selalu sertakan nilai ClientTimeout bersama dengan nilai connect dan sock_read.

Masih mengirimkan proxy_auth

Saat ini masih berfungsi dengan peringatan, tetapi akan gagal pada versi 4.0. Gantilah dengan proxy_headers={"Proxy-Authorization": aiohttp.encode_basic_auth(user, pw)} atau masukkan kredensial ke dalam URL proxy.

Langkah selanjutnya

Dengan sesi di atas, Anda telah melakukan otentikasi, pembatasan, dan pemantauan lalu lintas proxy aiohttp; yang tersisa adalah pool yang dilaluinya. Geonode proxy perumahan melayani endpoint yang digunakan di sini: 2,5 juta alamat IP online, 30–50 juta alamat IP unik yang berganti setiap bulan di lebih dari 195 negara, dengan penargetan berdasarkan negara, provinsi, kota, dan ASN melalui nama pengguna serta sesi tetap hingga 24 jam melalui HTTP, HTTPS, dan SOCKS5. Harga mulai dari $0,27/GB untuk pembelian dalam jumlah besar, paket awal adalah 10 GB seharga $7,92/bulan, dan uji coba 3 hari seharga $5 tanpa kontrak. Contoh penggunaan web scraping dan price intelligence menunjukkan bagaimana tim menggabungkan pengaturan konektor ini dengan logika percobaan ulang dan penjadwalan.

Pertanyaan yang Sering Diajukan

Bagaimana cara mengatur proxy aiohttp untuk seluruh sesi?

Berikan proxy= ke konstruktor ClientSession: aiohttp.ClientSession(proxy="http://user:pass@host:port"). Setiap permintaan dari sesi tersebut akan menggunakannya kecuali jika panggilan tersebut mengganti proxy= dengan nilai lain. Proksi tingkat sesi telah tersedia sejak aiohttp 3.11; pada rilis yang lebih lama, Anda harus meneruskan proxy= pada setiap panggilan get() atau post() sebagai gantinya.

Mengapa proxy_auth sudah tidak direkomendasikan lagi di aiohttp dan apa yang menggantikannya?

Sejak aiohttp 3.14, parameter proxy_auth dan kelas aiohttp.BasicAuth sudah tidak direkomendasikan lagi dan dijadwalkan untuk dihapus pada versi 4.0. Penggantinya adalah header sederhana: proxy_headers={"Proxy-Authorization": aiohttp.encode_basic_auth("user", "pass")}. Header ini melakukan pengkodean Base64 yang sama tanpa kelas pembantu dan berfungsi baik per permintaan maupun di tingkat sesi.

Apakah aiohttp mendukung proxy HTTPS atau SOCKS5?

aiohttp secara native berkomunikasi dengan proxy HTTP dan menggunakan perintah CONNECT untuk meneruskan target https:// melalui proxy tersebut. Menghubungkan ke proxy itu sendiri melalui TLS (TLS-in-TLS) bergantung pada dukungan asyncio dalam build Python Anda dan tidak disarankan untuk diandalkan. SOCKS4 dan SOCKS5 memerlukan paket pihak ketiga aiohttp-socks beserta ProxyConnector-nya.

Berapa banyak permintaan bersamaan yang dapat saya jalankan melalui proxy aiohttp?

TCPConnector secara default mengizinkan 100 koneksi simultan tanpa batasan per-host. Melalui proxy, setiap permintaan berbagi host dan port yang sama, jadi atur limit secara eksplisit, tambahkan limit_per_host jika Anda juga melakukan panggilan langsung, dan bungkus korutin permintaan dalam asyncio.Semaphore agar tugas yang antri tidak membuka soket secara bersamaan.

Mengapa permintaan aiohttp saya melalui proxy macet alih-alih gagal?

ClientSession memiliki batas waktu total 5 menit secara default, yang tampak seperti macet ketika exit residential terhenti. Berikan aiohttp.ClientTimeout(total=30, connect=10, sock_read=20) ke sesi atau permintaan agar kemacetan segera terdeteksi sebagai asyncio.TimeoutError, lalu coba ulang dengan ID sesi baru daripada menunggu.

Apa saja batasan praktis dalam mengarahkan scraper melalui proxy residensial?

Proxy hanya mengubah alamat IP sumber. Proksi tidak memberikan izin untuk mengabaikan robots.txt, ketentuan situs, atau batasan laju, dan tidak membuat pengumpulan data pribadi menjadi sah. Jaga agar jumlah koneksi tetap wajar, kurangi frekuensi saat mendapat respons 429, dan ingat bahwa sesi tetap (sticky session) Geonode berlaku paling lama 24 jam sebelum alamat IP dilepaskan.