返回博客

如何为GraphQL API设置代理:IP轮换和绕过速率限制的代码示例

完整的代理设置指南,用于与GraphQL API配合使用:代码示例、IP地址轮换、绕过速率限制和防止封锁。

📅2026年2月15日
```html

GraphQL API变得越来越流行,但随之而来的是限制:速率限制、IP封锁、地理过滤。如果您通过GraphQL处理大量数据——解析电子商务平台、收集社交媒体分析或测试API——没有代理是不可避免的。在本文中,我们将讨论如何正确设置GraphQL请求的代理,实现IP轮换并避免封锁。

我们将展示Python和Node.js的实际示例,分析常见错误并提供选择不同任务的代理类型的建议。

GraphQL请求为什么需要代理

GraphQL API通常用于在短时间内获取大量数据。与REST API不同,REST API的数据分散在多个端点上,GraphQL允许通过一个请求获取所有必要的数据。这很方便,但也带来了问题:

  • 速率限制 — 大多数公共GraphQL API限制来自单个IP的请求数量(例如,GitHub API:每小时5000个请求,Shopify:每秒2个请求)
  • IP封锁 — 超过限制或可疑活动时,您的IP可能会被封锁几个小时或永久封锁
  • 地理限制 — 某些API仅在特定国家可用(例如,地方市场或区域服务)
  • 防爬虫保护 — 服务器监控请求模式并封锁可疑的IP

代理解决了这些问题,允许通过多个IP地址分配请求,模拟来自不同地区的请求并绕过封锁。这在处理以下情况时尤其重要:

  • 从电子商务平台(Shopify,WooCommerce GraphQL API)解析数据
  • 从社交媒体(Facebook Graph API,Instagram API)收集分析
  • 监控商品价格和库存
  • 从不同地理位置测试API
  • 自动化数据收集以进行分析和研究

选择哪种类型的代理来处理GraphQL

选择代理类型取决于任务和API的要求。我们将讨论三种主要类型及其在GraphQL请求中的应用:

代理类型 速度 匿名性 何时使用
数据中心代理 非常高(10-50毫秒) 中等 解析公共API,测试,高速比匿名性更重要
住宅代理 中等(100-300毫秒) 非常高 处理受保护的API(Shopify,Facebook),绕过严格的过滤器
移动代理 中等(150-400毫秒) 最大 Instagram API,TikTok API,带有GraphQL的移动应用程序

选择建议:

  • 对于公共API(GitHub,OpenWeather)——数据中心代理就足够了,它们快速且便宜
  • 对于电子商务(Shopify,WooCommerce)——住宅代理,因为这些平台积极过滤数据中心
  • 对于社交媒体(Facebook Graph API,Instagram)——移动或住宅代理是必须的
  • 对于大规模解析——组合:数据中心用于主要流量 + 住宅代理用于封锁时的轮换

在Python中为GraphQL设置代理(requests,httpx,gql)

Python是处理API的最流行语言之一。我们将讨论三种设置GraphQL请求代理的方法。

选项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)

在Node.js中为GraphQL设置代理(axios,apollo-client)

Node.js也广泛用于处理GraphQL API。我们将讨论两种主要方法。

选项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.message);
});

选项2:使用代理的Apollo Client

Apollo Client是Node.js和浏览器中最流行的GraphQL客户端。通过自定义fetch设置代理:

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);

// 带代理的自定义fetch
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);
});

实现代理轮换以绕过速率限制

代理轮换是绕过API限制的关键技术。您可以将请求分配给多个代理,而不是将所有请求发送到单个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"尝试 {attempt + 1}/{max_retries} 使用 {proxy} 失败: {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 代理身份验证所需 — 代理的用户名/密码不正确
  • HTTP 429 请求过多 — 超过速率限制(需要代理轮换)
  • HTTP 403 禁止 — 代理的IP被封锁(将代理类型更改为住宅代理)
  • 连接被拒绝 — 代理不可用(从池中排除)

高级错误处理

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允许精确指定所需内容
  • 使用分页而不是一次请求所有数据
  • 将相关请求组合为一个(GraphQL支持多个请求)
  • 在客户端缓存结果以减少请求数量

✓ 代理管理

  • 使用至少5-10个代理的池进行轮换
  • 定期检查代理的可用性(健康检查)
  • 自动将不可用的代理排除在池之外
  • 对于关键任务,保持其他类型的备用代理

✓ 遵守限制

  • 研究API文档 — 其中列出了确切的限制
  • 在请求之间添加延迟(至少1-2秒)
  • 监控X-RateLimit-Remaining和X-RateLimit-Reset头
  • 在收到429错误时,指数增加延迟

✓ 安全性和匿名性

  • 始终使用HTTPS代理来保护授权令牌
  • 与代理一起轮换User-Agent
  • 不要在代码中存储令牌 — 使用环境变量
  • 仅记录最低限度所需的信息

大规模项目的推荐架构

如果您处理大量数据,建议使用以下架构:

  1. 任务队列(Redis,RabbitMQ)— 用于在工作者之间分配请求
  2. 工作者池 — 每个工作者使用自己的代理
  3. 代理管理器 — 监控代理状态并在工作者之间分配
  4. 数据库 — 用于存储结果和任务状态
  5. 监控 — 监控错误、速度、代理使用情况

结论

通过代理处理GraphQL API并不仅仅是向请求中添加proxies参数。为了可靠和有效的工作,需要实现代理轮换、正确的错误处理、设置头部并遵守API限制。我们讨论了Python和Node.js的实际示例,可以立即在您的项目中使用。

主要结论:对于受保护的API(Shopify,Facebook)使用住宅代理,对于公共API和大规模解析使用数据中心代理,实现自动轮换并排除不可用的代理,添加延迟并处理所有类型的错误。这将使您能够稳定地与任何GraphQL API进行交互,而不会被封锁。

如果您计划在生产环境中使用GraphQL API,建议使用住宅代理——它们提供最大稳定性和最低封锁风险。对于测试和开发,数据中心代理就足够了——它们更快且更便宜。

```