// SEO

NawiSEO 4 - analiza miesiąca w GSC

▸ // rozdziały lekcji

NawiSEO 4 - Wolniś zrywa kartkę z kalendarza obok wykresu kliknięć

Raport miesięczny z Search Console wygląda zwykle tak samo. Ustawiasz zakres dat, przełączasz na strony, eksportujesz CSV, wklejasz do arkusza i przez kwadrans dopasowujesz wiersze. Co miesiąc. Po trzecim razie przestaje się to dziać, bo nikt tego nie lubi.

Ten workflow robi to jednym kliknięciem. Liczy sam zakres dat poprzedniego pełnego miesiąca, pobiera kliknięcia, wyświetlenia, CTR i średnią pozycję każdej podstrony i zapisuje je w tabeli. Każdy miesiąc osobno, więc po pół roku masz historię, a nie tylko ostatni stan.

To czwarta lekcja serii NawiSEO. Używa tego samego poświadczenia Google co NawiSEO 3.

Co robi ten workflow

  1. Uruchom recznie - start kliknięciem.
  2. Ustawienia - właściwość w Search Console i trzy pola liczone wyrażeniami: od, do i miesiac.
  3. Przygotuj tabele - zakłada tabelę nawiseo_gsc_miesiac albo używa istniejącej (opcja Reuse Existing Tables).
  4. Dane z Search Console - HTTP Request wysyła jedno zapytanie do Search Analytics z podziałem na strony.
  5. Rozbij wiersze - Split Out zamienia listę rows z odpowiedzi na osobne items, jedna podstrona to jeden item.
  6. Przygotuj wiersz - Edit Fields porządkuje pola i zaokrągla liczby.
  7. Zapisz miesiac - upsert po url i miesiac.

Kanwa workflow, siedem węzłów w jednej linii

Zanim zaczniesz: co tu jest sprawdzone

Workflow testowałem na przykładowej odpowiedzi API. Na żywym koncie Google tego nie sprawdzałem. Daty, rozbicie wierszy, zaokrąglenia i upsert działają. Połączenie z Google i format prawdziwej odpowiedzi opisuję według dokumentacji.

Czego potrzebujesz

  • poświadczenia Google OAuth2 API ze scope https://www.googleapis.com/auth/webmasters.readonly i włączonego Search Console API w Google Cloud. Jeśli przerobiłeś NawiSEO 3, masz je już gotowe,
  • dostępu do właściwości w Search Console na tym samym koncie Google.

Konfigurację Google krok po kroku pokazuję w lekcji Credentials Google OAuth2 na komputerze.

Tabela nawiseo_adresy nie jest tu potrzebna. Ten workflow zapisuje do osobnej tabeli, bo trzyma jeden wiersz na adres i miesiąc, a nie jeden wiersz na adres.

Jak uruchomić

  1. Zaimportuj plik z tej lekcji.
  2. W węźle Ustawienia wpisz w wlasciwosc swoją właściwość, dokładnie tak jak w Search Console: https://twojadomena.pl/ z ukośnikiem albo sc-domain:twojadomena.pl.
  3. W węźle Dane z Search Console wybierz swoje poświadczenie Google OAuth2 API.
  4. Kliknij Execute workflow.
  5. Otwórz tabelę nawiseo_gsc_miesiac (Overview > zakładka Data tables).

Tabela ma kolumny: miesiac, url, klikniecia, wyswietlenia, ctr_proc i pozycja.

Tabela nawiseo_gsc_miesiac - sierpień 2026 dla damianslimak.pl, kliknięcia, wyświetlenia, CTR i pozycja dla każdego adresu

Daty, które liczą się same

Pola w Ustawieniach wyliczają zakres poprzedniego pełnego miesiąca:

{{ $now.minus({months: 1}).startOf('month').toFormat('yyyy-MM-dd') }}
{{ $now.minus({months: 1}).endOf('month').toFormat('yyyy-MM-dd') }}
{{ $now.minus({months: 1}).toFormat('yyyy-MM') }}

$now.minus({months: 1}) cofa się o miesiąc. startOf('month') przeskakuje na pierwszy dzień tego miesiąca, endOf('month') na ostatni. toFormat('yyyy-MM-dd') daje format, którego oczekuje API. Trzecie pole to etykieta miesiąca w tabeli, na przykład 2026-08.

Nie liczysz, ile dni ma luty, i nie poprawiasz niczego w styczniu. Cofnięcie o miesiąc w styczniu trafia w grudzień poprzedniego roku.

Pro-tip. Chcesz dane za inny miesiąc, na przykład żeby uzupełnić historię wstecz? Zmień months: 1 na months: 2 we wszystkich trzech polach. Upsert dopisze ten miesiąc obok pozostałych.

Zapytanie do Search Analytics

Adres zapytania ma w środku właściwość:

https://www.googleapis.com/webmasters/v3/sites/{{ encodeURIComponent($('Ustawienia').item.json.wlasciwosc) }}/searchAnalytics/query

