httpx adalah klien HTTP yang paling sering dipilih oleh proyek-proyek Python baru ketika mereka sudah tidak lagi dapat dipenuhi oleh requests, terutama karena httpx menyediakan satu API untuk kode sinkron dan asinkron. Panduan ini menunjukkan cara mengintegrasikan proxy httpx ke dalam Client dan AsyncClient, melakukan otentikasi terhadap titik akhir perumahan yang berganti-ganti, mengunci sesi yang melekat, menambahkan SOCKS5, menyesuaikan batas waktu dan upaya ulang, serta mendiagnosis tiga kesalahan yang sebenarnya akan Anda temui. Pada akhirnya, Anda akan memiliki klien yang dapat digunakan kembali yang merutekan melalui proxy dan dapat membuktikan alamat IP keluarnya.
Contoh-contoh ini ditujukan untuk httpx versi 0.27 atau yang lebih baru. Jika Anda menggunakan versi yang lebih lama, perhatikan bahwa kata kunci proxies= telah diganti namanya menjadi proxy= dan mounts= pada httpx 0.26; semua penjelasan di bawah ini menggunakan nama-nama yang berlaku saat ini.
Dua cara untuk mengonfigurasi proxy httpx: proxy dan mounts
httpx menyediakan satu opsi sederhana dan satu opsi yang lebih presisi. proxy= menerima satu URL dan mengarahkan setiap permintaan melalui URL tersebut. mounts= memetakan pola URL ke transportasi, dan setiap transportasi dapat memiliki proxy, jumlah percobaan ulang, serta batas koneksi tersendiri.
| Opsi | Cakupan | Kapan digunakan |
|---|
proxy="http://..." | Seluruh klien, semua skema | Satu proxy untuk semuanya, yang biasanya digunakan pada gateway bergilir |
mounts={"https://": HTTPTransport(proxy=...)} | Per pola URL | Proxy berbeda per skema atau host, atau melewati proxy untuk layanan internal |
httpx.get(url, proxy=...) | Panggilan tingkat atas tunggal | Pemeriksaan cepat dan skrip tanpa klien yang bertahan lama |
Bentuk sederhananya terlihat seperti ini:
import httpx
PROXY = "http://USERNAME:PASSWORD@premium-residential.geonode.com:9000"
with httpx.Client(proxy=PROXY, timeout=20.0) as client:
r = client.get("https://api.ipify.org?format=json")
print(r.status_code, r.json())
Port 9000 adalah gerbang berputar HTTP(S), sehingga setiap koneksi baru mendapatkan alamat IP perumahan yang berbeda. Kredensial disertakan dalam URL sebagai otentikasi Basic, dan httpx mengubahnya menjadi header Proxy-Authorization tanpa kode tambahan.
Perangkap dalam perutean per-skema
Dengan mounts, Anda dapat memberikan transportasi terpisah untuk target http:// dan https://. Perangkapnya terletak pada skema URL proxy itu sendiri. Untuk target https://, httpx mengirimkan CONNECT ke proxy dan kemudian menegosiasikan TLS dengan server asal di dalam terowongan tersebut. Lompatan ke proxy menggunakan HTTP biasa, sehingga URL proxy pada mount https:// seharusnya dimulai dengan http://, bukan https://. Menulis https:// di sana memerintahkan httpx untuk membungkus koneksi ke proxy itu sendiri dalam TLS, yang tidak didukung oleh sebagian besar gateway, sehingga Anda akan mengalami kegagalan handshake yang tampaknya tidak terkait dengan proxy.
import httpx
PROXY = "http://USERNAME:PASSWORD@premium-residential.geonode.com:9000"
mounts = {
"http://": httpx.HTTPTransport(proxy=PROXY),
"https://": httpx.HTTPTransport(proxy=PROXY), # http:// scheme is correct here
# internal services stay direct: mount a transport without a proxy
"all://*.internal.example": httpx.HTTPTransport(),
}
with httpx.Client(mounts=mounts) as client:
print(client.get("https://api.ipify.org?format=json").json())
Pola yang lebih spesifik lebih diutamakan daripada pola umum, sehingga mount all://*.internal.example melewati proxy untuk domain tersebut sementara semua yang lain tetap diarahkan melalui proxy.
Otentikasi dalam URL
Kredensial Geonode berupa nama pengguna dan kata sandi biasa, jadi formulir userinfo http://user:pass@host:port sudah cukup. Dua catatan praktis. Pertama, jika kata sandi mengandung karakter seperti @, :, atau /, lakukan pengkodean persentase (percent-encode) dengan urllib.parse.quote sebelum membangun URL. Kedua, jaga agar informasi rahasia tidak masuk ke dalam sistem kontrol versi dengan membacanya dari variabel lingkungan:
import os
from urllib.parse import quote
import httpx
user = os.environ["GEONODE_USER"]
password = quote(os.environ["GEONODE_PASS"], safe="")
proxy = f"http://{user}:{password}@premium-residential.geonode.com:9000"
client = httpx.Client(proxy=proxy)
Semua yang mengikuti menambahkan opsi penargetan dan sesi dengan memperluas nama pengguna, bukan URL, sehingga pola yang sama tetap berfungsi.
Sinkronisasi Client versus AsyncClient
Klien asinkron menggunakan kata kunci proxy= yang sama. Perbedaan satu-satunya muncul saat Anda beralih ke mounts: AsyncClient memerlukan httpx.AsyncHTTPTransport, bukan httpx.HTTPTransport. Mengirimkan transportasi sinkron ke klien asinkron akan memicu kesalahan pada permintaan pertama.
import asyncio
import httpx
PROXY = "http://USERNAME:PASSWORD@premium-residential.geonode.com:9000"
async def fetch(client: httpx.AsyncClient, url: str) -> str:
r = await client.get(url)
r.raise_for_status()
return r.json()["ip"]
async def main() -> None:
transport = httpx.AsyncHTTPTransport(proxy=PROXY, retries=2)
async with httpx.AsyncClient(
mounts={"all://": transport}, timeout=httpx.Timeout(10.0, connect=5.0)
) as client:
urls = ["https://api.ipify.org?format=json"] * 5
ips = await asyncio.gather(*(fetch(client, u) for u in urls))
print(ips)
asyncio.run(main())
Karena port 9000 berputar per koneksi, lima permintaan bersamaan biasanya menampilkan beberapa alamat IP yang berbeda. Itulah perilaku yang diharapkan dari kumpulan alamat IP yang berputar; jika Anda ingin alamat IP-nya sama, gunakan sesi tetap (di bawah).
SOCKS5 hingga httpx[socks]
Dukungan SOCKS dalam konfigurasi proxy httpx bersifat opsional. Pasang dengan pip install "httpx[socks]", yang akan menambahkan paket socksio, lalu ganti skemanya. Port bergilir SOCKS5 dari Geonode adalah 11000:
import httpx
PROXY = "socks5://USERNAME:PASSWORD@premium-residential.geonode.com:11000"
with httpx.Client(proxy=PROXY) as client:
print(client.get("https://api.ipify.org?format=json").json())
Tanpa instalasi tambahan tersebut, httpx akan memunculkan pesan ImportError yang menyebutkan socksio segera setelah mendeteksi URL proxy socks5://. Untuk penggalian data biasa, port HTTP lebih mudah untuk didebug karena respons 407 dapat dibaca.
Variabel lingkungan dan trust_env
Dengan pengaturan default trust_env=True, httpx membaca HTTP_PROXY, HTTPS_PROXY, ALL_PROXY, dan NO_PROXY, lalu menerapkannya secara otomatis. Hal ini praktis di workstation, namun dapat menimbulkan kebingungan dalam CI, di mana proxy perusahaan di lingkungan secara diam-diam mengubah rute Anda. Jika Anda mengatur proxy dalam kode, pertimbangkan untuk menonaktifkan pencarian variabel lingkungan agar perilakunya konsisten di mana pun:
import httpx
client = httpx.Client(
proxy="http://USERNAME:PASSWORD@premium-residential.geonode.com:9000",
trust_env=False,
)
proxy= yang dinyatakan secara eksplisit sudah memiliki prioritas lebih tinggi daripada pengaturan lingkungan; trust_env=False juga mencegah httpx membaca SSL_CERT_FILE, jadi tetapkanlah dengan sengaja.
Batas Waktu dan Upaya Ulang di Tingkat Transportasi
Proksi menambahkan satu hop, sehingga waktu koneksi menjadi lebih lama dan batas waktu default 5 detik terkadang terlalu ketat untuk titik keluar perumahan. httpx.Timeout memungkinkan Anda menetapkan anggaran total dengan batas koneksi terpisah, sedangkan HTTPTransport(retries=...) melakukan percobaan ulang terhadap upaya koneksi yang gagal bahkan sebelum permintaan dikirim.
import httpx
PROXY = "http://USERNAME:PASSWORD@premium-residential.geonode.com:9000"
transport = httpx.HTTPTransport(proxy=PROXY, retries=2)
timeout = httpx.Timeout(15.0, connect=8.0)
with httpx.Client(mounts={"all://": transport}, timeout=timeout) as client:
r = client.get("https://api.ipify.org?format=json")
print(r.json())
Upaya ulang pada tingkat transportasi mencakup kegagalan ConnectError dan sejenisnya saat membangun koneksi. Fitur ini tidak melakukan percobaan ulang berdasarkan kode status, dan tidak melakukan percobaan ulang setelah byte dikirim, sehingga 502 dari gateway atau 429 dari situs target tetap mencapai kode Anda. Tangani hal tersebut dalam loop kecil buatan Anda sendiri, di mana Anda juga dapat memutuskan apakah akan mengubah ID sesi.
Sesi yang melekat dengan tata bahasa nama pengguna Geonode
Mengganti alamat IP pada setiap koneksi tidak tepat untuk alur multi-langkah seperti login, paginasi, dan unduhan. Untuk alur-alur tersebut, proxy httpx Anda harus mempertahankan alamat IP keluar yang sama di seluruh permintaan. Geonode mengenkode informasi ini dalam nama pengguna dan menyajikannya di port sticky HTTP(S) 10000:
USER[-type-residential|datacenter|mix][-country-xx][-state-name|-city-name][-asn-N][-session-XXXXXXXX][-lifetime-SECONDS]
ID sesi terdiri dari 8 karakter alfanumerik yang Anda buat sendiri; masa berlakunya berkisar antara 180 hingga 86.400 detik (24 jam). Provinsi dan kota tidak boleh digabungkan, dan asn mengharuskan adanya negara.
import secrets
import httpx
def sticky_client(country: str = "us", lifetime: int = 600) -> httpx.Client:
session_id = secrets.token_hex(4) # 8 hex characters
username = f"USERNAME-country-{country}-session-{session_id}-lifetime-{lifetime}"
proxy = f"http://{username}:PASSWORD@premium-residential.geonode.com:10000"
return httpx.Client(proxy=proxy, timeout=httpx.Timeout(15.0, connect=8.0))
with sticky_client() as client:
first = client.get("https://api.ipify.org?format=json").json()["ip"]
second = client.get("https://api.ipify.org?format=json").json()["ip"]
print(first == second, first)
session_id yang baru berarti alamat IP baru, sehingga rotasi "secara manual" hanya berarti membuat klien baru. Jaga agar masa berlaku port tetap sesingkat mungkin sesuai kebutuhan pekerjaan.
Verifikasi alamat IP keluar
Setiap contoh di atas mengakses https://api.ipify.org?format=json, yang akan mengembalikan {"ip": "..."} terlepas dari alamat mana pun yang mengaksesnya. Bandingkan hal tersebut dengan panggilan langsung (tanpa proxy) sekali saja, dan Anda akan mendapatkan uji coba sederhana dua baris yang membuktikan bahwa proxy berada di jalur tersebut. Lakukan hal ini sebelum melakukan debugging apa pun: banyak laporan "proxy httpx tidak berfungsi" sebenarnya merupakan permintaan yang tidak pernah menggunakan proxy karena variabel lingkungan atau pola mount mengambil alih.
Penanganan Kode Kesalahan 407 pada ProxyError dan ConnectError
Tiga jenis kegagalan ini mencakup hampir semua masalah pada proxy httpx. Tabel berikut memetakan masing-masing kegagalan tersebut beserta penyebab dan solusinya.
| Gejala | Pengecualian | Penyebab Umum | Solusi |
|---|
| Status respons 407 | httpx.ProxyError (muncul saat pengaturan terowongan untuk target https) atau respons 407 biasa untuk target http | Kredensial salah, atau opsi nama pengguna yang ditolak oleh gateway | Periksa format nama pengguna; lakukan pengkodean persentase pada kata sandi |
| Terowongan ditolak, 502/503 dari proxy | httpx.ProxyError | Gateway tidak dapat membuka terowongan, atau port yang digunakan tidak sesuai dengan protokol | Pastikan menggunakan 9000/10000 untuk HTTP(S), 11000/12000 untuk SOCKS5 |
| Tidak dapat menjangkau host proxy | httpx.ConnectError | Masalah DNS, firewall, atau nama host yang salah | Selesaikan masalah host secara manual; tingkatkan batas waktu connect |
| Permintaan macet lalu gagal | httpx.ConnectTimeout / httpx.ReadTimeout | Koneksi keluar dari jaringan rumahan lambat atau target diblokir | Perpanjang batas waktu, coba lagi dengan ID sesi baru |
import httpx
PROXY = "http://USERNAME:PASSWORD@premium-residential.geonode.com:9000"
def get_via_proxy(url: str) -> httpx.Response | None:
try:
with httpx.Client(proxy=PROXY, timeout=httpx.Timeout(15.0, connect=8.0)) as client:
r = client.get(url)
if r.status_code == 407:
raise RuntimeError("proxy rejected credentials (407)")
return r
except httpx.ProxyError as e:
print("proxy refused tunnel:", e)
except httpx.ConnectError as e:
print("cannot connect to proxy host:", e)
except httpx.TimeoutException as e:
print("timed out:", type(e).__name__)
return None
resp = get_via_proxy("https://api.ipify.org?format=json")
print(resp.json() if resp else "failed")
Semua pengecualian httpx mewarisi dari httpx.HTTPError, sehingga satu kode except yang umum sudah cukup untuk pencatatan, tetapi memisahkan ProxyError dari ConnectError memberi tahu Anda apakah perlu memeriksa kredensial atau jaringan.
Kesalahan umum
Menggunakan https:// dalam URL proxy
Sebagaimana dijelaskan di atas, skema URL proxy menggambarkan lompatan ke proxy. Gunakan http:// untuk port HTTP(S) meskipun tujuannya adalah https://. Sisihkan https:// untuk gateway yang secara eksplisit mengiklankan TLS ke proxy.
Memadukan HTTPTransport ke dalam AsyncClient
mounts pada AsyncClient harus menggunakan AsyncHTTPTransport. Transportasi sinkronisasi diterima pada saat konstruksi tetapi gagal pada saat permintaan dengan pesan kesalahan yang tidak membantu, sehingga hal ini mudah terlewatkan dalam pengujian yang tidak pernah mengirimkan permintaan.
Mengharapkan satu alamat IP dari port yang berputar
Port 9000 berganti per koneksi. Jika dua permintaan berturut-turut harus berbagi alamat IP, gunakan port 10000 dengan ID -session- dalam nama pengguna. Menggunakan kembali Client tidak secara otomatis mengunci alamat IP karena koneksi dalam pool dapat diganti.
Membiarkan lingkungan mengesampingkan kode Anda
HTTPS_PROXY yang terselip dalam gambar Docker atau pelaksana CI akan mengalihkan lalu lintas yang Anda kira langsung, dan NO_PROXY dapat mengecualikan host yang Anda harapkan diproksi. Tetapkan trust_env=False ketika perutean proxy harus bersifat deterministik.
Langkah selanjutnya
Setelah klien di atas mengembalikan alamat IP residensial dan sesi sticky tetap terjaga antar permintaan, variabel yang tersisa adalah pool di baliknya. Proksi residensial dari Geonode menampilkan titik akhir yang digunakan dalam panduan ini: 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 sintaks nama pengguna serta sesi yang tetap berlaku hingga 24 jam. 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. Jika Anda sedang mengembangkan crawler berbasis layanan ini, kasus penggunaan web scraping menjelaskan cara memadukan proxy dengan logika percobaan ulang, dan proxy pusat data yang berganti-ganti merupakan opsi yang lebih murah untuk target yang tidak memblokir rentang alamat pusat data.
Pertanyaan yang Sering Diajukan
Bagaimana cara mengatur proxy httpx untuk semua permintaan?
Berikan URL proxy ke konstruktor klien: httpx.Client(proxy="http://user:pass@host:port"). Setiap permintaan yang dibuat melalui klien tersebut, baik untuk target http:// maupun https://, akan diarahkan melalui proxy. Kata kunci yang sama berfungsi pada httpx.AsyncClient dan pada helper tingkat atas seperti httpx.get(). Kredensial dalam URL dikirim sebagai Proxy-Authorization secara otomatis.
Apa perbedaan antara proxy dan mounts di httpx?
proxy adalah pintasan yang mengarahkan semua lalu lintas melalui satu proxy. mounts memetakan pola URL seperti "http://" atau "https://" ke transportasi, yang masing-masing dapat memiliki proxy, upaya ulang, dan batasan tersendiri. Gunakan mounts jika Anda memerlukan proxy yang berbeda per skema atau per host, atau ingin melewati proxy untuk beberapa tujuan dengan mem-mount transportasi tanpa proxy.
Mengapa mount https:// saya gagal ketika URL proxy dimulai dengan https://?
Skema dalam URL proxy menggambarkan cara httpx berkomunikasi dengan proxy, bukan dengan target. Untuk target https://, httpx membuka terowongan CONNECT melalui koneksi HTTP biasa ke proxy, sehingga URL proxy biasanya harus menggunakan http://. Penggunaan https:// meminta httpx untuk menegosiasikan TLS dengan proxy itu sendiri, yang tidak didukung oleh sebagian besar titik akhir proxy.
Apakah httpx mendukung proxy SOCKS5?
Ya, tetapi hanya setelah menginstal dependensi opsional dengan pip install httpx[socks], yang akan menarik socksio. Kemudian, berikan proxy="socks5://user:pass@host:port" persis seperti saat Anda menggunakan proxy HTTP. Dengan Geonode, port bergilir SOCKS5 adalah 11000 dan port tetap SOCKS5 adalah 12000; port HTTP(S) tidak menerima handshake SOCKS.
Apakah httpx dapat mencoba ulang permintaan secara otomatis saat koneksi proxy gagal?
Hanya pada tingkat transportasi. httpx.HTTPTransport(retries=2) akan mencoba ulang upaya koneksi yang gagal, termasuk kesalahan koneksi ke proxy, tetapi tidak akan pernah mencoba ulang permintaan yang sudah mendapatkan respons seperti 407 atau 502. Upaya ulang berdasarkan kode status HTTP harus dilakukan dalam loop Anda sendiri, di mana Anda dapat mengganti ID sesi atau menunda permintaan secara sengaja.
Apakah ada batasan terhadap apa yang dapat dilakukan oleh konfigurasi proxy httpx dengan alamat IP residensial?
Proksi mengubah alamat IP keluar Anda; proksi tidak mengubah apa yang diizinkan oleh situs. Patuhi robots.txt, ketentuan layanan, dan batasan laju, jaga agar jumlah koneksi bersamaan tetap wajar, dan hindari mengumpulkan data pribadi yang tidak memiliki dasar hukum untuk diproses. Sesi perumahan juga dibatasi hingga 24 jam; setelah itu, alamat IP akan dilepaskan dan Anda harus membuka sesi baru.