iT邦幫忙

2025 iThome 鐵人賽

DAY 12
0

身為網站管理者,定期追蹤網站的效能和 SEO 表現至關重要。然而,手動檢查既耗時又容易忘記。今天,我們將利用 n8n 這個強大的自動化工具,串接 Google PageSpeed Insights API,打造一個能定時回報網站健康狀況的自動化流程,並將報告發送到 Discord,讓你輕鬆掌握網站的最新狀態

核心概念:使用 Google PageSpeed Insights API

n8n 本身沒有內建的 Lighthouse 節點,但幸運的是,Google 提供了一個名為 PageSpeed Insights API 的免費服務,其底層就是使用 Lighthouse 引擎來進行分析,我們可以透過 n8n 的 HTTP Request 節點來呼叫這個 API,取得完整的分析報告。

前置作業

流程設計藍圖

[排程觸發] -> [設定網站清單] -> [呼叫 API 檢查] -> [取得分數] -> [傳送結果]
  1. 排程觸發:設定一個固定的時間,例如每週日早上,自動啟動流程

  2. 設定網站清單:定義要檢查的一個或多個網站網址

  3. 呼叫 API 檢查:依序將清單中的網址傳送給 PageSpeed Insights API 進行分析

  4. 擷取關鍵分數:從 API 回傳的完整報告中,只抓出我們關心的效能(Performance)和 SEO 分數

  5. 彙整並傳送結果:將所有網站的分數整理成一份易讀的訊息,並發送到指定的 Discord 頻道

workflow

步驟一:設定排程觸發

  • 來到儀表板,新增一個流程「Create Workflow」

    image 2.png

  • 初始節點選擇排程「On a schedule」

    image 3.png

  • 選擇「Cron」來下指令

    image 4.png

  • 選擇一個時間,像我是設定每個禮拜日的早上 10 點

    image 5.png

步驟二:建立網站清單

  • 下個節點選擇「Edit Fields (Set)」來設定要檢查的網站清單

    image 6.png

  • 點選中間的「Add Field」

    image 7.png

  • 欄位名稱設定為「urls」,格式選擇「Array」,內容是包含網址的陣列,例如:

    ["https://www.hexschool.com", "https://5xcampus.com/"];
    

    image 8.png

步驟三:拆分陣列

  • 下個節點選擇「Split Out」

    image 9.png

  • 把上個節點的「urls」變數抓到欄位裡面做拆分

    image 10.png

步驟四:呼叫 Google API

  • 接著選擇「HTTP Request」

    image 11.png

  • 方法選擇為「GET」,URL 設定為下

    • YOUR_API_KEY 要替換為自己的金鑰
    https://www.googleapis.com/pagespeedonline/v5/runPagespeed?url={{ $json.urls }}&strategy=desktop&category=performance&category=seo&key=YOUR_API_KEY
    
  • 接著點選正上方的「Execute step」來測試跑看看,等待一段時間可以收到整包資料

    image 12.png

步驟五:擷取所需分數

  • 下個節點再選擇「Edit Fields (Set)」來抓出需要的資料

    image 13.png

  • 點選中間的「Add Field」

    image 7.png

  • 欄位名稱設定如下

    • url

      {
        {
          $json.lighthouseResult.requestedUrl;
        }
      }
      
    • performanceScore

      {
        {
          Math.round($json.lighthouseResult.categories.performance.score * 100);
        }
      }
      
    • seoScore

      {
        {
          Math.round($json.lighthouseResult.categories.seo.score * 100);
        }
      }
      

    image 14.png

步驟六:整理報告訊息

  • 下個節點選擇「Code」來整理訊息

    image 15.png

  • 程式碼如下

    let messageBody = "";
    
    // 走訪所有從上一個節點傳來的項目
    for (const item of items) {
      const url = item.json.url;
      const performance = item.json.performanceScore;
      const seo = item.json.seoScore;
    
      // 如果 messageBody 不是空的,就先加上分隔線
      if (messageBody !== "") {
        messageBody += "\n----------------------------------\n";
      }
    
      // 組合單一網站的報告內容
      messageBody += `**網站:** ${url}\n`;
      messageBody += `**效能分數 (Performance):** \`${performance}\` / 100\n`;
      messageBody += `**SEO 分數:** \`${seo}\` / 100`;
    }
    
    // 最後,回傳一個包含完整訊息的「單一」項目
    return [
      {
        json: {
          summaryMessage: messageBody,
        },
      },
    ];
    

步驟七:發送到 Discord

  • 下個節點選擇「Discord」來傳送訊息

    image 16.png

    image 17.png

  • 「Connection Type」選擇「Webhook」,憑證的串接在之前的文章有撰寫過,這邊就不重複惹

    image 18.png

  • Message 把上個節點的變數抓進來

    image 19.png

  • 回到畫布點選正下方的「Execute workflow」來測試看看

    image 20.png

  • Discord 有收到訊息代表成功啦

    image 21.png

  • 由於有設定排程,所以要記得調整設定的時區

    image 22.png

  • 選擇台灣時區

    image 23.png

  • 接著記得到最上方切換為「Active」來啟用這個流程

    image 24.png

