GraphQL APIはますます人気が高まっていますが、それに伴い制限も増えています: レート制限、IPによるブロック、地理的フィルター。大量のデータをGraphQLを通じて扱う場合—eコマースサイトをパースしたり、ソーシャルメディアから分析データを収集したり、APIをテストしたりする場合—プロキシなしでは済まされません。この記事では、GraphQLリクエストのためのプロキシを正しく設定し、IPのローテーションを実装し、ブロックを回避する方法を説明します。
PythonとNode.jsの実用的な例を示し、一般的なエラーを分析し、さまざまなタスクに適したプロキシの選び方についての推奨事項を提供します。
GraphQLリクエストにプロキシが必要な理由
GraphQL APIは、短時間で大量のデータを取得するために頻繁に使用されます。REST APIとは異なり、データが多数のエンドポイントに分かれているのではなく、GraphQLでは必要なすべてを1つのリクエストで要求できます。これは便利ですが、問題も生じます:
- レート制限 — ほとんどの公開GraphQL APIは、1つのIPからのリクエスト数を制限しています(例: GitHub API: 1時間あたり5000リクエスト、Shopify: 1秒あたり2リクエスト)
- IPブロック — 制限を超えたり、疑わしい活動があったりすると、あなたのIPが数時間または永遠にブロックされる可能性があります
- 地理的制限 — 一部のAPIは特定の国からのみアクセス可能です(例: ローカルマーケットプレイスや地域サービス)
- パース防止 — サーバーはリクエストのパターンを追跡し、疑わしいIPをブロックします
プロキシはこれらの問題を解決し、リクエストを多数のIPアドレスに分散させ、異なる地域からのリクエストを模倣し、ブロックを回避することを可能にします。これは特に以下の作業を行う際に重要です:
- eコマースプラットフォームからのデータのパース(Shopify、WooCommerce GraphQL API)
- ソーシャルメディアからの分析データの収集(Facebook Graph API、Instagram API)
- 商品の価格と在庫の監視
- 異なる地理的位置からのAPIのテスト
- 分析や研究のためのデータ収集の自動化
GraphQLで使用するプロキシの種類
プロキシの種類の選択は、タスクとAPIの要件によって異なります。GraphQLリクエストに対する3つの主要なタイプとその適用について説明します:
| プロキシの種類 | 速度 | 匿名性 | 使用するタイミング |
|---|---|---|---|
| データセンターのプロキシ | 非常に高い (10-50 ms) | 中程度 | 公開APIのパース、テスト、高速が匿名性より重要な場合 |
| レジデンシャルプロキシ | 中程度 (100-300 ms) | 非常に高い | 保護されたAPI(Shopify、Facebook)での作業、厳しいフィルターの回避 |
| モバイルプロキシ | 中程度 (150-400 ms) | 最大 | Instagram API、TikTok API、GraphQLを使用したモバイルアプリ |
選択に関する推奨:
- 公開API用(GitHub、OpenWeather) — データセンターのプロキシで十分です。速くて安価です。
- eコマース用(Shopify、WooCommerce) — レジデンシャルプロキシを使用してください。これらのプラットフォームはデータセンターを積極的にフィルタリングします。
- ソーシャルメディア用(Facebook Graph API、Instagram) — モバイルまたはレジデンシャルプロキシが必須です。
- 大量のパース用 — 組み合わせ: 基本トラフィック用のデータセンター + ブロック時のローテーション用のレジデンシャル。
GraphQL用のPythonでのプロキシ設定 (requests, httpx, gql)
PythonはAPIでの作業に最も人気のある言語の1つです。GraphQLリクエストのためのプロキシ設定の3つの方法を見てみましょう。
オプション1: requestsライブラリ(シンプルなHTTPクライアント)
最も簡単な方法は、標準ライブラリのrequestsを使用することです。複雑なロジックのない基本的なGraphQLリクエストに適しています。
import requests
import json
# プロキシの設定
proxies = {
'http': 'http://username:password@proxy.example.com:8080',
'https': 'http://username:password@proxy.example.com:8080'
}
# GraphQLリクエスト
query = """
query {
products(first: 10) {
edges {
node {
id
title
priceRange {
minVariantPrice {
amount
}
}
}
}
}
}
"""
# プロキシを介してリクエストを送信
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))
オプション2: httpxライブラリ(非同期リクエスト)
多くのリクエストを並行して送信する必要がある場合は、httpxを使用して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)'
}
# 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()
# 使用例
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))
オプション3: gqlライブラリ(専用のGraphQLクライアント)
GraphQLでの高度な作業には、gqlライブラリを使用してください。これはスキーマの検証、キャッシング、リクエストの便利な操作をサポートします:
from gql import gql, Client
from gql.transport.requests import RequestsHTTPTransport
# プロキシを使用したトランスポートの設定
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
)
# クライアントの作成
client = Client(transport=transport, fetch_schema_from_transport=True)
# GraphQLリクエスト
query = gql("""
query GetProducts($first: Int!) {
products(first: $first) {
edges {
node {
id
title
variants(first: 1) {
edges {
node {
price
}
}
}
}
}
}
}
""")
# リクエストの実行
result = client.execute(query, variable_values={"first": 20})
print(result)
GraphQL用のNode.jsでのプロキシ設定 (axios, apollo-client)
Node.jsもGraphQL APIでの作業に広く使用されています。2つの主要なアプローチを見てみましょう。
オプション1: プロキシを使用したAxios
シンプルで柔軟なHTTPクライアントで、プロキシをサポートしています:
const axios = require('axios');
const HttpsProxyAgent = require('https-proxy-agent');
// プロキシの設定
const proxyUrl = 'http://username:password@proxy.example.com:8080';
const httpsAgent = new HttpsProxyAgent(proxyUrl);
// GraphQLリクエスト
const query = `
query {
products(first: 10) {
edges {
node {
id
title
priceRange {
minVariantPrice {
amount
}
}
}
}
}
}
`;
// リクエストの送信
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);
});
オプション2: プロキシを使用したApollo Client
Apollo ClientはNode.jsとブラウザ用の最も人気のあるGraphQLクライアントです。カスタムフェッチを介してプロキシを設定します:
const { ApolloClient, InMemoryCache, HttpLink, gql } = require('@apollo/client');
const fetch = require('cross-fetch');
const HttpsProxyAgent = require('https-proxy-agent');
// プロキシエージェント
const proxyUrl = 'http://user:pass@proxy.example.com:8080';
const agent = new HttpsProxyAgent(proxyUrl);
// プロキシを使用したカスタムフェッチ
const customFetch = (uri, options) => {
return fetch(uri, {
...options,
agent: agent
});
};
// 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()
});
// GraphQLリクエスト
const GET_REPOS = gql`
query GetRepositories($login: String!) {
user(login: $login) {
repositories(first: 5) {
nodes {
name
stargazerCount
}
}
}
}
`;
// リクエストの実行
client.query({
query: GET_REPOS,
variables: { login: 'facebook' }
})
.then(result => {
console.log(JSON.stringify(result.data, null, 2));
})
.catch(error => {
console.error('Error:', error);
});
レート制限を回避するためのプロキシのローテーション実装
プロキシのローテーションは、APIの制限を回避するための重要な技術です。すべてのリクエストを1つのIPから送信するのではなく、複数のプロキシに分散させます。これにより、レート制限を回避し、ブロックを避けることができます。
Pythonでのシンプルなローテーション
プロキシを循環的に切り替える基本的なローテーションの実装:
import requests
import itertools
import time
# プロキシのリスト
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',
]
# 無限イテレータを作成
proxy_pool = itertools.cycle(PROXY_LIST)
def make_graphql_request(query):
"""プロキシのローテーションを使用してGraphQLリクエストを送信"""
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"プロキシ {proxy} に関するエラー: {e}")
# 次のプロキシに切り替え
return make_graphql_request(query)
# 使用例
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) # リクエスト間の待機時間
エラー追跡を伴うスマートローテーション
動作しないプロキシを追跡し、自動的にプールから除外するより高度なバージョン:
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):
"""ランダムなアクティブプロキシを取得"""
if not self.active_proxies:
raise Exception("すべてのプロキシが利用できません!")
return random.choice(self.active_proxies)
def mark_failure(self, proxy):
"""失敗した試行をマーク"""
self.failures[proxy] += 1
if self.failures[proxy] >= self.max_failures:
print(f"プロキシ {proxy} がプールから除外されました (エラー制限を超えました)")
if proxy in self.active_proxies:
self.active_proxies.remove(proxy)
def mark_success(self, proxy):
"""成功時にエラーカウントをリセット"""
self.failures[proxy] = 0
# 初期化
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):
"""自動再試行を伴うGraphQLリクエスト"""
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()
# 成功 — エラーカウントをリセット
rotator.mark_success(proxy)
return response.json()
except Exception as e:
print(f"{proxy} での試行 {attempt + 1}/{max_retries} が失敗しました: {e}")
rotator.mark_failure(proxy)
time.sleep(2) # 再試行前の待機時間
raise Exception("すべての試行後にリクエストを実行できませんでした")
# 使用例
query = 'query { products(first: 10) { edges { node { id title } } } }'
result = graphql_request_with_retry(query)
print(result)
GraphQLリクエストのためのヘッダーとUser-Agentの設定
正しいHTTPヘッダーは、プロキシを介してGraphQL APIと正常に連携するために重要です。多くのAPIは、IPだけでなくリクエストヘッダーも検証します。
GraphQL用の必須ヘッダー
headers = {
# コンテンツタイプ — GraphQL用には常にapplication/json
'Content-Type': 'application/json',
# 認証(APIによって異なる)
'Authorization': 'Bearer YOUR_ACCESS_TOKEN',
# または
'X-Shopify-Storefront-Access-Token': 'token_here',
# User-Agent — 実際のブラウザを模倣
'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 — JSONを受け入れることを示す
'Accept': 'application/json',
# Accept-Language — ユーザーの言語
'Accept-Language': 'en-US,en;q=0.9',
# Accept-Encoding — 圧縮サポート
'Accept-Encoding': 'gzip, deflate, br',
# Referer — リクエストがどこから来たか(オプション)
'Referer': 'https://example.com/',
# Origin — CORSリクエスト用
'Origin': 'https://example.com'
}
User-Agentのローテーション
より高い匿名性を確保するために、プロキシとともにUser-Agentをローテーションすることをお勧めします:
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):
"""ランダムなヘッダーを生成"""
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'
}
プロキシを介したエラー処理と再試行
プロキシを使用する際には、タイムアウト、利用できないプロキシ、ブロックなどのエラーが避けられません。これらの状況を適切に処理し、再試行メカニズムを実装することが重要です。
プロキシを介したGraphQLの一般的なエラー
- タイムアウト — プロキシが遅いか、過負荷(タイムアウトを30-60秒に増やしてください)
- HTTP 407 Proxy Authentication Required — プロキシのログイン/パスワードが無効
- HTTP 429 Too Many Requests — レート制限を超えました(プロキシのローテーションが必要)
- HTTP 403 Forbidden — プロキシのIPがブロックされています(レジデンシャルプロキシに切り替えてください)
- Connection refused — プロキシが利用できません(プールから除外してください)
高度なエラー処理
import requests
import time
from requests.exceptions import ProxyError, Timeout, ConnectionError
def graphql_request_robust(query, proxy, max_retries=3, backoff=2):
"""
すべてのタイプのエラーを処理する堅牢なGraphQLリクエスト
Args:
query: GraphQLリクエスト
proxy: プロキシのURL
max_retries: 最大試行回数
backoff: 試行間の待機時間の乗数
"""
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
)
# レート制限のチェック
if response.status_code == 429:
retry_after = int(response.headers.get('Retry-After', 60))
print(f"レート制限!{retry_after}秒待機します...")
time.sleep(retry_after)
continue
# IPのブロックのチェック
if response.status_code == 403:
print(f"IP {proxy} がブロックされました!別のプロキシが必要です。")
raise Exception("IPがブロックされました")
# プロキシ認証エラーのチェック
if response.status_code == 407:
print(f"プロキシ {proxy} の認証エラー")
raise Exception("プロキシ認証に失敗しました")
response.raise_for_status()
# GraphQLエラーのチェック
data = response.json()
if 'errors' in data:
print(f"GraphQLエラー: {data['errors']}")
# 一部のエラーは再試行可能であり、一部はそうではありません
if is_retryable_graphql_error(data['errors']):
time.sleep(backoff * (attempt + 1))
continue
else:
raise Exception(f"GraphQLエラー: {data['errors']}")
return data
except (ProxyError, ConnectionError) as e:
print(f"試行 {attempt + 1}: プロキシが利用できません - {e}")
time.sleep(backoff * (attempt + 1))
except Timeout as e:
print(f"試行 {attempt + 1}: タイムアウト - {e}")
time.sleep(backoff * (attempt + 1))
except requests.exceptions.HTTPError as e:
print(f"試行 {attempt + 1}: HTTPエラー - {e}")
if attempt < max_retries - 1:
time.sleep(backoff * (attempt + 1))
else:
raise
raise Exception(f"{max_retries}回の試行後にリクエストを実行できませんでした")
def is_retryable_graphql_error(errors):
"""GraphQLエラーが再試行可能かどうかを判断します"""
retryable_codes = ['THROTTLED', 'INTERNAL_ERROR', 'TIMEOUT']
for error in errors:
if error.get('extensions', {}).get('code') in retryable_codes:
return True
return False
プロキシを介したGraphQLのベストプラクティス
GraphQL APIをプロキシを介して効果的に操作するための推奨事項をまとめます:
✓ リクエストの最適化
- 必要なフィールドのみをリクエストしてください — GraphQLは必要なものを正確に指定できます
- すべてのデータを一度にリクエストするのではなく、ページネーションを使用してください
- 関連するリクエストを1つにまとめてください(GraphQLは複数のリクエストをサポートしています)
- クライアント側で結果をキャッシュしてリクエスト数を減らしてください
✓ プロキシの管理
- ローテーション用に最低5-10のプロキシプールを使用してください
- プロキシの動作状況を定期的に確認してください(ヘルスチェック)
- 動作しないプロキシを自動的にプールから除外してください
- 重要なタスクには異なるタイプのバックアッププロキシを保持してください
✓ 制限の遵守
- APIのドキュメントを確認してください — 正確な制限が記載されています
- リクエスト間に遅延を追加してください(最低1-2秒)
- X-RateLimit-RemainingおよびX-RateLimit-Resetヘッダーを追跡してください
- 429エラーを受け取った場合、遅延を指数関数的に増やしてください
✓ セキュリティと匿名性
- 常にHTTPSプロキシを使用して認証トークンを保護してください
- プロキシとともにUser-Agentをローテーションしてください
- コード内にトークンを保存しないでください — 環境変数を使用してください
- 最小限の必要な情報のみをログに記録してください
大規模プロジェクトのための推奨アーキテクチャ
大量のデータを扱う場合は、次のアーキテクチャをお勧めします:
- タスクキュー(Redis、RabbitMQ) — ワーカー間でリクエストを分散させるため
- ワーカープール — 各ワーカーが独自のプロキシを使用
- プロキシマネージャー — プロキシの状態を追跡し、ワーカー間で分配
- データベース — 結果とタスクの状態を保存
- モニタリング — エラー、速度、プロキシの使用状況を追跡
結論
GraphQL APIをプロキシを介して操作することは、単にリクエストにproxiesパラメータを追加するだけではありません。信頼性と効率的な作業を実現するためには、プロキシのローテーション、適切なエラー処理、ヘッダーの設定、APIの制限の遵守を実装する必要があります。PythonとNode.jsの実用的な例を見てきましたが、これらはすぐにあなたのプロジェクトで使用できます。
主な結論: 保護されたAPI(Shopify、Facebook)にはレジデンシャルプロキシを使用し、公開APIや大量のパースにはデータセンターを使用し、動作しないプロキシを除外する自動ローテーションを実装し、遅延を追加し、すべてのタイプのエラーを処理してください。これにより、あらゆるGraphQL APIと安定して作業し、ブロックを回避できます。
プロダクションでGraphQL APIを使用する予定がある場合は、レジデンシャルプロキシを使用することをお勧めします。これにより、最大の安定性と最小のブロックリスクが確保されます。テストや開発には、データセンターのプロキシが適しています。これらはより速く、安価です。