// SEO

NawiSEO 6 - PageSpeed Insights

▸ // rozdziały lekcji

NawiSEO 6 - Wolniś ze stoperem przy liczniku wydajności

Wchodzisz na stronę PageSpeed Insights, wklejasz adres, czekasz, spisujesz wynik. Potem przełączasz na komputer i spisujesz drugi. Następny adres. Przy 235 podstronach nikt tego nie robi, więc mierzy się stronę główną i zgaduje, że reszta wygląda podobnie.

Ten workflow bierze adresy z tabeli z serii NawiSEO, mierzy każdy dwa razy, na telefonie i na komputerze, i wpisuje oba wyniki obok adresu. Po kilku uruchomieniach widzisz w jednej tabeli, które podstrony ciągną serwis w dół.

Co robi ten workflow

  1. Uruchom recznie - start przyciskiem Execute workflow.
  2. Ustawienia - jedno pole, ile_na_raz (5).
  3. Przygotuj tabele - Data Table z operacją Create zakłada nawiseo_adresy, jeśli jej nie ma.
  4. Adresy bez pomiaru - Data Table z operacją Get bierze 5 adresów z pustą kolumną psi_mobile.
  5. PageSpeed mobile - HTTP Request do PageSpeed Insights API z strategy=mobile. Klucz API bierze z poświadczenia.
  6. Wynik mobile - Edit Fields zamienia ocenę na liczbę 0-100, a nieudany pomiar na -1.
  7. PageSpeed desktop - to samo zapytanie z strategy=desktop.
  8. Wyniki razem - Edit Fields składa adres i oba wyniki w jeden item.
  9. Zapisz wyniki - Data Table z operacją Update wpisuje psi_mobile i psi_desktop w wiersz z tym samym url.

Czego potrzebujesz

Dokumentacja API traktuje klucz jako opcjonalny. W praktyce jest wymagany. Mój test bez klucza od razu zwrócił „The service is receiving too many requests from you" i nic się nie zmierzyło.

Klucz robisz w Google Cloud Console: włączasz w projekcie PageSpeed Insights API i tworzysz klucz API.

W n8n klucz trzymasz w poświadczeniu typu Query Auth. To ogólne poświadczenie, które dopisuje do każdego zapytania jeden parametr w adresie. W polu Name wpisujesz key, w polu Value swój klucz. Poświadczenia nie trafiają do eksportu JSON, więc plik workflow możesz komuś wysłać bez wycinania klucza. Klucz i poświadczenie krok po kroku pokazuję w lekcji Credentials z kluczem API Google (wkrótce).

Jak uruchomić

  1. Zaimportuj plik z tej lekcji (jak importować workflow).
  2. W Overview, zakładka Credentials, załóż poświadczenie Query Auth: Name key, Value twój klucz.
  3. W obu węzłach PageSpeed (mobile i desktop) wybierz to poświadczenie. Authentication jest już ustawione na Generic Credential Type, typ Query Auth.
  4. Kliknij Execute workflow.
  5. Otwórz tabelę nawiseo_adresy (Overview, zakładka Data tables) i sprawdź kolumny psi_mobile i psi_desktop dla pięciu adresów.
  6. Uruchamiaj ponownie, aż wszystkie adresy mają wynik. Każde uruchomienie bierze następną piątkę, bo zmierzone adresy mają już wypełnione psi_mobile.

Poświadczenie Query Auth z polem Name ustawionym na key i zamazanym Value

Kolejka po pustej kolumnie

Węzeł Adresy bez pomiaru ma jeden warunek: psi_mobile jest puste. Limit bierze z Ustawień. Ten sam patent co w NawiSEO 2: pusta kolumna robi za kolejkę, a zapis wyniku zdejmuje adres z kolejki.

Chcesz zmierzyć wszystko od nowa po zmianach na stronie - wyczyść kolumnę psi_mobile. Chcesz powtórzyć jeden adres - wyczyść jedną komórkę.

Zapytanie do PageSpeed

Oba węzły HTTP Request wysyłają GET na https://www.googleapis.com/pagespeedonline/v5/runPagespeed z trzema parametrami:

ParametrWartość
url{{ $('Adresy bez pomiaru').item.json.url }}
strategymobile albo desktop
categoryperformance

Czwarty parametr, key, dokłada poświadczenie Query Auth. Nie widzisz go w liście parametrów i dobrze, bo nie siedzi w workflow.

Sprawdziłem, że klucz faktycznie idzie w zapytaniu: z fałszywym kluczem w poświadczeniu Google odpowiedział błędem 400 „API key not valid. Please pass a valid API key.".