現在,這個 n8n 流程就會像個忠實的檢查員,每週準時為你檢查網站的健康狀況,並自動回報給你

  • 最後附上這個流程的 JSON

    {
      "name": "lighthouse",
      "nodes": [
        {
          "parameters": {
            "rule": {
              "interval": [
                {
                  "field": "weeks",
                  "triggerAtHour": 10
                }
              ]
            }
          },
          "type": "n8n-nodes-base.scheduleTrigger",
          "typeVersion": 1.2,
          "position": [0, 0],
          "id": "19013ae7-392e-46bf-8c17-0cd55aa84149",
          "name": "Schedule Trigger"
        },
        {
          "parameters": {
            "url": "=https://www.googleapis.com/pagespeedonline/v5/runPagespeed?url={{ $json.urls }}&strategy=desktop&category=performance&category=seo&key=<YOUR_GOOGLE_API_KEY>",
            "options": {}
          },
          "type": "n8n-nodes-base.httpRequest",
          "typeVersion": 4.2,
          "position": [660, 0],
          "id": "d1692a12-8ebd-479e-b08e-c0eeef9cc1bf",
          "name": "HTTP Request"
        },
        {
          "parameters": {
            "fieldToSplitOut": "urls",
            "options": {}
          },
          "type": "n8n-nodes-base.splitOut",
          "typeVersion": 1,
          "position": [440, 0],
          "id": "bab43bd3-f13c-4a6c-9926-ae5ed49b8dfa",
          "name": "Split Out"
        },
        {
          "parameters": {
            "assignments": {
              "assignments": [
                {
                  "id": "6a3aea36-198c-461c-abfc-1408cc314aa6",
                  "name": "urls",
                  "value": "=[\"https://www.hexschool.com\", \"https://5xcampus.com/\"]",
                  "type": "array"
                }
              ]
            },
            "options": {}
          },
          "type": "n8n-nodes-base.set",
          "typeVersion": 3.4,
          "position": [220, 0],
          "id": "52c5e6a4-01e8-4e47-b855-c533f02b9e26",
          "name": "List"
        },
        {
          "parameters": {
            "assignments": {
              "assignments": [
                {
                  "id": "b726f03a-faa2-4210-9468-95df2db00422",
                  "name": "url",
                  "value": "={{ $json.lighthouseResult.requestedUrl }}",
                  "type": "string"
                },
                {
                  "id": "80cce080-3191-471a-b9a6-2c2b492e18b2",
                  "name": "performanceScore",
                  "value": "={{ Math.round($json.lighthouseResult.categories.performance.score * 100) }}",
                  "type": "string"
                },
                {
                  "id": "1b014eb7-3531-46d8-8344-0c0949223f8f",
                  "name": "seoScore",
                  "value": "={{ Math.round($json.lighthouseResult.categories.seo.score * 100) }}",
                  "type": "string"
                }
              ]
            },
            "options": {}
          },
          "type": "n8n-nodes-base.set",
          "typeVersion": 3.4,
          "position": [880, 0],
          "id": "f41b65ae-9237-4e43-9fb1-23d5235525bc",
          "name": "Edit Fields"
        },
        {
          "parameters": {
            "jsCode": "let messageBody = \"\";\n\n// 走訪所有從上一個節點傳來的項目\nfor (const item of items) {\n  const url = item.json.url;\n  const performance = item.json.performanceScore;\n  const seo = item.json.seoScore;\n\n  // 如果 messageBody 不是空的,就先加上分隔線\n  if (messageBody !== \"\") {\n    messageBody += \"\\n----------------------------------\\n\";\n  }\n\n  // 組合單一網站的報告內容\n  messageBody += `**網站:** ${url}\\n`;\n  messageBody += `**效能分數 (Performance):** \\`${performance}\\` / 100\\n`;\n  messageBody += `**SEO 分數:** \\`${seo}\\` / 100`;\n}\n\n// 最後,回傳一個包含完整訊息的「單一」項目\nreturn [{\n  json: {\n    summaryMessage: messageBody\n  }\n}];"
          },
          "type": "n8n-nodes-base.code",
          "typeVersion": 2,
          "position": [1100, 0],
          "id": "0d232e8b-7b96-457f-8361-f8197b2398e5",
          "name": "Code"
        },
        {
          "parameters": {
            "authentication": "webhook",
            "content": "={{ $json.summaryMessage }}",
            "options": {}
          },
          "type": "n8n-nodes-base.discord",
          "typeVersion": 2,
          "position": [1320, 0],
          "id": "1464b030-d148-4d02-a0ac-671db31a9b91",
          "name": "Discord",
          "webhookId": "<YOUR_WEBHOOK_ID>",
          "credentials": {
            "discordWebhookApi": {
              "id": "<YOUR_CREDENTIAL_ID>",
              "name": "Discord Webhook account"
            }
          }
        }
      ],
      "pinData": {},
      "connections": {
        "Schedule Trigger": {
          "main": [
            [
              {
                "node": "List",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "HTTP Request": {
          "main": [
            [
              {
                "node": "Edit Fields",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Split Out": {
          "main": [
            [
              {
                "node": "HTTP Request",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "List": {
          "main": [
            [
              {
                "node": "Split Out",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Edit Fields": {
          "main": [
            [
              {
                "node": "Code",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Code": {
          "main": [
            [
              {
                "node": "Discord",
                "type": "main",
                "index": 0
              }
            ]
          ]
        }
      },
      "active": true,
      "settings": {
        "executionOrder": "v1",
        "timezone": "Asia/Taipei",
        "callerPolicy": "workflowsFromSameOwner",
        "executionTimeout": -1
      },
      "versionId": "<WORKFLOW_VERSION_ID>",
      "meta": {
        "templateCredsSetupCompleted": true
      },
      "id": "<WORKFLOW_ID>",
      "tags": []
    }
    

上一篇
[Day11]_Code Review 提醒
下一篇
[Day13]_LeetCode 每週複習
系列文
告別重複瑣事: n8n workflow 自動化工作實踐17
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言