Jika Anda menjalankan skrip parsing atau multi-akun di Playwright dan untuk setiap proxy Anda mengangkat instance browser yang terpisah — Anda membakar memori dan CPU dengan sia-sia. Playwright dapat menetapkan proxy pada tingkat BrowserContext, bukan seluruh browser, dan ini mengubah arsitektur skrip secara radikal: satu proses browser, puluhan konteks terisolasi dengan IP, cookie, dan sidik jari yang berbeda. Dalam artikel ini — bagaimana cara kerjanya, kode dalam Python dan Node.js, dan mengapa ini sangat penting saat bekerja dengan proxy residensial dan seluler.
Masalah: proxy pada browser.launch() — mahal dan tidak nyaman
Pendekatan klasik banyak skrip di Playwright terlihat seperti ini: untuk setiap proxy, chromium.launch({ proxy: {...} }) dipanggil, proses browser terpisah dibuat, ia berjalan, kemudian ditutup, dan yang berikutnya diluncurkan dengan proxy baru. Jika perlu memproses 50 alamat IP secara paralel untuk parsing Wildberries atau Ozon, ini berarti 50 proses Chromium penuh secara bersamaan — masing-masing menghabiskan dari 150 hingga 300 MB RAM hanya pada saat mulai, tanpa menghitung tab dan rendering.
Dalam praktiknya, ini terhalang pada batasan dengan 15-20 browser paralel pada VPS biasa dengan 8 GB memori. Server mulai melakukan swapping, skrip jatuh dengan timeout, sebagian proxy tidak sempat bekerja. Masalahnya bukan pada proxy dan bukan pada Playwright — masalahnya ada pada arsitektur: setiap peluncuran browser menarik seluruh mesin rendering, proses GPU, jaringan, dan puluhan thread pendukung, meskipun pada kenyataannya Anda hanya memerlukan IP baru dan sesi yang bersih.
Playwright menyelesaikan masalah ini dengan elegan: proxy dapat ditetapkan tidak saat meluncurkan browser, tetapi saat membuat BrowserContext — "sandbox" terisolasi yang ringan di dalam satu proses browser yang sama. Satu proses Chromium dapat menampung puluhan konteks, masing-masing dengan proxy, cookie, dan localStorage-nya sendiri, tanpa saling berinteraksi.
Browser vs BrowserContext: apa perbedaannya
Browser — adalah proses mesin itu sendiri (Chromium, Firefox, atau WebKit), yang diluncurkan sekali dan hidup lama. BrowserContext — adalah lingkungan terisolasi di dalam proses ini, mirip dengan jendela pribadi (incognito), dengan cookie, cache, izin, dan yang penting bagi kita, pengaturan jaringan, termasuk proxy.
Jika dibandingkan dengan browser anti-deteksi seperti Dolphin Anty atau AdsPower — di sana setiap "profil" juga pada dasarnya adalah kontainer terisolasi terpisah dengan cookie dan sidik jari. Playwright memungkinkan untuk membangun logika serupa secara programatik: satu browser — banyak konteks, masing-masing seperti "profil" terpisah dengan IP-nya sendiri.
| Parameter | Proxy pada Browser | Proxy pada BrowserContext |
|---|---|---|
| Memori untuk satu IP | 150-300 MB (proses baru) | 15-40 MB (konteks baru) |
| Kecepatan perubahan IP | 2-5 detik (memulai ulang browser) | 0.1-0.3 detik (konteks baru) |
| Isolasi cookie | Penuh (proses yang berbeda) | Penuh (konteks tidak berbagi cookie) |
| Maks. sesi paralel pada 8 GB RAM | ~20-25 | ~150-200 |
Proxy pada konteks: kode dalam Python
Berikut adalah pola dasar: satu browser diluncurkan sekali, dan kemudian untuk setiap proxy, konteks terpisah dibuat. Ini adalah API sinkron Playwright, tetapi logikanya identik untuk versi async.
from playwright.sync_api import sync_playwright
proxies = [
{"server": "http://proxy1.example.com:8000", "username": "user1", "password": "pass1"},
{"server": "http://proxy2.example.com:8000", "username": "user2", "password": "pass2"},
{"server": "http://proxy3.example.com:8000", "username": "user3", "password": "pass3"},
]
with sync_playwright() as p:
# Browser diluncurkan SATU kali, tanpa proxy
browser = p.chromium.launch(headless=True)
for proxy in proxies:
# Proxy ditetapkan pada konteks, bukan pada browser
context = browser.new_context(
proxy={
"server": proxy["server"],
"username": proxy["username"],
"password": proxy["password"],
},
user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
)
page = context.new_page()
page.goto("https://httpbin.org/ip", timeout=30000)
print(page.content())
page.close()
context.close() # hanya menutup konteks, browser tetap hidup
browser.close()
Poin kunci — browser.launch() dipanggil satu kali di luar loop, sedangkan browser.new_context() — di dalam loop berdasarkan proxy. Untuk proxy dengan otorisasi menggunakan nama pengguna dan kata sandi, Playwright secara otomatis menyisipkan header Basic Auth pada tingkat jaringan, tidak perlu mengatur apa pun tambahan.
Proxy pada konteks: kode dalam Node.js
Logika yang sama dalam JavaScript menggunakan async/await — sering digunakan dalam kombinasi dengan kerangka kerja parsing di Node.js:
const { chromium } = require('playwright');
const proxies = [
{ server: 'http://proxy1.example.com:8000', username: 'user1', password: 'pass1' },
{ server: 'http://proxy2.example.com:8000', username: 'user2', password: 'pass2' },
];
(async () => {
const browser = await chromium.launch({ headless: true });
for (const proxy of proxies) {
const context = await browser.newContext({
proxy: {
server: proxy.server,
username: proxy.username,
password: proxy.password,
},
});
const page = await context.newPage();
await page.goto('https://httpbin.org/ip', { timeout: 30000 });
console.log(await page.content());
await page.close();
await context.close();
}
await browser.close();
})();
Untuk pemrosesan paralel puluhan proxy, konteks dapat dibuat melalui Promise.all, tanpa menunggu penutupan yang sebelumnya — ini adalah keuntungan utama dari pendekatan "satu browser, banyak konteks" dibandingkan menjalankan proses terpisah.
Rotasi proxy yang bersih tanpa memulai ulang browser
"Rotasi bersih" berarti bahwa saat mengganti IP, tidak ada jejak dari sesi sebelumnya — tidak ada cookie, tidak ada cache, tidak ada service worker. Saat memulai ulang seluruh browser, ini dijamin secara otomatis, tetapi mahal dalam sumber daya. Saat bekerja dengan konteks, hal yang sama dicapai dengan menutup konteks lama dan membuat yang baru — operasi ini memakan waktu sepersekian detik.
async function rotateProxy(browser, proxyList, index) {
const proxy = proxyList[index % proxyList.length];
const context = await browser.newContext({ proxy });
const page = await context.newPage();
return { context, page };
}
// Menggunakan seperti ini: pada setiap iterasi N, ganti IP
for (let i = 0; i < 100; i++) {
if (i % 10 === 0) {
const { context, page } = await rotateProxy(browser, proxies, i);
// bekerja dengan sesi baru 10 permintaan
}
}
Pola ini sangat berguna saat parsing marketplace: misalnya, saat mengumpulkan harga dari Wildberries atau Ozon, di mana perlindungan dari bot melacak jumlah permintaan dari satu IP dalam satuan waktu. Rotasi pada tingkat konteks memungkinkan mengganti IP setiap 5-10 permintaan, tanpa kehilangan kinerja saat memulai ulang browser.
Untuk skenario semacam ini, proxy residensial sangat cocok — mereka memberikan IP pengguna nyata, yang lebih jarang ditandai sebagai mencurigakan oleh marketplace, dibandingkan dengan rentang server yang jelas.
Perbandingan berdasarkan memori dan kecepatan
Pada VPS uji dengan 8 GB RAM dan 4 vCPU, dilakukan pengujian dua pendekatan pada tugas mengakses 100 halaman dengan IP yang berbeda. Dalam kasus pertama, untuk setiap IP, chromium.launch() diluncurkan secara terpisah, dalam kasus kedua — satu browser dengan membuat 100 konteks secara berurutan.
| Metode | 100 peluncuran browser.launch() | 100 konteks dalam 1 browser |
|---|---|---|
| Memori puncak | ~6.8 GB | ~1.9 GB |
| Total waktu eksekusi | 182 detik | 97 detik |
| Jumlah sesi yang gagal karena timeout | 7 | 0 |
Penghematan memori dalam pengujian ini sekitar 70%, dan total waktu eksekusi berkurang hampir setengahnya karena tidak adanya overhead untuk memulai dan menyelesaikan proses Chromium setiap kali. Pada volume besar — ribuan IP per hari — perbedaannya menjadi lebih mencolok, dan ini secara langsung mempengaruhi berapa banyak proxy yang dapat Anda gunakan pada satu server tanpa peningkatan perangkat keras.
Kasus praktis: parsing dan multi-akun
Parsing marketplace. Saat mengumpulkan harga dari Wildberries, Ozon, atau Avito, satu browser menjaga puluhan konteks terbuka, masing-masing dengan IP unik dan penyimpanan cookie sendiri. Ini mengurangi risiko pemblokiran berdasarkan pola permintaan dan memungkinkan pengumpulan data lebih cepat, tanpa menunggu proses untuk memulai ulang antara IP.
Multi-akun dan farming akun. Untuk menguji kampanye iklan di Facebook Ads atau TikTok Ads melalui skrip Playwright, setiap akun logis untuk disimpan dalam konteks terpisah dengan proxy dan user-agent sendiri — ini meniru perilaku browser anti-deteksi seperti Multilogin atau GoLogin, hanya tanpa lisensi untuk anti-deteksi itu sendiri, sepenuhnya secara programatik.
Pengujian iklan geografis. Pemasar yang perlu memeriksa bagaimana kreatif ditampilkan di berbagai negara, konteks dengan proxy yang berbeda memungkinkan untuk "mengunjungi" wilayah yang diperlukan dalam satu sesi browser, tanpa membuka puluhan jendela terpisah secara manual.
Untuk skenario di mana penting untuk meniru lalu lintas seluler — misalnya, memeriksa versi seluler situs atau iklan di TikTok Ads — sebaiknya pertimbangkan proxy seluler, yang memberikan IP dari operator seluler nyata dan lebih jarang menimbulkan kecurigaan pada sistem anti-fraud.
Namun, jika tugasnya adalah parsing cepat dan stabil tanpa kebutuhan akan anonimitas tinggi (misalnya, mengumpulkan data publik dari situs tanpa perlindungan anti-bot yang ketat), proxy pusat data memberikan rasio kecepatan dan biaya terbaik pada volume permintaan yang besar.
Kesalahan umum dan cara menghindarinya
1. Proxy ditetapkan di launch(), bukan di new_context(). Ini adalah kesalahan paling umum bagi pemula — proxy yang ditetapkan saat meluncurkan browser diterapkan ke semua konteks sekaligus dan tidak dapat diubah secara individu. Selalu berikan proxy di new_context(), jika Anda memerlukan fleksibilitas berdasarkan IP.
2. Tidak menutup konteks lama. Jika membuat ratusan konteks dan tidak memanggil context.close(), memori browser akan tumbuh secara linier, sampai proses jatuh karena OOM. Selalu tutup konteks setelah menyelesaikan tugas untuk IP ini.
3. Satu user-agent untuk semua konteks. Jika proxy berubah, tetapi sidik jari browser tetap sama (user-agent, bahasa, zona waktu), sistem anti-bot dari marketplace dan media sosial tetap dapat mengaitkan sesi satu sama lain. Gantilah user-agent dan viewport bersama dengan proxy untuk setiap konteks.
4. Mengabaikan timeout proxy. Proxy residensial dan seluler kadang-kadang merespons lebih lambat daripada proxy pusat data karena infrastruktur jaringan yang nyata. Tetapkan timeout yang wajar (20-40 detik) saat memanggil page.goto(), jika tidak, skrip akan menganggap proxy yang berfungsi "mati".
5. Terlalu banyak konteks dalam satu browser secara bersamaan. Meskipun menghemat memori, pembuatan tidak terkontrol dari 500+ konteks aktif secara bersamaan tetap akan terhalang oleh batasan proses. Keseimbangan yang wajar — 30-80 konteks aktif secara bersamaan untuk satu proses browser, tergantung pada volume RAM server.
Kesimpulan
Menetapkan proxy pada tingkat BrowserContext, bukan pada seluruh browser — adalah solusi arsitektur yang menghemat hingga 70% memori, mempercepat rotasi IP puluhan kali, dan memberikan isolasi cookie yang bersih antara sesi tanpa perlu memulai ulang Chromium secara terus-menerus. Untuk parsing marketplace, pengujian iklan di berbagai wilayah, dan multi-akun, ini adalah cara langsung untuk meningkatkan jumlah sesi paralel pada server yang sama tanpa peningkatan perangkat keras.
Jika Anda membangun skrip di Playwright untuk mengumpulkan data dari Wildberries, Ozon, atau untuk bekerja dengan kabinet iklan Facebook Ads dan TikTok Ads, kualitas dari pool proxy itu sendiri tidak kalah pentingnya dengan arsitektur kode. Untuk tugas di mana keandalan IP dan ketahanan terhadap sistem anti-bot sangat penting, sebaiknya mulai dengan proxy residensial — mereka sangat cocok dengan pendekatan "satu browser, banyak konteks" yang dijelaskan di atas.