Ustawienia węzła są dobrane pod wolne API:

  • Timeout 120 sekund. Pomiar PageSpeed to uruchomienie Lighthouse na serwerach Google i trwa dłużej niż zwykłe zapytanie,
  • Batching 1 zapytanie na sekundę, żeby nie wysyłać pięciu naraz,
  • Retry On Fail 3 próby co 5 sekund, bo pojedynczy pomiar potrafi się nie udać,
  • On Error ustawione na Continue. Jeśli pomiar nie wyjdzie nawet po trzech próbach, workflow nie staje, tylko idzie dalej z odpowiedzią błędu.

Węzeł PageSpeed mobile - parametry zapytania, Authentication z Query Auth i zakładka Settings z Retry On Fail oraz On Error

Skąd liczba 0-100

Odpowiedź PageSpeed to długi JSON z pełnym raportem Lighthouse. Z całości bierzemy jedno pole:

{{ $json.lighthouseResult ? Math.round($json.lighthouseResult.categories.performance.score * 100) : -1 }}

API podaje ocenę jako ułamek od 0 do 1. Mnożymy przez 100 i zaokrąglamy, więc w tabeli masz 87 zamiast 0.87, tak jak w interfejsie PageSpeed.

Pierwsza część, przed znakiem zapytania, sprawdza, czy w odpowiedzi w ogóle jest raport. Po nieudanym pomiarze go nie ma, bo węzeł PageSpeed przepuścił dalej błąd. Wtedy wyrażenie zwraca -1. W teście z fałszywym kluczem tak właśnie wyszło: w tabeli psi_mobile = -1 i psi_desktop = -1, a pozostałe adresy z piątki poszły dalej.

Wynik mobile trzeba zapamiętać, zanim pójdzie zapytanie desktop, bo ono nadpisuje $json. Dlatego jest osobny węzeł Wynik mobile, a Wyniki razem sięga do niego po nazwie: $('Wynik mobile').item.json.psi_mobile. Adres bierze z Adresy bez pomiaru. n8n pamięta, z którego itemu co powstało, więc wyniki trafiają do właściwych adresów.

Tabela nawiseo_adresy po przebiegu - adresy z damianslimak.pl i wyniki w kolumnach psi_mobile i psi_desktop, pozostałe kolumny ukryte na zrzucie

Pro-tip. Na telefonie wynik jest zwykle niższy niż na komputerze. Google ocenia strony głównie po wersji mobilnej, więc sortuj tabelę po psi_mobile, nie po psi_desktop. Sam desktop potrafi uśpić czujność.

Haczyk. Adres z -1 nie wraca do kolejki, bo psi_mobile nie jest już puste. Traktuj -1 jako punkt do sprawdzenia, nie jako wynik. Posortuj tabelę po psi_mobile rosnąco, -1 będą na górze. Otwórz taki adres w przeglądarce albo w historii wykonań zobacz, co odpowiedział Google. Żeby zmierzyć go ponownie, wyczyść komórkę psi_mobile. Jeśli -1 masz przy wszystkich adresach naraz, winny jest raczej klucz niż strona.

Co sprawdziłem, a czego nie

Bez klucza API dostałem odmowę. Z fałszywym kluczem w Query Auth dostałem błąd 400 i zapis -1 w obu kolumnach, więc poświadczenie i obsługa błędu działają. Z prawdziwym kluczem tego workflow nie testowałem, więc pełnego przebiegu z prawdziwymi pomiarami nie zrobiłem. Przeliczenie oceny, złożenie wyników i update po url sprawdziłem na przykładowej odpowiedzi PageSpeed. Jeśli po wklejeniu klucza coś nie zadziała, pierwsze miejsce do sprawdzenia to odpowiedź węzła PageSpeed mobile.

Kiedy tego nie używać

Do diagnozy, co konkretnie spowalnia stronę. Tabela dostaje sam wynik, bez listy zaleceń. Po szczegóły wejdziesz do raportu w przeglądarce, ale już wiesz, które adresy otworzyć najpierw.

Podsumowanie

Pusta kolumna psi_mobile robi za kolejkę, każde uruchomienie mierzy pięć adresów na telefonie i komputerze, a wynik 0-100 ląduje obok adresu. Nieudany pomiar zapisuje się jako -1 i nie blokuje reszty. Klucz API siedzi w poświadczeniu Query Auth, więc nie wycieka z eksportem.

W ostatniej lekcji serii, NawiSEO X1 - nowe meta tagi z AI (wkrótce), model Gemini przygotuje propozycje nowego title, description i H1 dla podstron z tabeli.

