🥳加入无抓取社区领取您的免费试用,访问我们强大的网页抓取工具包!
返回博客

如何在 Pipedream 上实现 Google Trends 跟踪器?

Emily Chen
Emily Chen

Advanced Data Extraction Specialist

05-Jun-2025

在数字营销、SEO 和新兴趋势分析领域,随时掌握 Google 趋势的变化至关重要。然而,手动检查和整理关键字趋势数据既耗时又容易出错且效率低下。为了解决这个问题,我们构建了一个自动化报告系统,集成了 Pipedream、Scrapeless API 和 Discord Webhook,以简化整个过程——从关键字收集和数据处理到结果交付。

本文将详细介绍这一自动化系统的组成部分和实施过程。

主要特点

  • 自动化关键字趋势分析:使用 Scrapeless 获取过去一个月的趋势数据。

  • 智能评分与处理:自动计算每个关键字的平均受欢迎程度和趋势波动。

  • 可视化分析报告:生成结构化报告并带有可视化效果,直接发送到 Discord。


前提条件

  1. 注册 Scrapeless 并获取您的 API 密钥。
  2. 找到您的 API 令牌 并复制以备后用。
找到您的 API 令牌

⚠️ 注意: 保持您的 API 令牌安全,并不要与他人分享


  1. 登录 Pipedream
  2. 点击 “+ 新工作流” 来创建一个新的工作流。
+ 新工作流

如何创建 Google 趋势自动化报告系统(Pipedream + Scrapeless + Discord)

工作流结构

步骤名称 类型 功能
trigger 调度 根据调度触发工作流(例如,每小时)
http Node.js 代码 向 Scrapeless 提交抓取任务并获取结果
code_step Node.js 代码 解析和处理抓取的数据
discord_notify Node.js 代码 通过 webhook 将分析结果发送到 Discord

步骤 1:定时触发器

选择触发器类型为“调度”,并设置触发时间以自动执行该工作流,每周日 UTC 时间 16:00。

选择触发器类型

步骤 2 - http(请求 Scrapeless 抓取关键字趋势)

http(请求 Scrapeless 抓取关键字趋势)
  1. 添加一个 Node.js 代码步骤。以下是集成 Scrapeless API 调用逻辑的代码示例
Copy
import { axios } from "@pipedream/platform"