encodeURIComponent jest tu konieczny. Właściwość zawiera dwukropek i ukośniki, a te znaki w środku adresu API rozwaliłyby ścieżkę. Po zakodowaniu https://damianslimak.pl/ zamienia się w ciąg bez ukośników.

Treść zapytania:

{{ JSON.stringify({ startDate: $('Ustawienia').item.json.od, endDate: $('Ustawienia').item.json.do, dimensions: ['page'], rowLimit: 25000 }) }}

Wymiar page rozbija wyniki na podstrony. rowLimit ustawiony na 25000 oznacza, że jedno zapytanie zwróci najwyżej 25 tysięcy adresów. Jeśli masz ich więcej, reszta nie trafi do tabeli.

Węzeł Dane z Search Console, metoda POST, adres z wyrażeniem i treść JSON

Porządkowanie liczb

Odpowiedź API ma listę rows. Każdy wiersz to adres w keys[0] i cztery liczby. Przygotuj wiersz przerabia je na coś czytelnego:

  • url - z {{ $json.keys[0] }}, bo tam API odkłada wartość wymiaru page,
  • klikniecia i wyswietlenia - bez zmian,
  • ctr_proc - {{ Math.round($json.ctr * 10000) / 100 }}. API podaje CTR jako ułamek, na przykład 0.0345. Po przeliczeniu masz 3,45 procent z dwoma miejscami po przecinku,
  • pozycja - {{ Math.round($json.position * 10) / 10 }}, czyli średnia pozycja z jednym miejscem po przecinku.

Upsert po dwóch kolumnach

Zapisz miesiac szuka wiersza, w którym zgadzają się jednocześnie url i miesiac. Jest taki wiersz, zostaje zaktualizowany. Nie ma, powstaje nowy.

To daje dwie rzeczy naraz. Po pierwsze historię: sierpień i wrzesień tej samej podstrony to dwa osobne wiersze. Po drugie brak duplikatów: uruchomienie drugi raz w tym samym miesiącu aktualizuje liczby zamiast dopisywać nowe wiersze. Sprawdziłem to w teście.

Haczyk. Stara wersja tej lekcji robiła update w arkuszu po samym adresie. Każde uruchomienie nadpisywało poprzedni miesiąc, a adresy spoza arkusza przepadały po cichu. Jeśli budujesz podobny raport sam, klucz musi zawierać okres. Sam adres to za mało.

Co z tym zrobić

Workflow ma ręczny start. Chcesz, żeby chodził sam? Podmień Uruchom recznie na harmonogram raz w miesiącu, w pierwszych dniach. Pamiętaj, że wyzwalacz harmonogramu działa dopiero po Publish, co pokazuję w lekcji Interfejs i publikacja.

Z kilku miesięcy w tabeli od razu widać podstrony, którym spadają kliknięcia, i te z dużą liczbą wyświetleń przy niskim CTR. Te drugie to kandydaci do nowego title i description.

Podsumowanie

Jedno zapytanie ściąga miesięczne wyniki wszystkich podstron. Daty liczą się same z $now, CTR zamieniasz na procenty, a upsert po adresie i miesiącu buduje historię bez duplikatów. Połączenie z żywym kontem Google opisuję według dokumentacji, test przeszedł na przykładowej odpowiedzi.

W NawiSEO 5 - top frazy z Search Console sprawdzimy, na jakie frazy wyświetla się każda podstrona.