Pokaż JSON do skopiowania - nawiseo-pagespeed-insights.json
{
  "name": "NawiSEO 6 - PageSpeed Insights do tabeli",
  "nodes": [
    {
      "parameters": {},
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [
        0,
        0
      ],
      "id": "8fd8660d-7cba-a01e-49c3-96d4c62f0832",
      "name": "Uruchom recznie"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "ac00a6d3-ea99-8faf-5a6d-106e4fe95fc2",
              "name": "ile_na_raz",
              "value": "5",
              "type": "number"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        220,
        0
      ],
      "id": "6f9fddd0-9c4f-f3e0-6405-de43005261fd",
      "name": "Ustawienia"
    },
    {
      "parameters": {
        "resource": "table",
        "operation": "create",
        "tableName": "nawiseo_adresy",
        "columns": {
          "column": [
            {
              "name": "url",
              "type": "string"
            },
            {
              "name": "domena",
              "type": "string"
            },
            {
              "name": "sitemap",
              "type": "string"
            },
            {
              "name": "status_http",
              "type": "number"
            },
            {
              "name": "title",
              "type": "string"
            },
            {
              "name": "title_dl",
              "type": "number"
            },
            {
              "name": "description",
              "type": "string"
            },
            {
              "name": "description_dl",
              "type": "number"
            },
            {
              "name": "h1",
              "type": "string"
            },
            {
              "name": "h1_ilosc",
              "type": "number"
            },
            {
              "name": "sprawdzono",
              "type": "date"
            },
            {
              "name": "nowy_title",
              "type": "string"
            },
            {
              "name": "nowy_description",
              "type": "string"
            },
            {
              "name": "nowy_h1",
              "type": "string"
            },
            {
              "name": "gsc_status",
              "type": "string"
            },
            {
              "name": "gsc_ostatni_crawl",
              "type": "string"
            },
            {
              "name": "psi_mobile",
              "type": "number"
            },
            {
              "name": "psi_desktop",
              "type": "number"
            }
          ]
        },
        "options": {
          "createIfNotExists": true
        }
      },
      "type": "n8n-nodes-base.dataTable",
      "typeVersion": 1.1,
      "position": [
        440,
        0
      ],
      "id": "71b469f6-aaf8-86df-92c1-fbb2e4332f63",
      "name": "Przygotuj tabele"
    },
    {
      "parameters": {
        "operation": "get",
        "dataTableId": {
          "__rl": true,
          "value": "nawiseo_adresy",
          "mode": "name"
        },
        "returnAll": false,
        "matchType": "allConditions",
        "filters": {
          "conditions": [
            {
              "keyName": "psi_mobile",
              "condition": "isEmpty"
            }
          ]
        },
        "limit": "={{ $('Ustawienia').item.json.ile_na_raz }}"
      },
      "type": "n8n-nodes-base.dataTable",
      "typeVersion": 1.1,
      "position": [
        660,
        0
      ],
      "id": "463c7636-f606-78af-a8a5-30eb0778e857",
      "name": "Adresy bez pomiaru"
    },
    {
      "parameters": {
        "url": "https://www.googleapis.com/pagespeedonline/v5/runPagespeed",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpQueryAuth",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "url",
              "value": "={{ $('Adresy bez pomiaru').item.json.url }}"
            },
            {
              "name": "strategy",
              "value": "mobile"
            },
            {
              "name": "category",
              "value": "performance"
            }
          ]
        },
        "options": {
          "batching": {
            "batch": {
              "batchSize": 1,
              "batchInterval": 1000
            }
          },
          "timeout": 120000
        }
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        880,
        0
      ],
      "id": "26688109-f72f-4bc7-0523-75cba6c3c2f5",
      "name": "PageSpeed mobile",
      "retryOnFail": true,
      "maxTries": 3,
      "waitBetweenTries": 5000,
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "41eb73f5-64a0-7fc7-5e32-17899f73ac46",
              "name": "psi_mobile",
              "value": "={{ $json.lighthouseResult ? Math.round($json.lighthouseResult.categories.performance.score * 100) : -1 }}",
              "type": "number"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1100,
        0
      ],
      "id": "0f8cc0e9-1fa0-2fa3-974e-018ceee7bc1a",
      "name": "Wynik mobile"
    },
    {
      "parameters": {
        "url": "https://www.googleapis.com/pagespeedonline/v5/runPagespeed",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpQueryAuth",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "url",
              "value": "={{ $('Adresy bez pomiaru').item.json.url }}"
            },
            {
              "name": "strategy",
              "value": "desktop"
            },
            {
              "name": "category",
              "value": "performance"
            }
          ]
        },
        "options": {
          "batching": {
            "batch": {
              "batchSize": 1,
              "batchInterval": 1000
            }
          },
          "timeout": 120000
        }
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        1320,
        0
      ],
      "id": "66c52093-533e-1826-8f01-d541fb5cb3b6",
      "name": "PageSpeed desktop",
      "retryOnFail": true,
      "maxTries": 3,
      "waitBetweenTries": 5000,
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "8dad5b4b-7969-942c-8246-f50fc2399ef8",
              "name": "url",
              "value": "={{ $('Adresy bez pomiaru').item.json.url }}",
              "type": "string"
            },
            {
              "id": "d5cbc4f6-66cf-93f6-06d2-58360e19eda6",
              "name": "psi_mobile",
              "value": "={{ $('Wynik mobile').item.json.psi_mobile }}",
              "type": "number"
            },
            {
              "id": "623b5cd8-1997-ab41-c80c-e37ccb71aa8b",
              "name": "psi_desktop",
              "value": "={{ $json.lighthouseResult ? Math.round($json.lighthouseResult.categories.performance.score * 100) : -1 }}",
              "type": "number"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1540,
        0
      ],
      "id": "31c59081-3012-9bd8-4c6c-af4f78953a8b",
      "name": "Wyniki razem"
    },
    {
      "parameters": {
        "operation": "update",
        "dataTableId": {
          "__rl": true,
          "value": "nawiseo_adresy",
          "mode": "name"
        },
        "matchType": "allConditions",
        "filters": {
          "conditions": [
            {
              "keyName": "url",
              "condition": "eq",
              "keyValue": "={{ $json.url }}"
            }
          ]
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "psi_mobile": "={{ $json.psi_mobile }}",
            "psi_desktop": "={{ $json.psi_desktop }}"
          },
          "matchingColumns": [],
          "schema": [
            {
              "id": "url",
              "displayName": "url",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": true
            },
            {
              "id": "domena",
              "displayName": "domena",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": true
            },
            {
              "id": "sitemap",
              "displayName": "sitemap",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": true
            },
            {
              "id": "status_http",
              "displayName": "status_http",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "number",
              "readOnly": false,
              "removed": true
            },
            {
              "id": "title",
              "displayName": "title",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": true
            },
            {
              "id": "title_dl",
              "displayName": "title_dl",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "number",
              "readOnly": false,
              "removed": true
            },
            {
              "id": "description",
              "displayName": "description",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": true
            },
            {
              "id": "description_dl",
              "displayName": "description_dl",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "number",
              "readOnly": false,
              "removed": true
            },
            {
              "id": "h1",
              "displayName": "h1",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": true
            },
            {
              "id": "h1_ilosc",
              "displayName": "h1_ilosc",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "number",
              "readOnly": false,
              "removed": true
            },
            {
              "id": "sprawdzono",
              "displayName": "sprawdzono",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "date",
              "readOnly": false,
              "removed": true
            },
            {
              "id": "nowy_title",
              "displayName": "nowy_title",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": true
            },
            {
              "id": "nowy_description",
              "displayName": "nowy_description",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": true
            },
            {
              "id": "nowy_h1",
              "displayName": "nowy_h1",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": true
            },
            {
              "id": "gsc_status",
              "displayName": "gsc_status",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": true
            },
            {
              "id": "gsc_ostatni_crawl",
              "displayName": "gsc_ostatni_crawl",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": true
            },
            {
              "id": "psi_mobile",
              "displayName": "psi_mobile",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "number",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "psi_desktop",
              "displayName": "psi_desktop",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "number",
              "readOnly": false,
              "removed": false
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.dataTable",
      "typeVersion": 1.1,
      "position": [
        1760,
        0
      ],
      "id": "98c891cb-f1dd-6dd8-8652-e2dd3f64200e",
      "name": "Zapisz wyniki"
    }
  ],
  "connections": {
    "Uruchom recznie": {
      "main": [
        [
          {
            "node": "Ustawienia",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Ustawienia": {
      "main": [
        [
          {
            "node": "Przygotuj tabele",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Przygotuj tabele": {
      "main": [
        [
          {
            "node": "Adresy bez pomiaru",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Adresy bez pomiaru": {
      "main": [
        [
          {
            "node": "PageSpeed mobile",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "PageSpeed mobile": {
      "main": [
        [
          {
            "node": "Wynik mobile",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wynik mobile": {
      "main": [
        [
          {
            "node": "PageSpeed desktop",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "PageSpeed desktop": {
      "main": [
        [
          {
            "node": "Wyniki razem",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wyniki razem": {
      "main": [
        [
          {
            "node": "Zapisz wyniki",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "active": false
}