关于 TrendFlow
TrendFlow 是用于查询和导出 Google Trends 数据的 JavaScript/TypeScript 库,并提供配套 MCP 服务,供 Agent 研究搜索趋势。它支持地区与时间维度的热度、相关查询、主题和实时趋势。
主要功能
- 01按时间和地区查询热度
- 02查询相关词与主题建议
- 03查看趋势搜索的增长率和搜索量
- 04导出 CSV 和 JSON
- 05轮换代理池
适用场景
- 01比较不同地区和时段的搜索热度
- 02查找某个主题的相关查询
- 03跟踪增长迅速和搜索量高的词条
MCP 相关
独立的 `trendflow-mcp` 包提供六种工具,涵盖主题搜索、热度数据、相关查询、趋势搜索和趋势研究,并提供两个资源。
配置
远程服务无需本地安装。选择你使用的客户端,复制配置或使用一键安装。
https://trendflow.mory.dev/mcpclaude mcp add --transport http dev-mory-trendflow https://trendflow.mory.dev/mcp{"mcpServers":{"dev-mory-trendflow":{"type":"http","url":"https://trendflow.mory.dev/mcp"}}}在 Cursor 中打开{"servers":{"dev-mory-trendflow":{"type":"http","url":"https://trendflow.mory.dev/mcp"}}}在 VS Code 中打开{"mcpServers":{"dev-mory-trendflow":{"type":"http","url":"https://trendflow.mory.dev/mcp"}}}{"mcpServers":{"dev-mory-trendflow":{"serverUrl":"https://trendflow.mory.dev/mcp"}}}命令在终端中运行;JSON 粘贴到对应客户端的 MCP 配置后重启。需要授权的服务会在首次连接时提示登录。
工具
8| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| country | any | 否 | 国家/地区代码,例如 "US"、"GB"、"TH";下级区域,例如 "US-CA";或美国都市圈代码,例如 "807"。空字符串表示全球。 |
详细介绍
Trendflow JS
一个类型安全的 JavaScript/TypeScript 库,用于查询和导出 Google Trends 数据。
trendflow-py 的 JavaScript 移植版。
📖 文档:trendflow.mory.dev/docs/js — 两个库的使用指南, 以及一个可供 ChatGPT、Claude 和 Cursor 使用的托管 MCP 服务。
- GitHub:https://github.com/dariomory/trendflow-js/
- npm 包:https://www.npmjs.com/package/trendflow
- API 参考:https://dariomory.github.io/trendflow-js/
- Python 同系列项目:https://pypi.org/project/trendflow-py/
- 创建者:Dario Mory | GitHub https://github.com/dariomory
- 免费软件:MIT 许可证
安装
npm install trendflow
需要 Node.js 18+(使用全局 fetch)。提供 ESM 和 CommonJS,并附带捆绑的类型声明。
用法
import { Client, Region, Timeframe, Resolution, SearchProperty, ExportFormat } from "trendflow";
// Initialize client (optional config)
const tf = new Client({ language: "en", timeout: 10_000 });
// --- Const objects for type safety ---
// Region.US, Region.GB, Region.DE ... (or any code: "US-CA", "807")
// Timeframe.PAST_HOUR ... PAST_5_YEARS, ALL_TIME (or "2023-01-01 2023-06-30")
// Resolution.COUNTRY, Resolution.REGION, Resolution.CITY
// SearchProperty.WEB, IMAGES, NEWS, YOUTUBE, SHOPPING
// Fetch interest over time
const data = await tf.interestOverTime(
["Python", "JavaScript", "Rust"],
Timeframe.PAST_YEAR,
Region.US,
);
console.log(data.keywords); // ["Python", "JavaScript", "Rust"]
console.log(data.granularity); // "weekly"
console.log(data.points); // TrendPoint[] — { date: Date, scores: Record<string, number> }
// Regional breakdown (region defaults to Region.US)
const regional = await tf.interestByRegion("Python", Resolution.COUNTRY);
for (const row of regional.rows) {
console.log(row.label, row.value);
}
// Trending searches right now (any country code, or omit for worldwide)
const trending = await tf.trendingNow(Region.US);
for (const item of trending.results) {
console.log(item.title, item.growth, item.volume, item.traffic);
// "fifa world cup 2026" 3650 6 "+3,650%"
}
// Related queries (region defaults to worldwide)
const related = await tf.relatedQueries("machine learning", { region: Region.GB });
for (const query of related.top) console.log(query.term, query.value);
for (const query of related.rising) console.log(query.term, query.breakout);
// --- Narrowing a query ---
// Every query method takes an optional category and search property, and any of them
// accepts a custom date range and a sub-region or metro code in place of the named values.
// "jaguar" the car, on YouTube, in California, over the first half of 2023
const jaguar = await tf.interestOverTime(["jaguar"], "2023-01-01 2023-06-30", "US-CA", {
category: 47, // Autos & Vehicles — disambiguates without needing a topic id
searchProperty: SearchProperty.YOUTUBE,
});
// --- Exports ---
data.toArray(); // [{ date: Date, Python: 80, ... }] — plain objects, the JS answer to DataFrames
data.toJSON(); // same rows with ISO 8601 date strings (also drives JSON.stringify)
data.toCSV(); // CSV text
// Node.js only — writes UTF-8 to disk
await data.export(ExportFormat.CSV, "trends.csv");
await data.export(ExportFormat.JSON, "trends.json");
错误
请求失败时会抛出 ResponseError;HTTP 429 时则会抛出其子类 TooManyRequestsError。
两者都包含 .status 和原始 .response。
import { TooManyRequestsError } from "trendflow";
try {
await tf.interestOverTime(["Python"], Timeframe.PAST_YEAR, Region.US);
} catch (error) {
if (error instanceof TooManyRequestsError) {
// Google is rate-limiting this IP — back off and retry later.
}
}
热门趋势后端:RPC 和 RSS
Google 通过两种方式公开热门搜索趋势。两者不可互换,因此可以通过 backend 进行选择:
"rpc"(batchexecute) | "rss"(信息流) | |
|---|---|---|
| 条目数 | 50 | 10 |
| 数据载荷 | 约 2 KB JSON | 约 21 KB XML |
| 增长率和搜索量 | ✅ | ❌ — 以 "2000+" 等区间表示 |
| 新闻文章 | ❌ | ✅ |
window 选择 | ✅ | Google 会忽略 |
| 全球范围 | ✅ | ❌ 仅限国家/地区 |
const rss = await tf.trendingNow(Region.US, { backend: "rss" });
rss.source; // "rss"
rss.results[0].articles;
// [{ title: "...", url: "https://...", source: "Buffalo News", picture: "https://..." }]
"auto"(默认值)会先尝试 RPC,失败时再回退到信息流。特意将 RPC 放在首位:
它返回的条目数是信息流的五倍,并提供实际增长数据;因此,默认使用 RSS 会悄然降低结果质量。
需要文章时,请选用 "rss" —— 这是 RPC 唯一无法提供的内容;如果 RPC ID 过期,也可以用它作为第二种参考。
请注意,信息流虽然是信息流,但并不是更轻量的路径;Google 还会忽略其上的 hours、
sort 和 count:它始终返回相同的 10 条记录。
主题和搜索建议
Google 区分搜索词(字面字符串)和主题(实体,涵盖所有拼写和语言)。
suggestions() 用于查找主题;所有查询方法都已支持主题——在通常传入关键字的位置传入 mid 即可。
const topics = await tf.suggestions("artificial intelligence");
// [{ mid: "/m/0mkz", title: "Artificial intelligence", type: "Professional field" }]
const data = await tf.interestOverTime(
[topics[0].mid, "artificial intelligence"],
Timeframe.PAST_YEAR,
Region.US,
);
// { "/m/0mkz": 62, "artificial intelligence": 1 }
差距正是关键:主题的得分为 62,而字面短语的得分为 1,因为主题会汇总人们实际搜索的各种说法和翻译。
suggestions() 无需 Cookie 或代理——与 trendingNow() 一样,它可以通过那些会被 widgetdata 端点以 429 拒绝的 IP 进行响应。type 用于区分同名实体("Nike" 会同时返回公司和女神),如果 Google 未提供该值,则为 null。
速率限制
Google Trends 会严格限制数据中心和共享 IP 的访问,因此即使当天只发送了第一个请求,也经常会遇到 429。有两点需要注意:
- User-Agent。 无论请求次数有多么少,Google 都会对 Node HTTP 客户端发送的默认 Agent 字符串返回
429。正因如此,该库默认使用浏览器的 User-Agent——如果你覆盖了headers,请保留一个看起来真实的值。 - IP 信誉。 一旦某个 IP 被标记,无论请求头是什么,所有请求都会收到
429。通过住宅代理转发请求即可恢复。
使用代理池
传入代理 URL 列表,客户端会自动轮换使用;每当查询遭拒时,就切换到下一个代理:
import { Client, Region, Timeframe } from "trendflow";
const tf = new Client({
proxies: [
"http://user:[email protected]:7000",
"http://user:[email protected]:7000",
],
maxProxyAttempts: 3, // defaults to the pool size, capped at 5
onProxyRotate: ({ attempt, error }) => console.warn(`rotated after ${attempt}:`, error),
});
const data = await tf.interestOverTime(["Python"], Timeframe.PAST_YEAR, Region.US);
console.log(tf.currentProxy); // the proxy that answered
代理支持需要 undici,这是一个可选的 peer 依赖——npm install undici。列表项都是 URL,因此代理池可以混用不同提供商的代理。重复使用同一个轮换网关也可行:每个列表项都会建立独立连接,因此会分配到新的出口 IP。
轮换按查询进行,而不是按请求进行——这一点很重要。 Google 会将 NID Cookie 和 widget 令牌绑定到发起请求的 IP,因此单次查询必须使用同一个出口 IP 完成;如果后续的 widgetdata 调用使用了不同 IP,就会立即收到 429。代理池会为整个查询固定使用一个代理,仅在失败时才切换,并且每次都会重新初始化 Cookie 存储。出于同样的原因,如果提供商支持,请选择粘性会话,而不是按请求轮换的端点。
对于其他 IP 无法解决的错误,例如 404,或 Google 重命名 batchexecute RPC ID 时引发的 UnknownRpcError,不会进行轮换。
代理获取渠道
住宅代理才能真正解决 Google 的 429。以下代理已通过该库验证:
| 提供商 | 备注 | 端点格式 |
|---|---|---|
| Decodo(前身为 Smartproxy) | 入门套餐价格最低;支持按使用量付费。该库的实时测试使用此服务进行验证。 | http://user:[email protected]:7000 |
const tf = new Client({
proxies: ["http://user:[email protected]:7000"],
});
注册时请申请粘性会话——按请求轮换的端点会破坏上文所述的 Cookie/令牌绑定。请注意,共享住宅代理池可能专门用于 Google Trends 的 IP 已耗尽,此时即使是有效代理也会返回 429;这正是 maxProxyAttempts 的用途。
使用自己的客户端
如需日志记录、缓存或自定义路由,可改为传入 fetch(不能与 proxies 同时使用——如果两者都传入,该库会提示你):
import { ProxyAgent, fetch as undiciFetch } from "undici";
const agent = new ProxyAgent("http://user:[email protected]:7000");
const tf = new Client({
fetch: ((input, init = {}) =>
undiciFetch(input, { ...init, dispatcher: agent })) as typeof globalThis.fetch,
});
浏览器 / Next.js
除 export() 外,所有方法都可在支持 fetch 的环境中运行,但 Google Trends 不会发送 CORS 请求头——来自浏览器 JavaScript 的调用会被阻止。请在服务端使用该库(Route Handlers、Server Actions、API routes),并将结果传给客户端。
MCP 服务
该库附带 MCP 服务 trendflow-mcp,供 Agent 直接查询 Google Trends。它是一个独立的软件包——该库本身保持零运行时依赖。
claude mcp add trendflow -- npx -y trendflow-mcp
包含六个工具(search_topics、get_interest_over_time、get_interest_by_region、get_related_queries、get_trending_now、research_trend)和两个资源。请参阅 mcp/README.md。
功能对等
当前版本:trendflow-py 0.2.0 · trendflow 0.1.0。两个版本线相互独立;各自的变更日志都会交叉引用另一个版本线的发布。
| 功能 | Python — trendflow-py | JS — trendflow |
|---|---|---|
| 随时间变化的搜索热度 | ✅ | ✅ |
| 按地区查看搜索热度 | ✅ | ✅ |
| 当前热门趋势 | ✅ | ✅ |
| 趋势增长百分比和搜索量 | ✅ | ✅ |
| 支持任意国家/地区代码的趋势 | ✅ | ✅ |
| 热门趋势新闻文章(RSS) | ✅ | ✅ |
| 可选的趋势后端 | ✅ | ✅ |
| 相关查询 | ✅ | ✅ |
| 搜索建议 | ✅ suggestions() | ✅ suggestions() |
| 按主题(实体 mid)查询 | ✅ | ✅ |
| 导出 CSV / JSON | ✅ | ✅ |
| 轮换代理池 | ✅ | ✅ |
| 默认使用浏览器 User-Agent | ✅ | ✅ |
| 完整地理层级 | ✅ geo_list() | ✅ geoList() |
| 可覆盖 RPC ID | ✅ | ✅ |
| pandas DataFrame | ✅ to_dataframe() | ❌ 不适用 |
| 普通对象行 | ❌ 不适用 | ✅ toArray() |
| ESM + CommonJS + 类型 | ❌ 不适用 | ✅ |
| MCP 服务 | 🔜 计划中 | ✅ trendflow-mcp |
| CLI | ✅ | 🔜 计划中 |
当前热门趋势
Google 已弃用 hottrends/visualize/internal/data 端点,以及
api/dailytrends 和 api/realtimetrends;这三个端点现在都会返回 HTTP 404。本库改为
调用 trends.google.com 自身使用的 batchexecute RPC——trendflow-py 从 0.2.0 版本起也采用此方式——返回的数据
比旧端点更多:
const trending = await tf.trendingNow(Region.US);
// { title: "fifa world cup 2026", growth: 3650, volume: 6, traffic: "+3,650%", articles: [] }
与旧端点相比,有三个实际优势:
- 包含增长幅度和搜索量,不只是标题。
growth是该时间窗口内的涨幅百分比,volume是相对搜索量指数。 - 支持任意国家/地区代码,而非旧端点要求的 16 个硬编码名称;还支持全球范围, 旧端点不支持。
- 无需 Cookie,且速率限制宽松得多。 即使来自会被 widgetdata 端点返回
429的 IP,此 RPC 也能响应,因此trendingNow()通常无需代理即可使用。
此后端的 articles 为空,因为该 RPC 不包含文章链接。传入
{ backend: "rss" },即可获取每个趋势对应的新闻文章。
可通过 TrendingWindow 选择时间窗口:
import { TrendingWindow } from "trendflow";
await tf.trendingNow(Region.US, { window: TrendingWindow.RISING }); // default: fastest-growing
await tf.trendingNow(Region.US, { window: TrendingWindow.TOP }); // highest-volume
window 是 Google 未公开文档的参数。只有以下两个值值得命名;介于 4 和 12 之间的其他整数
也会返回数据,所覆盖的时间范围各不相同,你可以直接传入原始数字。
尚未实现:需要验证码的 RPC
同一个 batchexecute 端点还提供更高精度的时间序列(浮点值,而非公开 API 返回的四舍五入后的 0–100 值)
和关键词范围的相关查询。这两项功能都需要 reCAPTCHA Enterprise 令牌,没有令牌时会返回空数据,
因此本库未实现这些功能——仍可通过 interestOverTime() 和 relatedQueries() 获取相应数据,
这两个方法使用有文档说明的 widgetdata 端点。
API 对照
| Python | JavaScript |
|---|---|
interest_over_time() | interestOverTime() |
interest_by_region() | interestByRegion() |
trending_now() | trendingNow() |
related_queries() | relatedQueries() |
to_dataframe() | toArray() |
export(fmt, path) | export(fmt, path) — 仅 Node,另有 toCSV() / toJSON() |
显著差异:
- 所有操作都是异步的。 四个查询方法都会返回 Promise。
timeout的单位是毫秒(JS 惯例),而不是秒。- 枚举是
as const对象,因此Region.US是字符串"US";在需要该类型的地方,也接受任何有效的 字符串字面量。 - 结果是带类型的普通对象。 只有
InterestOverTimeResult是类,因为它提供了转换方法;其余均为接口。
开发
git clone [email protected]:dariomory/trendflow-js.git
cd trendflow-js
npm install
npm test # vitest — 60 tests, fully offline against a stubbed fetch
npm run qa # typecheck + test + build
单元测试不会访问网络。要检查真实端点:
npm run build && npm run smoke
TRENDFLOW_PROXY_URL=http://user:pass@host:7000 npm run smoke # via a proxy
作者
Trendflow JS 由 Dario Mory 于 2026 年创建。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。