API GraphQL semakin populer, tetapi bersamaan dengan itu, batasan juga meningkat: batas laju, pemblokiran berdasarkan IP, filter geografis. Jika Anda bekerja dengan volume data besar melalui GraphQL ā mengurai platform e-commerce, mengumpulkan analitik dari media sosial, atau menguji API ā proksi adalah suatu keharusan. Dalam artikel ini, kita akan membahas cara mengatur proksi untuk permintaan GraphQL dengan benar, menerapkan rotasi IP, dan menghindari pemblokiran.
Kami akan menunjukkan contoh praktis di Python dan Node.js, membahas kesalahan umum, dan memberikan rekomendasi tentang pemilihan jenis proksi untuk berbagai tugas.
Mengapa Proksi Diperlukan untuk Permintaan GraphQL
API GraphQL sering digunakan untuk mendapatkan volume data besar dalam waktu singkat. Berbeda dengan API REST, di mana data dibagi ke dalam banyak endpoint, GraphQL memungkinkan Anda untuk meminta semua yang diperlukan dalam satu permintaan. Ini nyaman, tetapi menciptakan masalah:
- Batas Laju ā sebagian besar API GraphQL publik membatasi jumlah permintaan dari satu IP (misalnya, GitHub API: 5000 permintaan per jam, Shopify: 2 permintaan per detik)
- Pemblokiran IP ā jika melebihi batas atau aktivitas mencurigakan, IP Anda dapat diblokir selama beberapa jam atau selamanya
- Pembatasan Geografis ā beberapa API hanya tersedia dari negara tertentu (misalnya, marketplace lokal atau layanan regional)
- Perlindungan dari Penguraian ā server melacak pola permintaan dan memblokir IP yang mencurigakan
Proksi menyelesaikan masalah ini dengan memungkinkan distribusi permintaan melalui banyak alamat IP, meniru permintaan dari berbagai wilayah, dan menghindari pemblokiran. Ini sangat penting saat bekerja dengan:
- Penguraian data dari platform e-commerce (API GraphQL Shopify, WooCommerce)
- Pengumpulan analitik dari media sosial (API Graph Facebook, API Instagram)
- Pemantauan harga dan ketersediaan produk
- Pengujian API dari berbagai lokasi geografis
- Automatisasi pengumpulan data untuk analitik dan penelitian
Jenis Proksi Apa yang Harus Dipilih untuk Bekerja dengan GraphQL
Pemilihan jenis proksi tergantung pada tugas dan persyaratan API. Mari kita bahas tiga jenis utama dan penerapannya untuk permintaan GraphQL:
| Jenis Proksi | Kecepatan | Anonimitas | Kapan Digunakan |
|---|---|---|---|
| Proksi Data Center | Sangat Tinggi (10-50 ms) | Sedang | Penguraian API publik, pengujian, kecepatan tinggi lebih penting daripada anonimitas |
| Proksi Residensial | Sedang (100-300 ms) | Sangat Tinggi | Bekerja dengan API yang dilindungi (Shopify, Facebook), menghindari filter ketat |
| Proksi Seluler | Sedang (150-400 ms) | Maksimal | API Instagram, API TikTok, aplikasi seluler dengan GraphQL |
Rekomendasi Pemilihan:
- Untuk API Publik (GitHub, OpenWeather) ā cukup dengan proksi data center, mereka cepat dan murah
- Untuk e-commerce (Shopify, WooCommerce) ā proksi residensial, karena platform ini aktif memfilter data center
- Untuk Media Sosial (API Graph Facebook, Instagram) ā proksi seluler atau residensial adalah suatu keharusan
- Untuk Penguraian Massal ā kombinasi: data center untuk lalu lintas utama + residensial untuk rotasi saat pemblokiran
Pengaturan Proksi di Python untuk GraphQL (requests, httpx, gql)
Python adalah salah satu bahasa paling populer untuk bekerja dengan API. Mari kita lihat tiga cara untuk mengatur proksi untuk permintaan GraphQL.
Opsi 1: Pustaka requests (klien HTTP sederhana)
Cara paling sederhana adalah menggunakan pustaka standar requests. Cocok untuk permintaan GraphQL dasar tanpa logika yang rumit.
import requests
import json
# Pengaturan proksi
proxies = {
'http': 'http://username:password@proxy.example.com:8080',
'https': 'http://username:password@proxy.example.com:8080'
}
# Permintaan GraphQL
query = """
query {
products(first: 10) {
edges {
node {
id
title
priceRange {
minVariantPrice {
amount
}
}
}
}
}
}
"""
# Mengirim permintaan melalui proksi
url = "https://your-shop.myshopify.com/api/2024-01/graphql.json"
headers = {
'Content-Type': 'application/json',
'X-Shopify-Storefront-Access-Token': 'your_token_here',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
}
response = requests.post(
url,
json={'query': query},
headers=headers,
proxies=proxies,
timeout=30
)
data = response.json()
print(json.dumps(data, indent=2))
Opsi 2: Pustaka httpx (permintaan asinkron)
Jika perlu mengirim banyak permintaan secara paralel, gunakan httpx dengan dukungan async/await:
import httpx
import asyncio
import json
async def fetch_graphql(query, proxy_url):
url = "https://api.example.com/graphql"
headers = {
'Content-Type': 'application/json',
'Authorization': 'Bearer YOUR_TOKEN',
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)'
}
# Pengaturan proksi untuk httpx
proxies = {
"http://": proxy_url,
"https://": proxy_url
}
async with httpx.AsyncClient(proxies=proxies, timeout=30.0) as client:
response = await client.post(
url,
json={'query': query},
headers=headers
)
return response.json()
# Penggunaan
query = """
query {
viewer {
login
repositories(first: 5) {
nodes {
name
stargazerCount
}
}
}
}
"""
proxy = "http://user:pass@proxy.example.com:8080"
result = asyncio.run(fetch_graphql(query, proxy))
print(json.dumps(result, indent=2))
Opsi 3: Pustaka gql (klien GraphQL khusus)
Untuk bekerja lebih lanjut dengan GraphQL, gunakan pustaka gql ā mendukung validasi skema, caching, dan bekerja dengan permintaan dengan nyaman:
from gql import gql, Client
from gql.transport.requests import RequestsHTTPTransport
# Pengaturan transportasi dengan proksi
transport = RequestsHTTPTransport(
url='https://api.example.com/graphql',
headers={
'Authorization': 'Bearer YOUR_TOKEN',
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64)'
},
proxies={
'http': 'http://user:pass@proxy.example.com:8080',
'https': 'http://user:pass@proxy.example.com:8080'
},
timeout=30
)
# Membuat klien
client = Client(transport=transport, fetch_schema_from_transport=True)
# Permintaan GraphQL
query = gql("""
query GetProducts($first: Int!) {
products(first: $first) {
edges {
node {
id
title
variants(first: 1) {
edges {
node {
price
}
}
}
}
}
}
}
""")
# Melaksanakan permintaan
result = client.execute(query, variable_values={"first": 20})
print(result)
Pengaturan Proksi di Node.js untuk GraphQL (axios, apollo-client)
Node.js juga banyak digunakan untuk bekerja dengan API GraphQL. Mari kita lihat dua pendekatan utama.
Opsi 1: Axios dengan proksi
Klien HTTP yang sederhana dan fleksibel dengan dukungan proksi:
const axios = require('axios');
const HttpsProxyAgent = require('https-proxy-agent');
// Pengaturan proksi
const proxyUrl = 'http://username:password@proxy.example.com:8080';
const httpsAgent = new HttpsProxyAgent(proxyUrl);
// Permintaan GraphQL
const query = `
query {
products(first: 10) {
edges {
node {
id
title
priceRange {
minVariantPrice {
amount
}
}
}
}
}
}
`;
// Mengirim permintaan
axios.post('https://your-shop.myshopify.com/api/2024-01/graphql.json',
{ query },
{
headers: {
'Content-Type': 'application/json',
'X-Shopify-Storefront-Access-Token': 'your_token_here',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'
},
httpsAgent: httpsAgent,
timeout: 30000
}
)
.then(response => {
console.log(JSON.stringify(response.data, null, 2));
})
.catch(error => {
console.error('Error:', error.message);
});
Opsi 2: Apollo Client dengan proksi
Apollo Client adalah klien GraphQL paling populer untuk Node.js dan browser. Pengaturan proksi melalui fetch kustom:
const { ApolloClient, InMemoryCache, HttpLink, gql } = require('@apollo/client');
const fetch = require('cross-fetch');
const HttpsProxyAgent = require('https-proxy-agent');
// Agen proksi
const proxyUrl = 'http://user:pass@proxy.example.com:8080';
const agent = new HttpsProxyAgent(proxyUrl);
// Fetch kustom dengan proksi
const customFetch = (uri, options) => {
return fetch(uri, {
...options,
agent: agent
});
};
// Membuat Apollo Client
const client = new ApolloClient({
link: new HttpLink({
uri: 'https://api.example.com/graphql',
fetch: customFetch,
headers: {
'Authorization': 'Bearer YOUR_TOKEN',
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)'
}
}),
cache: new InMemoryCache()
});
// Permintaan GraphQL
const GET_REPOS = gql`
query GetRepositories($login: String!) {
user(login: $login) {
repositories(first: 5) {
nodes {
name
stargazerCount
}
}
}
}
`;
// Melaksanakan permintaan
client.query({
query: GET_REPOS,
variables: { login: 'facebook' }
})
.then(result => {
console.log(JSON.stringify(result.data, null, 2));
})
.catch(error => {
console.error('Error:', error);
});
Implementasi Rotasi Proksi untuk Menghindari Batas Laju
Rotasi proksi adalah teknik kunci untuk menghindari batasan API. Alih-alih mengirim semua permintaan dari satu IP, Anda mendistribusikannya di antara banyak proksi. Ini memungkinkan Anda untuk menghindari batas laju dan menghindari pemblokiran.
Rotasi Sederhana di Python
Implementasi dasar rotasi dengan siklus pengalihan proksi:
import requests
import itertools
import time
# Daftar proksi
PROXY_LIST = [
'http://user:pass@proxy1.example.com:8080',
'http://user:pass@proxy2.example.com:8080',
'http://user:pass@proxy3.example.com:8080',
'http://user:pass@proxy4.example.com:8080',
]
# Membuat iterator tak terbatas
proxy_pool = itertools.cycle(PROXY_LIST)
def make_graphql_request(query):
"""Mengirim permintaan GraphQL dengan rotasi proksi"""
proxy = next(proxy_pool)
proxies = {'http': proxy, 'https': proxy}
url = "https://api.example.com/graphql"
headers = {
'Content-Type': 'application/json',
'Authorization': 'Bearer YOUR_TOKEN',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'
}
try:
response = requests.post(
url,
json={'query': query},
headers=headers,
proxies=proxies,
timeout=30
)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
print(f"Error with proxy {proxy}: {e}")
# Beralih ke proksi berikutnya
return make_graphql_request(query)
# Contoh penggunaan
queries = [
'query { products(first: 10) { edges { node { id title } } } }',
'query { collections(first: 5) { edges { node { id title } } } }',
'query { shop { name email } }'
]
for query in queries:
result = make_graphql_request(query)
print(result)
time.sleep(1) # Jeda antara permintaan
Rotasi Cerdas dengan Pelacakan Kesalahan
Versi yang lebih canggih, yang melacak proksi yang tidak berfungsi dan secara otomatis mengecualikannya dari kolam:
import requests
import random
from collections import defaultdict
import time
class ProxyRotator:
def __init__(self, proxy_list, max_failures=3):
self.proxy_list = proxy_list.copy()
self.max_failures = max_failures
self.failures = defaultdict(int)
self.active_proxies = proxy_list.copy()
def get_proxy(self):
"""Mendapatkan proksi aktif secara acak"""
if not self.active_proxies:
raise Exception("Semua proksi tidak tersedia!")
return random.choice(self.active_proxies)
def mark_failure(self, proxy):
"""Menandai percobaan yang gagal"""
self.failures[proxy] += 1
if self.failures[proxy] >= self.max_failures:
print(f"Proksi {proxy} dikeluarkan dari kolam (melebihi batas kesalahan)")
if proxy in self.active_proxies:
self.active_proxies.remove(proxy)
def mark_success(self, proxy):
"""Mengatur ulang penghitung kesalahan saat berhasil"""
self.failures[proxy] = 0
# Inisialisasi
proxies = [
'http://user:pass@proxy1.example.com:8080',
'http://user:pass@proxy2.example.com:8080',
'http://user:pass@proxy3.example.com:8080',
]
rotator = ProxyRotator(proxies)
def graphql_request_with_retry(query, max_retries=3):
"""Permintaan GraphQL dengan pengulangan otomatis"""
for attempt in range(max_retries):
proxy = rotator.get_proxy()
proxies_dict = {'http': proxy, 'https': proxy}
try:
response = requests.post(
'https://api.example.com/graphql',
json={'query': query},
headers={
'Content-Type': 'application/json',
'Authorization': 'Bearer TOKEN',
'User-Agent': 'Mozilla/5.0'
},
proxies=proxies_dict,
timeout=30
)
response.raise_for_status()
# Sukses ā reset penghitung kesalahan
rotator.mark_success(proxy)
return response.json()
except Exception as e:
print(f"Percobaan {attempt + 1}/{max_retries} dengan {proxy} gagal: {e}")
rotator.mark_failure(proxy)
time.sleep(2) # Jeda sebelum pengulangan
raise Exception("Tidak dapat melakukan permintaan setelah semua percobaan")
# Penggunaan
query = 'query { products(first: 10) { edges { node { id title } } } }'
result = graphql_request_with_retry(query)
print(result)
Pengaturan Header dan User-Agent untuk Permintaan GraphQL
Header HTTP yang benar sangat penting untuk keberhasilan bekerja dengan API GraphQL melalui proksi. Banyak API memeriksa tidak hanya IP, tetapi juga header permintaan.
Header Wajib untuk GraphQL
headers = {
# Jenis konten ā selalu application/json untuk GraphQL
'Content-Type': 'application/json',
# Otorisasi (tergantung pada API)
'Authorization': 'Bearer YOUR_ACCESS_TOKEN',
# atau
'X-Shopify-Storefront-Access-Token': 'token_here',
# User-Agent ā meniru browser nyata
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
# Accept ā menunjukkan bahwa kita menerima JSON
'Accept': 'application/json',
# Accept-Language ā bahasa pengguna
'Accept-Language': 'en-US,en;q=0.9',
# Accept-Encoding ā dukungan kompresi
'Accept-Encoding': 'gzip, deflate, br',
# Referer ā dari mana permintaan berasal (opsional)
'Referer': 'https://example.com/',
# Origin ā untuk permintaan CORS
'Origin': 'https://example.com'
}
Rotasi User-Agent
Untuk lebih banyak anonimitas, disarankan untuk merotasi User-Agent bersamaan dengan proksi:
import random
USER_AGENTS = [
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.1 Safari/605.1.15'
]
def get_random_headers(token):
"""Menghasilkan header acak"""
return {
'Content-Type': 'application/json',
'Authorization': f'Bearer {token}',
'User-Agent': random.choice(USER_AGENTS),
'Accept': 'application/json',
'Accept-Language': 'en-US,en;q=0.9',
'Accept-Encoding': 'gzip, deflate, br'
}
Penanganan Kesalahan dan Pengulangan Melalui Proksi
Saat bekerja dengan proksi, kesalahan tidak dapat dihindari: timeout, proksi tidak tersedia, pemblokiran. Penting untuk menangani situasi ini dengan benar dan menerapkan mekanisme pengulangan.
Kesalahan Umum GraphQL melalui Proksi
- Timeout ā proksi lambat atau terbebani (tingkatkan timeout menjadi 30-60 detik)
- HTTP 407 Proxy Authentication Required ā login/password proksi tidak valid
- HTTP 429 Too Many Requests ā melebihi batas laju (perlu rotasi proksi)
- HTTP 403 Forbidden ā IP proksi diblokir (ganti jenis proksi ke residensial)
- Connection refused ā proksi tidak tersedia (kecualikan dari kolam)
Penanganan Kesalahan yang Canggih
import requests
import time
from requests.exceptions import ProxyError, Timeout, ConnectionError
def graphql_request_robust(query, proxy, max_retries=3, backoff=2):
"""
Permintaan GraphQL yang andal dengan penanganan semua jenis kesalahan
Args:
query: Permintaan GraphQL
proxy: URL proksi
max_retries: maksimum percobaan
backoff: pengali jeda antara percobaan
"""
url = "https://api.example.com/graphql"
headers = {
'Content-Type': 'application/json',
'Authorization': 'Bearer TOKEN',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'
}
proxies = {'http': proxy, 'https': proxy}
for attempt in range(max_retries):
try:
response = requests.post(
url,
json={'query': query},
headers=headers,
proxies=proxies,
timeout=30
)
# Memeriksa batas laju
if response.status_code == 429:
retry_after = int(response.headers.get('Retry-After', 60))
print(f"Batas laju! Menunggu {retry_after} detik...")
time.sleep(retry_after)
continue
# Memeriksa pemblokiran IP
if response.status_code == 403:
print(f"IP {proxy} diblokir! Perlu proksi lain.")
raise Exception("IP diblokir")
# Memeriksa kesalahan otorisasi proksi
if response.status_code == 407:
print(f"Kesalahan otorisasi proksi {proxy}")
raise Exception("Otorisasi proksi gagal")
response.raise_for_status()
# Memeriksa kesalahan GraphQL
data = response.json()
if 'errors' in data:
print(f"Kesalahan GraphQL: {data['errors']}")
# Beberapa kesalahan bisa diulang, beberapa tidak
if is_retryable_graphql_error(data['errors']):
time.sleep(backoff * (attempt + 1))
continue
else:
raise Exception(f"Kesalahan GraphQL: {data['errors']}")
return data
except (ProxyError, ConnectionError) as e:
print(f"Percobaan {attempt + 1}: Proksi tidak tersedia - {e}")
time.sleep(backoff * (attempt + 1))
except Timeout as e:
print(f"Percobaan {attempt + 1}: Timeout - {e}")
time.sleep(backoff * (attempt + 1))
except requests.exceptions.HTTPError as e:
print(f"Percobaan {attempt + 1}: Kesalahan HTTP - {e}")
if attempt < max_retries - 1:
time.sleep(backoff * (attempt + 1))
else:
raise
raise Exception(f"Tidak dapat melakukan permintaan setelah {max_retries} percobaan")
def is_retryable_graphql_error(errors):
"""Menentukan apakah permintaan dapat diulang pada kesalahan GraphQL"""
retryable_codes = ['THROTTLED', 'INTERNAL_ERROR', 'TIMEOUT']
for error in errors:
if error.get('extensions', {}).get('code') in retryable_codes:
return True
return False
Praktik Terbaik Bekerja dengan GraphQL Melalui Proksi
Mari kita ringkas dan memberikan rekomendasi untuk bekerja secara efektif dengan API GraphQL melalui proksi:
ā Optimisasi Permintaan
- Mintalah hanya bidang yang diperlukan ā GraphQL memungkinkan Anda untuk secara tepat menentukan apa yang diperlukan
- Gunakan paginasi daripada meminta semua data sekaligus
- Kelompokkan permintaan terkait dalam satu (GraphQL mendukung permintaan ganda)
- Cache hasil di sisi klien untuk mengurangi jumlah permintaan
ā Manajemen Proksi
- Gunakan kolam minimal 5-10 proksi untuk rotasi
- Secara teratur periksa keandalan proksi (health check)
- Secara otomatis kecualikan proksi yang tidak berfungsi dari kolam
- Untuk tugas kritis, simpan proksi cadangan dari jenis lain
ā Mematuhi Batasan
- Pelajari dokumentasi API ā di sana tercantum batasan yang tepat
- Tambahkan jeda antara permintaan (1-2 detik minimal)
- Pantau header X-RateLimit-Remaining dan X-RateLimit-Reset
- Ketika menerima kesalahan 429, tingkatkan jeda secara eksponensial
ā Keamanan dan Anonimitas
- Selalu gunakan proksi HTTPS untuk melindungi token otorisasi
- Rotasi User-Agent bersamaan dengan proksi
- Jangan simpan token dalam kode ā gunakan variabel lingkungan
- Catat hanya informasi yang diperlukan
Arsitektur yang Direkomendasikan untuk Proyek Skala Besar
Jika Anda bekerja dengan volume data besar, kami merekomendasikan arsitektur berikut:
- Antrian Tugas (Redis, RabbitMQ) ā untuk mendistribusikan permintaan di antara pekerja
- Kolam Pekerja ā setiap pekerja menggunakan proksi mereka sendiri
- Manajer Proksi ā melacak status proksi dan mendistribusikannya di antara pekerja
- Basis Data ā untuk menyimpan hasil dan status tugas
- Pemantauan ā melacak kesalahan, kecepatan, penggunaan proksi
Kesimpulan
Bekerja dengan API GraphQL melalui proksi bukan hanya menambahkan parameter proxies dalam permintaan. Untuk bekerja yang andal dan efektif, Anda perlu menerapkan rotasi proksi, penanganan kesalahan yang benar, mengatur header, dan mematuhi batasan API. Kami telah membahas contoh praktis di Python dan Node.js yang dapat langsung digunakan dalam proyek Anda.
Kesimpulan utama: gunakan proksi residensial untuk API yang dilindungi (Shopify, Facebook), data center untuk API publik dan penguraian massal, terapkan rotasi otomatis dengan pengecualian proksi yang tidak berfungsi, tambahkan jeda dan tangani semua jenis kesalahan. Ini akan memungkinkan Anda untuk bekerja dengan stabil dengan semua API GraphQL tanpa pemblokiran.
Jika Anda berencana untuk bekerja dengan API GraphQL dalam produksi, kami merekomendasikan menggunakan proksi residensial ā mereka memberikan stabilitas maksimum dan risiko pemblokiran minimal. Untuk pengujian dan pengembangan, proksi data center sudah cukup ā mereka lebih cepat dan lebih murah.