pytrends实战:Python抓取Google Trends数据完全指南(附代理池与反限流)
本文是「跨境电商选品工具链」系列第一篇,介绍如何用 pytrends 库批量获取 Google Trends 周度搜索指数,并系统解决两个高频翻车场景:
timeframe格式报错和多词批量查询的归一化失真。
一、为什么要用 Google Trends 做选品?
跨境电商选品的核心问题是:这个品类的市场需求,是在涨还是在跌?
Google Trends 提供的搜索指数,是目前公开可得的、最接近真实消费需求的领先指标。对比 Amazon Best Seller 榜单(滞后),它能让你在一个品类爆发之前就发现信号。
但 Google Trends 本身只能在网页上手动查询,想要批量处理几十、几百个候选关键词,就必须借助 pytrends —— 一个非官方的 Python 封装库。
二、环境安装
pip install pytrends pandas scipy numpy
pytrends 本身没有 API Key 要求,它直接模拟浏览器请求 Google Trends 的内部 API。这也意味着它会受到 Google 的频控限制,后面会详细讲解如何处理。
三、最简用法
from pytrends.request import TrendReq
pt = TrendReq(hl='en-US', tz=360) # tz=360 对应美国中部时区
pt.build_payload(
kw_list=["ergonomic lumbar support cushion"],
timeframe="today 12-m", # 过去12个月
geo="US"
)
df = pt.interest_over_time()
print(df.head())
四、高频翻车一:timeframe 格式
错误示范:
timeframe = "today 1-y" # ❌ Google 不认识,直接返回 HTTP 400
Google Trends 内部 API 只接受以下几种格式:
| 格式 | 含义 |
|---|---|
today 12-m |
过去 12 个月(推荐,取代"1年") |
today 3-y |
过去 3 年 |
today 5-y |
过去 5 年 |
today 3-m |
过去 3 个月 |
now 7-d |
过去 7 天 |
正确写法:
def build_timeframe(years: int) -> str:
return "today 12-m" if years == 1 else f"today {years}-y"
五、高频翻车二:批量查询的归一化失真
pytrends 支持单次查询最多 5 个关键词,但有一个致命陷阱:
Google Trends 对批次内所有词做归一化,以搜索量最大的词为基准(=100),其他词按比例压缩。
举个例子:假设你把 "yoga mat" 和 "portable air purifier desk" 放在同一批次:
"yoga mat"搜索量 = 100(基准)"portable air purifier desk"真实搜索量很小,被压缩到 2-3
如果你用这个数据分析 air purifier 的增长趋势,会得到一个非常"平"的近乎水平线,斜率为 0,被误判为停滞词而剔除。
解决方案:每个词单独查询(BATCH_SIZE=1)
BATCH_SIZE = 1 # 单词独立查询,避免归一化压制
for kw in keywords:
pt = TrendReq(hl='en-US', tz=360)
pt.build_payload([kw], timeframe="today 12-m", geo="US")
df = pt.interest_over_time()
# 此时 df[kw] 的最大值 = 100,完整保留该词自身的趋势形态
代价是请求次数增加 5 倍,所以必须配合合理的休眠策略。
六、代理池与随机休眠
Google 对 pytrends 的限流策略是返回 HTTP 429 Too Many Requests。应对方案分两层:
第一层:随机休眠,让每次请求间隔看起来更像人类操作:
import random, time
SLEEP_MIN = 8.0
SLEEP_MAX = 20.0
# 每次请求后休眠随机时间
time.sleep(random.uniform(SLEEP_MIN, SLEEP_MAX))
第二层:代理池轮换
from pytrends.request import TrendReq
from typing import Optional
PROXY_LIST = [
"http://user:pass@proxy1.example.com:8080",
"http://user:pass@proxy2.example.com:8080",
]
class ProxyPool:
def __init__(self, proxies):
self._pool = proxies.copy()
self._idx = 0
def next(self) -> Optional[str]:
if not self._pool:
return None
proxy = self._pool[self._idx % len(self._pool)]
self._idx += 1
return proxy
pool = ProxyPool(PROXY_LIST)
def build_pytrend() -> TrendReq:
proxy = pool.next()
kwargs = dict(hl='en-US', tz=360, timeout=(10, 30), retries=2)
if proxy:
kwargs["proxies"] = [proxy]
return TrendReq(**kwargs)
429 时退避重试:
RETRY_MAX = 3
RETRY_SLEEP = 60.0 # 遭遇 429 后等待秒数(按次递增)
for attempt in range(1, RETRY_MAX + 1):
try:
pt = build_pytrend()
pt.build_payload([kw], timeframe="today 12-m", geo="US")
df = pt.interest_over_time()
break
except Exception as e:
if "429" in str(e):
wait = RETRY_SLEEP * attempt
print(f"Rate-limited,等待 {wait:.0f}s(第{attempt}次)…")
time.sleep(wait)
else:
raise
七、完整可复用脚本
将以上所有内容封装成 CLI 工具,支持直接传参或读取关键词文件:
# 用法示例:
# python3 trends_filter.py "lumbar cushion" "air purifier desk"
# python3 trends_filter.py --file keywords.txt --geo US --years 1
关键词文件格式(keywords.txt):
# 候选词(# 开头为注释)
portable air purifier desk
ergonomic lumbar support cushion
bamboo cutting board with juice groove
# 季节性对照组
christmas tree stand heavy duty
运行后输出:
14:22:01 INFO 共 5 个关键词,分 5 批处理(geo=US,今12月)
14:22:01 INFO 批次 [1-1]: ['portable air purifier desk']
14:22:14 INFO portable air purifier desk slope=+0.0821 seas=0.112 avg=12.3 ✅ PASS
14:22:28 INFO 批次 [2-2]: ['ergonomic lumbar support cushion']
14:22:41 INFO ergonomic lumbar support cushion slope=+0.1034 seas=0.089 avg=18.7 ✅ PASS
结果保存为 CSV + JSON,方便后续分析。
八、小结
| 问题 | 原因 | 解决 |
|---|---|---|
| HTTP 400 | today 1-y 格式不合法 |
改用 today 12-m |
| 趋势曲线异常压低 | 批量查询归一化 | BATCH_SIZE=1 单独查询 |
| HTTP 429 | Google 频控 | 随机休眠 8-20s + 代理池 + 退避重试 |
下一篇将介绍如何对抓到的时间序列做特征提取:用线性回归计算增长斜率,用功率谱密度计算季节性指数,从而自动筛掉衰退词和季节性词。
本文代码来源于开源项目,完整脚本见文末。
更多推荐
所有评论(0)