Pokaż JSON do skopiowania - nawiseo-4-search-console-analiza-miesiaca.json
{
  "name": "NawiSEO 4 - Search Console, poprzedni miesiac",
  "nodes": [
    {
      "parameters": {},
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [
        0,
        0
      ],
      "id": "0ab8b252-ff12-4088-04ac-3c39075ec92f",
      "name": "Uruchom recznie"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "24f527d6-de18-e136-7b7d-4ff0f27883cd",
              "name": "wlasciwosc",
              "value": "https://damianslimak.pl/",
              "type": "string"
            },
            {
              "id": "3da16d21-a12a-2111-126d-fffa6f33d646",
              "name": "od",
              "value": "={{ $now.minus({months: 1}).startOf('month').toFormat('yyyy-MM-dd') }}",
              "type": "string"
            },
            {
              "id": "b495bd14-f28f-91cd-42be-a1251940a513",
              "name": "do",
              "value": "={{ $now.minus({months: 1}).endOf('month').toFormat('yyyy-MM-dd') }}",
              "type": "string"
            },
            {
              "id": "73af948f-fdde-17d1-f46c-58aa0665c636",
              "name": "miesiac",
              "value": "={{ $now.minus({months: 1}).toFormat('yyyy-MM') }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        220,
        0
      ],
      "id": "2b3b9a12-2c09-fa5d-418d-67e7a66d84c3",
      "name": "Ustawienia"
    },
    {
      "parameters": {
        "resource": "table",
        "operation": "create",
        "tableName": "nawiseo_gsc_miesiac",
        "columns": {
          "column": [
            {
              "name": "miesiac",
              "type": "string"
            },
            {
              "name": "url",
              "type": "string"
            },
            {
              "name": "klikniecia",
              "type": "number"
            },
            {
              "name": "wyswietlenia",
              "type": "number"
            },
            {
              "name": "ctr_proc",
              "type": "number"
            },
            {
              "name": "pozycja",
              "type": "number"
            }
          ]
        },
        "options": {
          "createIfNotExists": true
        }
      },
      "type": "n8n-nodes-base.dataTable",
      "typeVersion": 1.1,
      "position": [
        440,
        0
      ],
      "id": "c538e11e-b1a8-5cd1-b3e1-2cb80369bde0",
      "name": "Przygotuj tabele"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://www.googleapis.com/webmasters/v3/sites/{{ encodeURIComponent($('Ustawienia').item.json.wlasciwosc) }}/searchAnalytics/query",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "googleOAuth2Api",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ startDate: $('Ustawienia').item.json.od, endDate: $('Ustawienia').item.json.do, dimensions: ['page'], rowLimit: 25000 }) }}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        660,
        0
      ],
      "id": "752d1593-3429-e21c-5852-ba4fdb7db843",
      "name": "Dane z Search Console"
    },
    {
      "parameters": {
        "fieldToSplitOut": "rows",
        "options": {}
      },
      "type": "n8n-nodes-base.splitOut",
      "typeVersion": 1,
      "position": [
        880,
        0
      ],
      "id": "be9dc8f6-0ddb-d46b-9dee-1f371a81a5bb",
      "name": "Rozbij wiersze"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "2480b6b2-6ce1-eae8-2ffc-f39278d58f51",
              "name": "miesiac",
              "value": "={{ $('Ustawienia').item.json.miesiac }}",
              "type": "string"
            },
            {
              "id": "15b3699f-c7f5-dcd2-10d0-c3ced51e01eb",
              "name": "url",
              "value": "={{ $json.keys[0] }}",
              "type": "string"
            },
            {
              "id": "23ea0c60-4ca9-27f0-c080-d1a2c1469cca",
              "name": "klikniecia",
              "value": "={{ $json.clicks }}",
              "type": "number"
            },
            {
              "id": "aa5c283c-e5aa-1135-5d7d-accb0a75ca85",
              "name": "wyswietlenia",
              "value": "={{ $json.impressions }}",
              "type": "number"
            },
            {
              "id": "0a79841b-7193-e6d4-78d7-4ad2f1944527",
              "name": "ctr_proc",
              "value": "={{ Math.round($json.ctr * 10000) / 100 }}",
              "type": "number"
            },
            {
              "id": "77b70e71-efad-981c-7bcf-a2560a3a4229",
              "name": "pozycja",
              "value": "={{ Math.round($json.position * 10) / 10 }}",
              "type": "number"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1100,
        0
      ],
      "id": "37b04d31-24b8-661c-8a96-3dc4304ed08e",
      "name": "Przygotuj wiersz"
    },
    {
      "parameters": {
        "operation": "upsert",
        "dataTableId": {
          "__rl": true,
          "value": "nawiseo_gsc_miesiac",
          "mode": "name"
        },
        "matchType": "allConditions",
        "filters": {
          "conditions": [
            {
              "keyName": "url",
              "condition": "eq",
              "keyValue": "={{ $json.url }}"
            },
            {
              "keyName": "miesiac",
              "condition": "eq",
              "keyValue": "={{ $json.miesiac }}"
            }
          ]
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "miesiac": "={{ $json.miesiac }}",
            "url": "={{ $json.url }}",
            "klikniecia": "={{ $json.klikniecia }}",
            "wyswietlenia": "={{ $json.wyswietlenia }}",
            "ctr_proc": "={{ $json.ctr_proc }}",
            "pozycja": "={{ $json.pozycja }}"
          },
          "matchingColumns": [],
          "schema": [
            {
              "id": "miesiac",
              "displayName": "miesiac",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "url",
              "displayName": "url",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "klikniecia",
              "displayName": "klikniecia",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "number",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "wyswietlenia",
              "displayName": "wyswietlenia",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "number",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "ctr_proc",
              "displayName": "ctr_proc",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "number",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "pozycja",
              "displayName": "pozycja",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "number",
              "readOnly": false,
              "removed": false
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.dataTable",
      "typeVersion": 1.1,
      "position": [
        1320,
        0
      ],
      "id": "113f9b09-c407-d055-6e53-80318f9c17e6",
      "name": "Zapisz miesiac"
    }
  ],
  "connections": {
    "Uruchom recznie": {
      "main": [
        [
          {
            "node": "Ustawienia",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Ustawienia": {
      "main": [
        [
          {
            "node": "Przygotuj tabele",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Przygotuj tabele": {
      "main": [
        [
          {
            "node": "Dane z Search Console",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Dane z Search Console": {
      "main": [
        [
          {
            "node": "Rozbij wiersze",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Rozbij wiersze": {
      "main": [
        [
          {
            "node": "Przygotuj wiersz",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Przygotuj wiersz": {
      "main": [
        [
          {
            "node": "Zapisz miesiac",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "active": false
}