Google Cloud Functionsは、サーバー管理なしでコードを実行するためのサーバーレスプラットフォームです。パース、APIリクエストの自動化、データ収集を行う際には、ブロックを回避し、IPをローテーションし、地理的ターゲティングを行うために、プロキシを介してトラフィックをルーティングする必要があります。このガイドでは、PythonとNode.jsを使用したCloud Functionsでのプロキシ設定を実際の例を交えて説明します。
Cloud Functionsでプロキシを使用する理由
Google Cloud Functionsは、Googleのデータセンターの共有IPアドレスを持つ隔離された環境で動作します。外部APIやウェブサイトへの頻繁なリクエストでは、以下のような問題が発生します:
- IPによるブロック — 多くのサービス(Google、Facebook、マーケットプレイス)はデータセンターからのトラフィックを認識し、レート制限や完全なブロックを適用します。
- 地理的制限 — 特定の国でのみアクセス可能なコンテンツへのアクセス(例: WildberriesやOzonの地域価格のパース)。
- レート制限 — 1つのIPアドレスが1分間に行えるリクエストの数は限られています。プロキシを使用することで負荷を分散できます。
- 匿名性 — 機密データや競争情報を扱う際に、リクエストの実際のソースを隠すことができます。
Cloud Functionsでのプロキシの典型的な使用シナリオ:
- 競合の価格を監視するためのマーケットプレイス(Wildberries、Ozon、Amazon)のパース
- APIまたはウェブスクレイピングを介してソーシャルメディア(Instagram、TikTok)からのデータ収集
- 異なる地域での広告の自動チェック
- SEO分析のための検索エンジン(Google、Yandex)への大量リクエスト
- アプリケーションのジオロケーション機能のテスト
Cloud Functionsに適したプロキシの種類
プロキシの種類の選択は、タスク、予算、匿名性の要件によって異なります。以下は主要なオプションの比較です:
| プロキシの種類 | 速度 | 匿名性 | 最適な用途 |
|---|---|---|---|
| データセンターのプロキシ | 高速 (50-200 ms) | 中程度 | 簡単なウェブサイトのパース、APIリクエスト、SEOモニタリング |
| レジデンシャルプロキシ | 中程度 (200-800 ms) | 高い | ソーシャルメディア、マーケットプレイスのパース、ボット対策システムの回避 |
| モバイルプロキシ | 中程度 (300-1000 ms) | 非常に高い | Instagram、TikTok、モバイルアプリ、Facebook API |
選択に関する推奨:
- マーケットプレイスのパース(Wildberries、Ozon、Amazon)には、リクエストごとにローテーションするレジデンシャルプロキシを使用して、各リクエストを新しいIPから行います。
- APIリクエスト(Google Maps API、OpenWeatherMap)には、高速なデータセンターのプロキシを使用します。IPに厳しい制限がない場合。
- ソーシャルメディア(Instagram、TikTok)には、モバイルプロキシを使用します。モバイルオペレーターのIPを持ち、ブロックされることはほとんどありません。
- SEOパース(Google、Yandex)には、必要な地域に地理的に関連付けられたレジデンシャルプロキシを使用します。
Pythonでのプロキシ設定 (requests, aiohttp)
Pythonは、パースや自動化の作業においてCloud Functionsで最も人気のある言語です。ここでは、requests(同期リクエスト)とaiohttp(非同期リクエスト)ライブラリを使用したプロキシの統合について説明します。
requestsライブラリを使用した例 (HTTPプロキシ)
import requests
import os
def parse_with_proxy(request):
# 環境変数からプロキシのデータを取得
proxy_host = os.environ.get('PROXY_HOST', 'proxy.example.com')
proxy_port = os.environ.get('PROXY_PORT', '8080')
proxy_user = os.environ.get('PROXY_USER', 'username')
proxy_pass = os.environ.get('PROXY_PASS', 'password')
# 認証付きプロキシのURLを形成
proxy_url = f"http://{proxy_user}:{proxy_pass}@{proxy_host}:{proxy_port}"
proxies = {
'http': proxy_url,
'https': proxy_url
}
try:
# タイムアウト付きでプロキシを介してリクエストを行う
response = requests.get(
'https://api.example.com/data',
proxies=proxies,
timeout=10,
headers={'User-Agent': 'Mozilla/5.0'}
)
# レスポンスのステータスを確認
response.raise_for_status()
return {
'statusCode': 200,
'body': response.json(),
'ip_used': response.headers.get('X-Forwarded-For', 'unknown')
}
except requests.exceptions.ProxyError as e:
return {'statusCode': 502, 'error': f'プロキシエラー: {str(e)}'}
except requests.exceptions.Timeout:
return {'statusCode': 504, 'error': 'リクエストタイムアウト'}
except requests.exceptions.RequestException as e:
return {'statusCode': 500, 'error': f'リクエスト失敗: {str(e)}'}
重要なポイント:
- 環境変数 — プロキシのデータ(ホスト、ポート、ログイン、パスワード)をSecret ManagerまたはCloud Functionsの環境変数に保存し、コード内には保存しないでください。
- タイムアウト — プロキシに問題がある場合、関数がハングしないように必ず
timeoutを設定してください。 - User-Agent — リクエストが実際のブラウザからのものであるように見えるようにUser-Agentヘッダーを追加してください。
- エラーハンドリング — ProxyError(プロキシの問題)とTimeout(遅いプロキシ)を別々に処理してください。
aiohttpを使用した例 (非同期リクエスト)
高負荷のタスク(例: 1000ページ以上のパース)には、aiohttpを使用した非同期リクエストを利用します:
import aiohttp
import asyncio
import os
async def fetch_with_proxy(url, proxy_url):
async with aiohttp.ClientSession() as session:
try:
async with session.get(
url,
proxy=proxy_url,
timeout=aiohttp.ClientTimeout(total=10),
headers={'User-Agent': 'Mozilla/5.0'}
) as response:
return await response.text()
except aiohttp.ClientProxyConnectionError:
return {'error': 'プロキシ接続に失敗しました'}
except asyncio.TimeoutError:
return {'error': 'リクエストタイムアウト'}
def parse_multiple_urls(request):
proxy_url = f"http://{os.environ['PROXY_USER']}:{os.environ['PROXY_PASS']}@{os.environ['PROXY_HOST']}:{os.environ['PROXY_PORT']}"
urls = [
'https://example.com/page1',
'https://example.com/page2',
'https://example.com/page3'
]
# 非同期リクエストを並行して実行
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
tasks = [fetch_with_proxy(url, proxy_url) for url in urls]
results = loop.run_until_complete(asyncio.gather(*tasks))
return {'statusCode': 200, 'results': results}
非同期アプローチにより、プロキシを介して10〜100の並行リクエストを行うことができ、Cloud Functionsの実行時間制限内で大量のデータをパースするために重要です(最大9分)。
SOCKS5プロキシの使用
一部のプロキシプロバイダーは、UDPトラフィックのより信頼性の高い処理やブロックの回避のためにSOCKS5プロキシを提供しています。PythonでSOCKS5を使用するには、requests[socks]ライブラリを使用します:
# requirements.txtに追加:
# requests[socks]
import requests
def use_socks5_proxy(request):
proxy_url = f"socks5://{os.environ['PROXY_USER']}:{os.environ['PROXY_PASS']}@{os.environ['PROXY_HOST']}:{os.environ['PROXY_PORT']}"
proxies = {
'http': proxy_url,
'https': proxy_url
}
response = requests.get(
'https://api.ipify.org?format=json',
proxies=proxies,
timeout=10
)
return {'statusCode': 200, 'ip': response.json()}
Node.jsでのプロキシ設定 (axios, node-fetch)
Node.jsは、Cloud Functionsで2番目に人気のある言語です。ここでは、axiosとnode-fetchライブラリを使用したプロキシの統合について説明します。
axiosを使用した例
const axios = require('axios');
const HttpsProxyAgent = require('https-proxy-agent');
exports.parseWithProxy = async (req, res) => {
const proxyUrl = `http://${process.env.PROXY_USER}:${process.env.PROXY_PASS}@${process.env.PROXY_HOST}:${process.env.PROXY_PORT}`;
const agent = new HttpsProxyAgent(proxyUrl);
try {
const response = await axios.get('https://api.example.com/data', {
httpsAgent: agent,
timeout: 10000,
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
}
});
res.status(200).json({
success: true,
data: response.data,
proxyUsed: proxyUrl.split('@')[1] // パスワードなしのホスト:ポートを返す
});
} catch (error) {
if (error.code === 'ECONNREFUSED') {
res.status(502).json({ error: 'プロキシ接続が拒否されました' });
} else if (error.code === 'ETIMEDOUT') {
res.status(504).json({ error: 'プロキシタイムアウト' });
} else {
res.status(500).json({ error: error.message });
}
}
};
package.jsonの依存関係:
{
"dependencies": {
"axios": "^1.6.0",
"https-proxy-agent": "^7.0.2"
}
}
node-fetchとSOCKS5を使用した例
const fetch = require('node-fetch');
const { SocksProxyAgent } = require('socks-proxy-agent');
exports.fetchWithSocks5 = async (req, res) => {
const proxyUrl = `socks5://${process.env.PROXY_USER}:${process.env.PROXY_PASS}@${process.env.PROXY_HOST}:${process.env.PROXY_PORT}`;
const agent = new SocksProxyAgent(proxyUrl);
try {
const response = await fetch('https://api.ipify.org?format=json', {
agent,
timeout: 10000
});
const data = await response.json();
res.status(200).json({
success: true,
yourIP: data.ip
});
} catch (error) {
res.status(500).json({ error: error.message });
}
};
SOCKS5の依存関係:
{
"dependencies": {
"node-fetch": "^2.7.0",
"socks-proxy-agent": "^8.0.2"
}
}
プロキシの認証: ログイン/パスワードとIPホワイトリスト
プロキシを使用する際の認証方法は主に2つあります:
1. ログインとパスワードによる認証
最も一般的な方法は、プロキシのURLに認証情報を渡すことです:
http://username:password@proxy.example.com:8080
利点: 設定が簡単で、送信元の固定IPは必要ありません。
欠点: 認証情報が各リクエストで送信され、わずかなオーバーヘッドがあります。
2. IPホワイトリストによる認証
一部のプロバイダーは、Cloud FunctionsのIPアドレスをホワイトリストに追加することを許可します。問題点: Cloud FunctionsはGoogle Cloudのプールから動的IPを使用します。
解決策: Cloud NATを使用して、静的な外部IPを介して出力トラフィックをルーティングします:
- Google CloudでVPCネットワークとサブネットを作成します。
- 静的IPを予約してCloud NATを設定します。
- Cloud FunctionsをVPCコネクタに接続します。
- プロキシプロバイダーのホワイトリストに静的IPを追加します。
プロキシの設定後は、ログインとパスワードが不要になります:
proxies = {
'http': 'http://proxy.example.com:8080',
'https': 'http://proxy.example.com:8080'
}
推奨: ほとんどの場合、ログイン/パスワードによる認証を使用するのが簡単で、Cloud NATに追加のコスト($0.044/時間 + トラフィック)がかかりません。
IPローテーションとプロキシプールの管理
大量のデータをパースする際には、ブロックを避けるためにIPローテーションを使用することが重要です。いくつかのアプローチがあります:
1. プロバイダー側でのローテーション (ローテーティングプロキシ)
多くのプロバイダーは、リクエストごとにIPを自動的に変更する単一のエンドポイントを提供しています:
# 単一のエンドポイント、IPが自動的に変更されます
proxy_url = "http://username:password@rotating.proxy.com:8080"
# 各リクエストが新しいIPから行われます
for i in range(100):
response = requests.get('https://api.ipify.org', proxies={'http': proxy_url})
print(f"リクエスト {i}: IP = {response.text}")
利点: プロキシプールを手動で管理する必要がなく、統合が簡単です。
欠点: 特定のIPを制御できず、コストが高くなる可能性があります。
2. プロキシプールを手動で管理
静的プロキシのリストがある場合は、コードレベルでローテーションを実装します:
import random
import requests
# プロキシプール(Secret Managerから読み込むことも可能)
PROXY_POOL = [
"http://user:pass@proxy1.example.com:8080",
"http://user:pass@proxy2.example.com:8080",
"http://user:pass@proxy3.example.com:8080",
]
def get_random_proxy():
return random.choice(PROXY_POOL)
def parse_with_rotation(urls):
results = []
for url in urls:
proxy = get_random_proxy()
try:
response = requests.get(
url,
proxies={'http': proxy, 'https': proxy},
timeout=10
)
results.append({
'url': url,
'status': response.status_code,
'proxy': proxy.split('@')[1]
})
except Exception as e:
# プロキシが機能しない場合、別のプロキシを試みます
proxy = get_random_proxy()
response = requests.get(url, proxies={'http': proxy, 'https': proxy})
results.append({'url': url, 'status': response.status_code})
return results
3. セッションベースのプロキシ (スティッキーセッション)
セッション内で1つのIPを保持する必要があるタスク(例: サイトへの認証)には、プロキシのURLにセッションIDを追加します:
# セッションIDをログインに追加
import uuid
session_id = str(uuid.uuid4())
proxy_url = f"http://username-session-{session_id}:password@proxy.example.com:8080"
# このsession_idを持つすべてのリクエストは同じIPを通過します
session = requests.Session()
session.proxies = {'http': proxy_url, 'https': proxy_url}
# 認証
session.post('https://example.com/login', data={'user': 'test', 'pass': '123'})
# 同じセッション内の後続リクエスト
session.get('https://example.com/dashboard')
エラーハンドリングとタイムアウト
Cloud Functionsでプロキシを使用する際は、データを失わず、実行時間の制限を超えないように正しくエラーを処理することが重要です。
エラーの種類と処理方法
| エラー | 原因 | 解決策 |
|---|---|---|
| ProxyError | プロキシが利用できないか、認証情報が無効です | プールから別のプロキシに切り替えます |
| Timeout | 遅いプロキシまたは過負荷のサーバー | 5〜10秒のタイムアウトを設定し、別のIPで再試行します |
| 407 Proxy Authentication Required | 無効なログイン/パスワード | 環境変数の認証情報を確認します |
| 429 Too Many Requests | ターゲットサイトでのレート制限 | リクエスト間に遅延を追加し、より多くのIPを使用します |
| 403 Forbidden | プロキシのIPがサイトにブロックされています | IPを変更し、データセンターの代わりにレジデンシャルを使用します |
エラーの包括的な処理の例
import requests
import time
from requests.exceptions import ProxyError, Timeout, RequestException
def fetch_with_retry(url, proxy_pool, max_retries=3):
"""
自動リトライとエラー時のプロキシ切り替えを行うリクエスト
"""
for attempt in range(max_retries):
proxy = random.choice(proxy_pool)
try:
response = requests.get(
url,
proxies={'http': proxy, 'https': proxy},
timeout=10,
headers={'User-Agent': 'Mozilla/5.0'}
)
# ステータスコードを確認
if response.status_code == 200:
return {'success': True, 'data': response.text, 'proxy': proxy}
elif response.status_code == 429:
# レート制限 — 待機して再試行
time.sleep(2 ** attempt) # 指数バックオフ
continue
elif response.status_code == 403:
# IPがブロックされました — プロキシを変更します
continue
else:
return {'success': False, 'status': response.status_code}
except ProxyError:
# プロキシが機能しない — 次のプロキシを試みます
print(f"プロキシ {proxy} が失敗しました、別のものを試みます...")
continue
except Timeout:
# タイムアウト — 別のプロキシで再試行
print(f"{proxy} でタイムアウト、再試行します...")
continue
except RequestException as e:
# その他のエラー
print(f"リクエスト失敗: {e}")
if attempt == max_retries - 1:
return {'success': False, 'error': str(e)}
continue
return {'success': False, 'error': '最大リトライ回数を超えました'}
Cloud Functionsでのタイムアウト設定
Cloud Functionsには実行時間の制限があります(デフォルトは60秒、最大540秒)。プロキシのタイムアウト設定時にはこれを考慮してください:
- 接続タイムアウト — プロキシとの接続にかかる時間(推奨5秒)
- 読み取りタイムアウト — プロキシを介してターゲットサーバーからの応答を受け取るのにかかる時間(推奨10〜15秒)
- 合計タイムアウト — リクエスト全体にかかる時間(関数のタイムアウトよりも短くする必要があります)
# Python: 別々のタイムアウト
response = requests.get(
url,
proxies=proxies,
timeout=(5, 15) # (接続タイムアウト, 読み取りタイムアウト)
)
# Node.jsでaxiosを使用
const response = await axios.get(url, {
httpsAgent: agent,
timeout: 10000 // ミリ秒単位の合計タイムアウト
});
ベストプラクティスとパフォーマンスの最適化
Cloud Functionsでプロキシを効果的に使用するための推奨事項:
1. 認証情報には環境変数を使用
プロキシのログインやパスワードをコードに保存しないでください。Secret Managerや環境変数を使用してください:
# Google Cloudでシークレットを作成
gcloud secrets create proxy-credentials \
--data-file=proxy-config.json
# Cloud Functionsにアクセスを提供
gcloud secrets add-iam-policy-binding proxy-credentials \
--member=serviceAccount:PROJECT_ID@appspot.gserviceaccount.com \
--role=roles/secretmanager.secretAccessor
# コード内でシークレットを読み取る
from google.cloud import secretmanager
import json
def get_proxy_config():
client = secretmanager.SecretManagerServiceClient()
name = f"projects/{PROJECT_ID}/secrets/proxy-credentials/versions/latest"
response = client.access_secret_version(request={"name": name})
return json.loads(response.payload.data.decode('UTF-8'))
2. パース結果をキャッシュする
Cloud StorageやFirestoreを使用してデータをキャッシュし、プロキシを介しての再リクエストを避けます:
import hashlib
from google.cloud import storage
def fetch_with_cache(url, proxy):
# URLに基づいてキャッシュキーを生成
cache_key = hashlib.md5(url.encode()).hexdigest()
# Cloud Storageでキャッシュを確認
bucket = storage.Client().bucket('my-cache-bucket')
blob = bucket.blob(f"cache/{cache_key}.json")
if blob.exists():
# キャッシュされたデータを返す
return json.loads(blob.download_as_text())
# プロキシを介してリクエストを行う
response = requests.get(url, proxies={'http': proxy})
data = response.json()
# キャッシュに保存
blob.upload_from_string(json.dumps(data))
return data
3. 監視とロギング
Cloud Loggingを通じてプロキシのパフォーマンスとエラー頻度を追跡します:
import logging
import time
def fetch_with_logging(url, proxy):
start_time = time.time()
try:
response = requests.get(url, proxies={'http': proxy}, timeout=10)
duration = time.time() - start_time
logging.info({
'url': url,
'proxy': proxy.split('@')[1],
'status': response.status_code,
'duration': duration,
'success': True
})
return response
except Exception as e:
duration = time.time() - start_time
logging.error({
'url': url,
'proxy': proxy.split('@')[1],
'error': str(e),
'duration': duration,
'success': False
})
raise
4. コールドスタートの最適化
Cloud Functionsにはコールドスタートの遅延があります。依存関係を最小限にし、ライブラリの最小バージョンを使用します:
# requirements.txt — 必要なライブラリのみ
requests==2.31.0
# 重要でない限り、pandasのような重いライブラリは避けてください
グローバル変数を使用して接続を再利用します:
# コールドスタート時にセッションを一度作成
session = requests.Session()
session.proxies = {'http': PROXY_URL, 'https': PROXY_URL}
def parse_data(request):
# 呼び出し間でセッションを再利用
response = session.get('https://api.example.com/data')
return response.json()
5. プロキシの地理的関連付け
地理的ターゲティングが必要なタスク(例: 地域価格のパース)には、特定の国や都市に関連付けられたプロキシを使用します:
# 国をログインに指定できるレジデンシャルプロキシの例
proxy_url = f"http://username-country-ru:password@proxy.example.com:8080"
# または、国ごとに異なるエンドポイントを使用します
PROXIES_BY_COUNTRY = {
'RU': 'http://user:pass@ru.proxy.example.com:8080',
'US': 'http://user:pass@us.proxy.example.com:8080',
'DE': 'http://user:pass@de.proxy.example.com:8080'
}
def parse_by_country(country_code):
proxy = PROXIES_BY_COUNTRY.get(country_code)
response = requests.get('https://example.com', proxies={'http': proxy})
return response.text
結論
Google Cloud Functionsとのプロキシ統合は、IP制限なしでのパース、自動化、APIとの連携に広範な可能性を開きます。考慮すべき主なポイントは、リトライロジックを持つエラーハンドリング、ハングを防ぐためのタイムアウトの使用、ブロックを避けるためのIPローテーション、Secret Managerでの認証情報の安全な保存です。
ほとんどのパースおよび自動化タスクには、レジデンシャルプロキシが最適な選択です。これにより、高い匿名性と実際のユーザーのIPを使用することで低いブロック率が確保されます。ソーシャルメディアやモバイルアプリケーションを扱う場合は、モバイルプロキシを推奨します。これらはモバイルオペレーターのIPを持ち、InstagramやTikTokのようなプラットフォームでほとんどブロックされません。
プロキシを使用したCloud Functionsの適切な設定により、インフラを管理することなく、大量のデータを処理するためのスケーラブルでコスト効率の良いソリューションを得ることができます。