Google Cloud Functions 是一个无服务器平台,可以在不管理服务器的情况下运行代码。在进行解析、自动化 API 请求或数据收集时,通常需要通过代理路由流量,以绕过封锁、进行 IP 轮换和地理定位。在本指南中,我们将讨论如何在 Cloud Functions 中使用 Python 和 Node.js 设置代理,并提供实际示例。
为什么在 Cloud Functions 中使用代理
Google Cloud Functions 在一个与 Google 数据中心共享 IP 地址的隔离环境中运行。当频繁请求外部 API 或网站时,会出现以下问题:
- IP 封锁 — 许多服务(如 Google、Facebook、市场)识别来自数据中心的流量并实施速率限制或完全封锁。
- 地理限制 — 访问仅在特定国家/地区可用的内容(例如,解析 Wildberries 或 Ozon 的区域价格)。
- 速率限制 — 单个 IP 地址每分钟可以进行有限的请求。代理可以分散负载。
- 匿名性 — 在处理敏感数据或进行竞争情报时隐藏请求的真实来源。
在 Cloud Functions 中使用代理的典型场景:
- 解析市场(Wildberries、Ozon、Amazon)以监控竞争对手的价格
- 通过 API 或网页抓取从社交媒体(Instagram、TikTok)收集数据
- 自动化检查不同地区的广告
- 对搜索引擎(Google、Yandex)进行大量请求以进行 SEO 分析
- 测试应用程序的地理定位功能
哪些类型的代理适合 Cloud Functions
选择代理类型取决于任务、预算和匿名性要求。以下是主要选项的比较:
| 代理类型 | 速度 | 匿名性 | 最适合 |
|---|---|---|---|
| 数据中心代理 | 高(50-200 毫秒) | 中等 | 解析简单网站、API 请求、SEO 监控 |
| 住宅代理 | 中等(200-800 毫秒) | 高 | 解析社交媒体、市场、绕过反机器人系统 |
| 移动代理 | 中等(300-1000 毫秒) | 非常高 | 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 代理
一些代理提供商提供 SOCKS5 代理,以更可靠地处理 UDP 流量或绕过封锁。要在 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 的第二大流行语言。我们将讨论如何使用 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 白名单
在使用代理时,有两种主要的身份验证方法:
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 网络和子网
- 配置 Cloud NAT 并保留静态 IP
- 将 Cloud Functions 连接到 VPC 连接器
- 将静态 IP 添加到代理提供商的白名单中
配置后,代理不需要用户名和密码:
proxies = {
'http': 'http://proxy.example.com:8080',
'https': 'http://proxy.example.com:8080'
}
建议: 对于大多数情况,使用基于用户名/密码的身份验证 — 这更简单,并且不需要额外的 Cloud NAT 成本(从 $0.044/小时 + 流量)。
IP 轮换和代理池管理
在解析大量数据时,使用 IP 轮换至关重要,以避免封锁。存在几种方法:
1. 由提供商进行轮换(Rotating Proxies)
许多提供商提供轮换代理 — 一个端点,在每个请求时自动更改 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. 基于会话的代理(sticky sessions)
对于需要在会话中保持一个 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 代理身份验证所需 | 用户名/密码错误 | 检查环境变量中的凭据 |
| 429 请求过多 | 目标网站的速率限制 | 在请求之间添加延迟,使用更多 IP |
| 403 禁止访问 | 代理 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 集成为解析、自动化和 API 工作提供了广泛的可能性,而不受 IP 限制。需要考虑的主要事项包括:正确处理错误并实施重试逻辑,使用超时以防止挂起,进行 IP 轮换以避免封锁,以及在 Secret Manager 中安全存储凭据。
对于大多数解析和自动化任务,最佳选择是 住宅代理 — 它们提供高匿名性和低封锁率,因为使用的是实际用户的 IP。对于社交媒体和移动应用,我们建议使用 移动代理,它们具有移动运营商的 IP,几乎不会被 Instagram 和 TikTok 等平台封锁。
通过正确配置 Cloud Functions 和代理,您可以获得可扩展且经济高效的解决方案,以处理大量数据,而无需管理基础设施。