export default defineComponent({
  async run({ steps, $ }) {
    const keywords = ["AI", "Machine Learning", "Data Science"];
    const allResults = {};
    
    for (const keyword of keywords) {
      console.log(`正在处理关键字: ${keyword}`);
      
      const payload = {
        actor: "scraper.google.trends",
        input: {
          q: keyword,
          date: "today 1-m",
          data_type: "interest_over_time",
          hl: "en",
          tz: "420",
          geo: "",
          cat: "",
          property: "",
        },
        proxy: {
          country: "",
        }
      };

      try {
        const response = await axios($, {
          method: 'POST',
          url: 'https://api.scrapeless.com/api/v1/scraper/request',
          headers: {
            'Content-Type': 'application/json',
            'x-api-token': 'Scrapeless API KEY'
          },
          data: payload
        });
        
        console.log(`关键字 ${keyword} 的响应:`, response);
        
        if (response.job_id) {
          console.log(`为 ${keyword} 创建了任务,ID: ${response.job_id}`);
          
          let attempts = 0;
          const maxAttempts = 12;
          let jobResult = null;
          
          while (attempts < maxAttempts) {
            await new Promise(resolve => setTimeout(resolve, 10000));
            attempts++;
            
            try {
              const resultResponse = await axios($, {
                method: 'GET',
                url: `https://api.scrapeless.com/api/v1/scraper/result/${response.job_id}`,
                headers: {
                  'x-api-token': 'Scrapeless API KEY'
                }
              });
              
              console.log(`第 ${attempts} 次尝试 ${keyword}:`, resultResponse);
              
              if (resultResponse.status === 'completed') {
                jobResult = resultResponse.result;
```javascript
console.log(`工作完成,关键词为 ${keyword}:`, jobResult);
                break;
              } else if (resultResponse.status === 'failed') {
                console.error(`工作失败,关键词为 ${keyword}:`, resultResponse);
                break;
              } else {
                console.log(`工作仍在进行中,关键词为 ${keyword}, 状态: ${resultResponse.status}`);
              }
            } catch (error) {
              console.error(`检查关键词 ${keyword} 的工作状态时出错:`, error);
            }
          }
          
          if (jobResult) {
            allResults[keyword] = jobResult;
          } else {
            allResults[keyword] = { 
              error: `工作超时或在 ${attempts} 次尝试后失败`,
              job_id: response.job_id 
            };
          }
          
        } else {
          allResults[keyword] = response;
        }
        
      } catch (error) {
        console.error(`关键词 ${keyword} 出错:`, error);
        allResults[keyword] = { 
          error: `请求失败: ${error.message}` 
        };
      }
    }
    
    console.log("最终结果:", JSON.stringify(allResults, null, 2));
    return allResults;
  },
})

注意:

  • 此步骤使用 Scrapeless 的 scraper.google.trends 角色来获取每个关键词的 "interest_over_time" 数据。
  • 默认关键词为: ["AI", "机器学习", "数据科学"]。您可以根据需要替换它们。
  • YOUR_API_TOKEN 替换为您的实际 API 令牌。

步骤 3 - 代码步骤 (处理爬取的数据)

步骤 3 - 代码步骤 (处理爬取的数据)
  1. 添加另一个 Node.js 代码步骤。以下是代码示例
Copy
export default defineComponent({
  async run({ steps, $ }) {
    const httpData = steps.http.$return_value;
    
    console.log("接收到的 HTTP 数据:", JSON.stringify(httpData, null, 2));
    
    const processedData = {};
    const timestamp = new Date().toISOString();
    
    for (const [keyword, data] of Object.entries(httpData)) {
      console.log(`处理 ${keyword}:`, data);
      
      let timelineData = null;
      let isPartial = false;
      
      if (data?.interest_over_time?.timeline_data) {
        timelineData = data.interest_over_time.timeline_data;
        isPartial = data.interest_over_time.isPartial || false;
      } else if (data?.timeline_data) {
        timelineData = data.timeline_data;
      } else if (data?.interest_over_time && Array.isArray(data.interest_over_time)) {
        timelineData = data.interest_over_time;
      }
      
      if (timelineData && Array.isArray(timelineData) && timelineData.length > 0) {
        console.log(`找到 ${keyword} 的 ${timelineData.length} 个数据点`);
        
        const values = timelineData.map(item => {
          const value = item.value || item.interest || item.score || item.y || 0;
          return parseInt(value) || 0;
        });
        
        const validValues = values.filter(v => v >= 0);
        
        if (validValues.length > 0) {
          const average = Math.round(validValues.reduce((sum, val) => sum + val, 0) / validValues.length);
          const trend = validValues.length >= 2 ? validValues[validValues.length - 1] - validValues[0] : 0;
          const max = Math.max(...validValues);
          const min = Math.min(...validValues);
          
          const recentValues = validValues.slice(-7);
          const earlyValues = validValues.slice(0, 7);
          const recentAvg = recentValues.length > 0 ? recentValues.reduce((a, b) => a + b, 0) / recentValues.length : 0;
          const earlyAvg = earlyValues.length > 0 ? earlyValues.reduce((a, b) => a + b, 0) / earlyValues.length : 0;
          const weeklyTrend = Math.round(recentAvg - earlyAvg);
          
          processedData[keyword] = {
            keyword,
            average,
            trend,
            weeklyTrend,
            values: validValues,
            max,
            min,
            dataPoints: validValues.length,
            isPartial,
            timestamps: timelineData.map(item => item.time || item.date || item.timestamp),
            lastValue: validValues[validValues.length - 1],
            firstValue: validValues[0],
            volatility: Math.round(Math.sqrt(validValues.map(x => Math.pow(x - average, 2)).reduce((a, b) => a + b, 0) / validValues.length)),
            status: '成功'
          };
        } else {
          processedData[keyword] = {
            keyword,
            average: 0,
            trend: 0,
            weeklyTrend: 0,
            values: [],
            max: 0,
            min: 0,
            dataPoints: 0,
            isPartial: false,
            error: "时间线数据中未找到有效值",
            status: '错误',
            rawTimelineLength: timelineData.length
          };
        }
      } else {
        processedData[keyword] = {

关键字,
平均值:0,
趋势:0,
每周趋势:0,
值:[],
最大值:0,
最小值:0,
数据点:0,
部分数据:false,
错误: "未找到时间线数据",
状态:'错误',
可用键:数据 ? Object.keys(data) : []
};
}
}

Copy
const summary = {
  时间戳,
  总关键字:Object.keys(processedData).length,
  成功关键字:Object.values(processedData).filter(d => d.status === 'success').length,
  周期:"今天 1-m",
  数据:processedData
};

console.log("最终处理的数据:", JSON.stringify(summary, null, 2));
return summary;

},
})

Copy
---

它将根据 Scrapeless 返回的数据计算以下指标:

* **平均值**
* **每周趋势变化**(过去 7 天与前 7 天)
* **最大/最小值**
* **波动性**

**注意:**

* 每个关键字将生成一个详细分析对象,便于可视化和进一步通知。

### 第 4 步 - discord_notify(将分析报告推送到 Discord)

![步骤 4 - discord_notify(将分析报告推送到 Discord)](https://assets.scrapeless.com/prod/posts/build-google-trends-monitor-with-pipedream/f18726b4fe24fa4e2815d32624288b79.png)

1. 添加最后一个 Node.js 步骤,以下是代码示例

import { axios } from "@pipedream/platform"

export default defineComponent({
async run({ steps, $ }) {
const processedData = steps.Code_step.$return_value;

Copy
const discordWebhookUrl = "https://discord.com/api/webhooks/1380448411299614821/MXzmQ14TOPK912lWhle_7qna2VQJBjWrdCkmHjdEloHKhYXw0fpBrp-0FS4MDpDB8tGh";

console.log("处理 Discord 数据:", JSON.stringify(processedData, null, 2));

const currentDate = new Date().toLocaleString('zh-CN');

const keywords = Object.values(processedData.data);
const successful = keywords.filter(k => k.status === 'success');

let bestPerformer = null;
let worstPerformer = null;
let strongestTrend = null;

if (successful.length > 0) {
  bestPerformer = successful.reduce((max, curr) => curr.average > max.average ? curr : max);
  worstPerformer = successful.reduce((min, curr) => curr.average < min.average ? curr : min);
  strongestTrend = successful.reduce((max, curr) => Math.abs(curr.weeklyTrend) > Math.abs(max.weeklyTrend) ? curr : max);
}

const discordMessage = {
  content: `📊 **谷歌趋势报告** - ${currentDate}`,
  embeds: [
    {
      title: "🔍 趋势分析",
      color: 3447003,
      timestamp: new Date().toISOString(),
      fields: [
        {
          name: "📈 概要",
          value: `**周期:** 上个月\n**分析的关键字:** ${processedData.totalKeywords}\n**成功:** ${processedData.successfulKeywords}/${processedData.totalKeywords}`,
          inline: false
        }
      ]
    }
  ]
};

if (successful.length > 0) {
  const keywordDetails = successful.map(data => {
    const trendIcon = data.weeklyTrend > 5 ? '🚀' : 
                     data.weeklyTrend > 0 ? '📈' : 
                     data.weeklyTrend < -5 ? '📉' : 
                     data.weeklyTrend < 0 ? '⬇️' : '➡️';
    
    const performanceIcon = data.average > 70 ? '🔥' : 
                           data.average > 40 ? '✅' : 
                           data.average > 20 ? '🟡' : '🔴';
    
    return {
      name: `${performanceIcon} ${data.keyword}`,
      value: `**得分:** ${data.average}/100\n**趋势:** ${trendIcon} ${data.weeklyTrend > 0 ? '+' : ''}${data.weeklyTrend}\n**范围:** ${data.min}-${data.max}`,
      inline: true
    };
  });
  
  discordMessage.embeds[0].fields.push(...keywordDetails);
}

if (bestPerformer && successful.length > 1) {
  discordMessage.embeds.push({
    title: "🏆 关键亮点",
    color: 15844367,
    fields: [
      {
        name: "🥇 最佳表现者",
        value: `**${bestPerformer.keyword}** (${bestPerformer.average}/100)`,
        inline: true
      },
      {
        name: "📊 最低得分",
        value: `**${worstPerformer.keyword}** (${worstPerformer.average}/100)`,
        inline: true
      },
      {
        name: "⚡ 最强趋势",
        value: `**${strongestTrend.keyword}** (${strongestTrend.weeklyTrend > 0 ? '+' : ''}${strongestTrend.weeklyTrend})`,
        inline: true
      }
    ]
  });
}

const failed = keywords.filter(k => k.status === 'error');
if (failed.length > 0) {
  discordMessage.embeds.push({
    title: "❌ 错误",
    color: 15158332,
    fields: failed.map(data => ({
      name: data.keyword,
      value: data.error || "未知错误",
      inline: true
    }))
  });
zh Copy
console.log("要发送的Discord消息:", JSON.stringify(discordMessage, null, 2));

try {
  const response = await axios($, {
    method: 'POST',
    url: discordWebhookUrl,
    headers: {
      'Content-Type': 'application/json'
    },
    data: discordMessage
  });

  console.log("Discord webhook响应:", response);

  return {
    webhook_sent: true,
    platform: "discord",
    message_sent: true,
    keywords_analyzed: processedData.totalKeywords,
    successful_keywords: processedData.successfulKeywords,
    timestamp: currentDate,
    discord_response: response
  };

} catch (error) {
  console.error("Discord webhook错误:", error);

  const simpleMessage = {
    content: `📊 **Google Trends报告 - ${currentDate}**\n\n` +
            `📈 **结果 (${processedData.successfulKeywords}/${processedData.totalKeywords}):**\n` +
            successful.map(data =>
              `• **${data.keyword}**: ${data.average}/100 (${data.weeklyTrend > 0 ? '+' : ''}${data.weeklyTrend})`
            ).join('\n') +
            (failed.length > 0 ? `\n\n❌ **错误:** ${failed.map(d => d.keyword).join(', ')}` : '')
  };

  try {
    const fallbackResponse = await axios($, {
      method: 'POST',
      url: discordWebhookUrl,
      headers: {
        'Content-Type': 'application/json'
      },
      data: simpleMessage
    });

    return {
      webhook_sent: true,
      platform: "discord",
      message_sent: true,
      fallback_used: true,
      discord_response: fallbackResponse
    };

  } catch (fallbackError) {
    console.error("Discord备用错误:", fallbackError);

    return {
      webhook_sent: false,
      platform: "discord",
      error: error.message,
      fallback_error: fallbackError.message,
      data_summary: {
        keywords: processedData.totalKeywords,
        successful: processedData.successfulKeywords,
        best_performer: bestPerformer?.keyword,
        best_score: bestPerformer?.average
      }
    };
  }
},

2. 将“YOUR DISCORD WEBHOOK URL”替换为您自己的Webhook地址
3. 单击“部署”以运行您的工作流程并获取实时信息。

### 第5步 - 接收实时Google Trends信息

您可以直接在Discord中接收检查关键词的数据:

![第5步 - 接收实时Google Trends信息](https://assets.scrapeless.com/prod/posts/build-google-trends-monitor-with-pipedream/7169818a2a9ab050b812d04b40d5c480.png)

**以下是完整的链接图:**

![以下是完整的链接图](https://assets.scrapeless.com/prod/posts/build-google-trends-monitor-with-pipedream/f75d04384f0a80200a0509310fd14680.png)

> **✅ 现在可用:Scrapeless在Pipedream的官方集成**
>
> Scrapeless现在在Pipedream的集成中心正式上线!只需几次点击,您就可以直接从Pipedream工作流程中调用我们强大的Google Trends API——无需设置,无需服务器。
>
> 无论您是在构建实时仪表板、自动化市场情报,还是为自定义分析提供支持,这个集成为您提供了最快的通往生产级趋势监控的路径。
>
> 👉 立即开始构建: **pipedream.com/apps/scrapeless**
 拖放并部署您的下一个趋势驱动工作流程——今天就开始。

非常适合需要快速获得可操作性洞察的开发人员、分析师和增长团队。

## 总结
通过Pipedream、Scrapeless API和Discord的强大结合,我们构建了一个不需要人工干预、每天自动执行的Google Trends报告系统。这不仅大大提高了工作效率,还使营销决策更具数据基础。

如果您也需要构建类似的数据自动化分析系统,不妨尝试这种技术组合!

---
Scrapeless完全遵守适用法律法规,仅依据平台服务条款访问公开可用数据。此解决方案旨在用于合法的商业智能和研究目的。

在Scrapeless,我们仅访问公开可用的数据,并严格遵循适用的法律、法规和网站隐私政策。本博客中的内容仅供演示之用,不涉及任何非法或侵权活动。我们对使用本博客或第三方链接中的信息不做任何保证,并免除所有责任。在进行任何抓取活动之前,请咨询您的法律顾问,并审查目标网站的服务条款或获取必要的许可。

最受欢迎的文章

目录