在市场调研和品牌监控中,评论数据是洞察用户情绪与产品反馈的金矿。但当你尝试从Trustpilot、G2或Google商家页面批量获取评论时,很快就会遭遇限流、验证码和地域化内容偏移。代理如何帮助你抓取评论数据——核心答案在于通过IP轮换和地理定位绕过这些限制,同时保持请求的合法性和可控性。本文将聚焦Trustpilot、G2和Google商家评论的实战抓取方案。
合规声明:本文仅讨论公开可访问的评论数据采集,不涉及登录墙后的内容或个人身份信息(PII)的批量抓取。在抓取任何平台前,请务必阅读其服务条款(ToS)和robots.txt文件,并遵守适用法律,包括美国的CFAA和欧盟的GDPR。robots.txt标准是判断哪些页面允许被爬取的第一道参考。
代理如何帮助你抓取评论数据:为什么评论页面会被限流和地域化
评论平台普遍部署多层反爬机制。以Trustpilot为例,其页面内容通过Next.js服务端渲染,评论数据嵌入在__NEXT_DATA__ JSON中;G2则使用更激进的反机器人检测,包括Cloudflare挑战和浏览器指纹校验;Google商家评论的展示受hl(语言)和gl(国家)参数影响,不同地区用户看到的评论子集可能完全不同。
当你用单一IP连续请求这些页面时,通常在50-200次请求后就会触发限流(HTTP 429)或验证码挑战。更隐蔽的问题是地域化内容偏移:平台会根据请求IP的地理位置返回不同语言的评论或不同的评论排序。例如,从日本IP请求Google商家页面,可能优先展示日语评论,而非你需要的英文评论。
住宅代理通过以下方式解决这些问题:
- IP轮换:每个请求来自不同的真实ISP IP地址,避免触发基于IP的频率限制。
- 地理定位:指定
country-US等参数,确保获取目标地区的评论内容。 - 真实指纹:住宅IP来自真实ISP,反爬系统难以区分代理流量与正常用户流量。
根据行业经验,使用住宅代理抓取评论数据的成功率通常可达90%以上,而数据中心代理在强反爬平台上往往低于30%。
代理类型对比:评论抓取场景
| 代理类型 | 成功率 | 平均延迟 | 适用平台 | 成本 |
|---|---|---|---|---|
| 住宅代理 | 90-98% | 200-800ms | Trustpilot / G2 / Google | 中高 |
| 数据中心代理 | 10-30% | 50-150ms | 低防护站点 | 低 |
| 移动代理 | 95-99% | 300-1200ms | 强反爬平台 | 高 |
对于评论抓取,住宅代理是性价比最优的选择。如果你需要更深入了解代理类型差异,可以参考ProxyHat代理位置页面了解可用的地理定位选项。
Trustpilot评论抓取实战:解析__NEXT_DATA__
Trustpilot的评论页面使用Next.js框架,所有评论数据以JSON格式嵌入在HTML源码中的<script id="__NEXT_DATA__">标签内。直接解析这段JSON比提取DOM元素更稳定,因为Trustpilot的CSS类名经常变化,但JSON结构相对固定。
关键策略:每页轮换IP,通过ProxyHat的session参数为每一页分配不同的出口IP,同时设置2-3秒的请求间隔以模拟人类浏览行为。
import requests
import json
import re
import time
PROXY_TEMPLATE = "http://user-session-{sid}:pass@gate.proxyhat.com:8080"
def scrape_trustpilot_reviews(domain, max_pages=10):
all_reviews = []
for page in range(1, max_pages + 1):
# 每页使用不同的session ID,实现IP轮换
session_id = f"tp-{domain}-{page}"
proxy_url = PROXY_TEMPLATE.format(sid=session_id)
proxies = {"http": proxy_url, "https": proxy_url}
url = f"https://www.trustpilot.com/review/{domain}?page={page}"
headers = {
"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-Language": "en-US,en;q=0.9",
"Accept": "text/html,application/xhtml+xml",
}
try:
resp = requests.get(url, proxies=proxies, headers=headers, timeout=30)
if resp.status_code == 200:
match = re.search(
r'<script id="__NEXT_DATA__" type="application/json">(.*?)</script>',
resp.text, re.DOTALL
)
if match:
data = json.loads(match.group(1))
reviews = data.get("props", {}).get("pageProps", {})
.get("reviews", [])
for r in reviews:
all_reviews.append({
"title": r.get("title", ""),
"text": r.get("text", ""),
"rating": r.get("rating", 0),
"date": r.get("dates", {}).get("publishedDate", ""),
})
print(f"Page {page}: {len(reviews)} reviews")
else:
print(f"Page {page}: __NEXT_DATA__ not found")
elif resp.status_code == 429:
print(f"Page {page}: Rate limited, waiting 10s")
time.sleep(10)
else:
print(f"Page {page}: HTTP {resp.status_code}")
except Exception as e:
print(f"Page {page}: Error - {e}")
time.sleep(2.5) # 请求间隔
return all_reviews
reviews = scrape_trustpilot_reviews("example.com", max_pages=5)
print(f"Total: {len(reviews)} reviews")上述代码通过user-session-{sid}为每一页创建独立的代理会话,ProxyHat网关会为不同session分配不同的出口IP。如果需要保持同一IP跨页抓取(例如需要登录态一致性),则使用固定的session ID即可。
G2评论抓取:对抗更强的反爬机制
G2的反爬比Trustpilot更激进,通常部署Cloudflare的JS挑战和浏览器指纹检测。直接用requests请求往往返回403或挑战页面。应对策略是使用住宅代理配合更真实的请求头,必要时考虑使用无头浏览器(如Playwright)。
G2的产品评论页面URL格式为https://www.g2.com/products/{slug}/reviews,星级分布和评论列表嵌入在页面HTML中。以下是Python抓取示例,使用国家定向住宅代理:
import requests
import time
from bs4 import BeautifulSoup
# 使用美国住宅代理,带session保持
proxy = "http://user-country-US-session-g2-sticky:pass@gate.proxyhat.com:8080"
proxies = {"http": proxy, "https": proxy}
def scrape_g2_reviews(product_slug, max_pages=5):
all_reviews = []
for page in range(1, max_pages + 1):
url = f"https://www.g2.com/products/{product_slug}/reviews?page={page}"
headers = {
"User-Agent": "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",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9",
"Accept-Language": "en-US,en;q=0.9",
"Accept-Encoding": "gzip, deflate, br",
"DNT": "1",
"Connection": "keep-alive",
"Upgrade-Insecure-Requests": "1",
}
try:
resp = requests.get(url, proxies=proxies, headers=headers, timeout=45)
if resp.status_code == 200:
soup = BeautifulSoup(resp.text, "html.parser")
# G2评论容器
review_items = soup.find_all("div", attrs={"itemprop": "review"})
for item in review_items:
review = {
"author": item.find("span", class_="author-name").get_text(strip=True) if item.find("span", class_="author-name") else "",
"rating": float(item.find("meta", attrs={"itemprop": "ratingValue"}).get("content", 0)) if item.find("meta", attrs={"itemprop": "ratingValue"}) else 0,
"title": item.find("div", class_="review-title").get_text(strip=True) if item.find("div", class_="review-title") else "",
"text": item.find("div", attrs={"itemprop": "reviewBody"}).get_text(strip=True) if item.find("div", attrs={"itemprop": "reviewBody"}) else "",
}
all_reviews.append(review)
print(f"Page {page}: {len(review_items)} reviews")
elif resp.status_code == 403:
print(f"Page {page}: Blocked (403), try rotating IP")
break
else:
print(f"Page {page}: HTTP {resp.status_code}")
except Exception as e:
print(f"Page {page}: Error - {e}")
time.sleep(3) # G2需要更长的间隔
return all_reviews
reviews = scrape_g2_reviews("slack", max_pages=3)
print(f"Total: {len(reviews)} reviews")注意G2使用粘性会话(session-g2-sticky)配合country-US,确保整个抓取过程使用同一美国IP,减少因IP跳变触发的安全检查。如果遇到403,建议切换session ID并增加间隔至5秒以上。
Google商家评论抓取:处理hl/gl地域化参数
Google商家(Google Business Profile)评论的展示受URL参数hl(界面语言)和gl(国家代码)影响。不同地区IP请求同一商家页面,可能看到不同的评论排序和语言偏好。使用国家定向的住宅代理可以确保获取一致locale的评论内容。
Google商家评论页面URL格式为:https://www.google.com/maps/place/{place_id}/reviews,或通过搜索https://www.google.com/search?q={business_name}+reviews&hl=en&gl=us获取。评论数据通常以动态加载方式呈现,需要解析页面中的protobuf或JSON响应。
import requests
import json
import re
import time
# 国家定向住宅代理 - 美国IP
proxy = "http://user-country-US-session-gb-stable:pass@gate.proxyhat.com:8080"
proxies = {"http": proxy, "https": proxy}
def scrape_google_business_reviews(place_id, max_scrolls=5):
all_reviews = []
headers = {
"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-Language": "en-US,en;q=0.9",
"Accept": "text/html,application/xhtml+xml",
}
# 使用hl=en和gl=us确保英文评论
url = f"https://www.google.com/maps/place/?q=place_id:{place_id}&hl=en&gl=us"
try:
resp = requests.get(url, proxies=proxies, headers=headers, timeout=45)
if resp.status_code == 200:
# Google Maps页面中的评论数据嵌入在页面脚本中
# 搜索类似 window.APP_INITIALIZATION_STATE 的数据块
json_matches = re.findall(
r'\[null,null,"(.*?)"\]', resp.text
)
print(f"Found {len(json_matches)} potential data blocks")
# 提取评论文本
review_pattern = re.findall(
r'"(\d)\.0","([^"]{10,500})"', resp.text
)
for rating, text in review_pattern[:50]:
all_reviews.append({
"rating": int(rating),
"text": text.replace("\\n", " ").replace("\\u0026", "&"),
})
print(f"Extracted {len(all_reviews)} reviews")
else:
print(f"HTTP {resp.status_code}")
except Exception as e:
print(f"Error: {e}")
return all_reviews
reviews = scrape_google_business_reviews("ChIJN1t_tDeuEmsRUsoyG3fr8-4")
print(f"Total: {len(reviews)} reviews")Google商家评论抓取的复杂度较高,因为Google使用动态加载和protobuf编码。对于大规模数据采集,建议结合Playwright等无头浏览器处理动态渲染,同时使用country-US住宅代理保持locale一致性。更多关于SERP抓取的场景说明,可参考SERP追踪用例页面。
Node.js示例:使用ProxyHat代理抓取评论
以下是Node.js版本的评论抓取示例,使用https-proxy-agent通过ProxyHat住宅代理请求Trustpilot页面:
const { HttpsProxyAgent } = require('https-proxy-agent');
const axios = require('axios');
async function scrapeReviews(domain, maxPages = 5) {
const allReviews = [];
for (let page = 1; page <= maxPages; page++) {
const sessionId = `node-tp-${page}`;
const proxyUrl = `http://user-session-${sessionId}:pass@gate.proxyhat.com:8080`;
const agent = new HttpsProxyAgent(proxyUrl);
const url = `https://www.trustpilot.com/review/${domain}?page=${page}`;
const headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
'Accept-Language': 'en-US,en;q=0.9',
};
try {
const resp = await axios.get(url, {
httpsAgent: agent,
headers,
timeout: 30000,
});
if (resp.status === 200) {
const match = resp.data.match(
/<script id="__NEXT_DATA__" type="application\/json">(.*?)<\/script>/s
);
if (match) {
const data = JSON.parse(match[1]);
const reviews = data.props?.pageProps?.reviews || [];
reviews.forEach(r => {
allReviews.push({
title: r.title || '',
rating: r.rating || 0,
text: r.text || '',
});
});
console.log(`Page ${page}: ${reviews.length} reviews`);
}
} else {
console.log(`Page ${page}: HTTP ${resp.status}`);
}
} catch (err) {
console.log(`Page ${page}: ${err.message}`);
}
await new Promise(r => setTimeout(r, 2500)); // 2.5s间隔
}
return allReviews;
}
scrapeReviews('example.com', 5).then(r => {
console.log(`Total: ${r.length} reviews`);
});常见错误与边界情况
1. 未设置Accept-Language导致地域偏移
即使使用美国IP,如果不设置Accept-Language: en-US,en;q=0.9头部,某些平台仍可能根据其他信号返回非英文评论。始终将语言头部与代理国家保持一致。
2. Session ID冲突导致IP不轮换
ProxyHat的session参数控制粘性:相同session ID复用同一IP,不同session ID分配新IP。如果你希望每页轮换IP,必须为每一页使用不同的session ID(如tp-page-1、tp-page-2)。如果所有页面使用相同session ID,则所有请求走同一IP——这在需要登录态一致性时有用,但在纯轮换场景下会适得其反。
3. 并发过高触发平台级封锁
即使IP轮换,如果同一目标域名的并发请求超过100次/秒,平台仍可能通过行为分析识别爬虫。建议保持每IP 2-5个请求/秒的速率,全局并发不超过20-50个请求。
4. 忽略robots.txt
在抓取前应检查目标域名的/robots.txt,确认评论页面是否允许爬取。部分平台可能通过robots.txt明确禁止抓取某些路径。更多合规细节参见robots.txt标准。
合规与道德抓取:何时应使用官方API
在投入代理抓取之前,应先评估目标平台是否提供官方API。Google提供Places API可获取商家评论(有配额限制);Trustpilot提供Business API供已认证的企业主访问评论数据。如果官方API能满足你的数据需求,优先使用API——它更稳定、合规风险更低。
当官方API不可用或数据覆盖不足时,代理抓取公开评论数据是合理替代方案,但需遵循以下原则:
- 仅采集公开数据:不绕过登录墙,不抓取需要认证才能访问的内容。
- 不采集PII:评论中的用户名、头像URL等个人信息应按需脱敏或遵循GDPR要求处理。
- 尊重速率限制:即使技术上可以更快,也应保持合理的请求频率。
- 遵守ToS:阅读平台服务条款,部分平台明确禁止自动化数据采集。
- 数据用途透明:明确数据将用于市场调研还是商业再分发,后者可能涉及更多法律约束。
关于代理定价和套餐选择,请参考ProxyHat定价页面。如需更广泛的网页抓取场景说明,可查看网页抓取用例。完整API文档请访问ProxyHat官方文档。
关键要点
- 评论平台通过IP限流、地域化和反爬机制保护数据,单一IP通常在50-200次请求后被封锁。
- 住宅代理通过IP轮换和地理定位有效绕过这些限制,成功率可达90%以上。
- Trustpilot评论嵌入在
__NEXT_DATA__JSON中,解析JSON比提取HTML更稳定。- G2反爬更激进,需要住宅IP配合真实请求头,建议使用粘性会话保持IP一致性。
- Google商家评论受
hl/gl参数影响,使用country-US住宅代理确保locale一致。- ProxyHat通过
gate.proxyhat.com:8080提供HTTP代理,session参数控制IP轮换与粘性。- 优先使用官方API;代理抓取仅限于公开数据,需遵守ToS、robots.txt和适用法律。






