Case: cotygodniowe wykrywanie kanibalizacji w 300 artykułach

12 listopada, 2026

Ręczny audyt kanibalizacji w serwisie z 300 artykułami robiliśmy raz na kwartał. Zajmował dwa dni pracy, a jego wynik starzał się szybciej, niż zdążyliśmy go wdrożyć. Ten case opisuje, jak zastąpiliśmy kwartalny przegląd cotygodniowym skryptem, ile fałszywych alarmów generował na starcie i co faktycznie udało się dzięki niemu naprawić. To przykład automatyzacji audytu treści, który działa bez dedykowanego zespołu: jedna osoba przegląda raport w poniedziałek rano i podejmuje decyzje.

Problem: audyt raz na kwartał to za rzadko

Serwis to blog ekspercki w branży B2B: około 300 opublikowanych artykułów, 6–8 nowych miesięcznie i regularne odświeżenia starszych tekstów. Przy takim tempie dwa artykuły zaczynają konkurować o to samo zapytanie znacznie częściej, niż sugeruje intuicja. Wystarczy nowy tekst poradnikowy, który częściowo powtarza starszy przewodnik, albo aktualizacja, która dopisuje sekcję wchodzącą w temat sąsiedniego wpisu.

Metodę ręcznego wykrywania opisaliśmy w artykule o tym, jak wykryć kanibalizację słów kluczowych w Search Console. Działa dobrze, ale ma jedną wadę: wymaga, żeby ktoś usiadł do danych. W praktyce kwartalny audyt wykrywał problem średnio 7–9 tygodni po jego powstaniu. Przez ten czas pozycje obu adresów zdążyły się rozjechać, a Google kilka razy zmieniał, który z nich pokazuje.

Drugim problemem była jakość samego audytu. Przy kwartalnym przeglądzie analityk dostawał kilkadziesiąt podejrzanych par naraz i musiał ocenić je w jedno popołudnie. Decyzje zapadały pospiesznie, część par odkładano „na następny kwartał”, a po trzech miesiącach nikt już nie pamiętał, dlaczego. Brakowało też historii: nie dało się sprawdzić, czy dana para konkuruje od tygodnia, czy od pół roku, a to zupełnie zmienia priorytet.

Cel projektu był prosty: skrócić czas od powstania kanibalizacji do decyzji redakcyjnej z dwóch miesięcy do jednego tygodnia, bez dokładania pracy zespołowi.

Źródła danych i częstotliwość pobierania

Skrypt korzysta z dwóch źródeł. Pierwsze to dane o skuteczności z Search Console, pobierane przez metodę searchanalytics.query z wymiarami query i page. Interfejs Search Console ucina wyniki i agreguje je na poziomie usługi, dlatego potrzebny był dostęp przez API. Szczegóły paginacji i limitów wierszy omawiamy w tekście o tym, jak pobrać pełne dane z Search Console API bez próbkowania.

Drugie źródło to treść samych artykułów: tytuł, nagłówki H2 i pierwsze 400 słów, pobierane z REST API WordPressa. Dla każdego tekstu liczymy embedding, a następnie podobieństwo kosinusowe (cosine similarity) między wszystkimi parami. Przy 300 artykułach daje to niecałe 45 tysięcy par, co mieści się w jednym przebiegu w kilka minut.

Harmonogram wygląda tak:

  • dane z Search Console: codziennie, okno ostatnich 28 dni, z trzydniowym opóźnieniem (świeższe dni są niekompletne),
  • embeddingi treści: tylko dla artykułów zmienionych od ostatniego przebiegu,
  • raport: raz w tygodniu, w poniedziałek o 6:00.

Historia zapytań trafia do BigQuery, bo po kilku miesiącach tabela przestaje mieścić się w arkuszu. Konfigurację eksportu opisaliśmy w poradniku o masowym eksporcie Search Console do BigQuery.

Reguła wykrywania i próg alarmu

Pierwsza wersja reguły była naiwna: alarm, gdy dwa adresy dostają wyświetlenia na to samo zapytanie. Pierwszy raport miał 412 pozycji, czyli był bezużyteczny. Zdecydowana większość par dzieliła się zapytaniami długiego ogona z pojedynczymi wyświetleniami, co jest naturalne dla serwisu tematycznego.

Ostateczna reguła łączy trzy warunki, które muszą wystąpić jednocześnie:

  1. Wspólne zapytanie z realnym wolumenem: co najmniej 50 wyświetleń w oknie 28 dni dla każdego z dwóch adresów.
  2. Rotacja w wynikach: w ciągu 28 dni Google pokazywał na to zapytanie raz jeden, raz drugi adres, a żaden nie zebrał ponad 80% wyświetleń.
  3. Podobieństwo treści: podobieństwo kosinusowe embeddingów co najmniej 0,82.

Próg 0,82 nie wziął się z literatury, tylko z kalibracji. Ręcznie oceniliśmy 60 par z pierwszego raportu jako „kanibalizacja” albo „naturalne sąsiedztwo” i szukaliśmy wartości, przy której odrzucamy możliwie mało prawdziwych przypadków. Poniżej 0,78 raport zalewały pary tekstów z tej samej kategorii, które po prostu dotyczą pokrewnych tematów.

Fałszywe alarmy: skąd się biorą i jak je ograniczono

Po wprowadzeniu trzech warunków raport skurczył się do 11 par tygodniowo, ale w pierwszym miesiącu około 40% z nich okazało się fałszywymi alarmami. Przyczyny dało się pogrupować w cztery typy.

Źródło fałszywego alarmuPrzykładRozwiązanie
Zapytania brandowe i nawigacyjnenazwa serwisu plus słowo „blog”lista wykluczeń zapytań
Para filar i artykuł pomocniczyprzewodnik ogólny i szczegółowy tutorialmapa klastrów, pary filaru z wpisem tylko przy podobieństwie powyżej 0,9
Krótkotrwała rotacja po publikacjinowy tekst przez 10 dni testowany obok staregoalarm dopiero przy rotacji w dwóch kolejnych tygodniach
Zamierzone wariantywersja dla początkujących i dla zaawansowanychręczna lista par zaakceptowanych

Najwięcej dała zasada dwóch tygodni. Google regularnie testuje świeży artykuł na zapytaniach starszego i w większości przypadków sam rozstrzyga sytuację w ciągu kilkunastu dni. Alarm wysłany w pierwszym tygodniu tylko marnował czas redakcji. Po wszystkich poprawkach odsetek fałszywych alarmów spadł do około 15%, a raport zawiera średnio dwie pary tygodniowo.

Format raportu, który da się przejrzeć w pięć minut

Raport trafia na kanał zespołu jako krótka wiadomość z linkiem do arkusza. Każda para ma jeden wiersz z kolumnami: oba adresy, wspólne zapytania z wyświetleniami, udział każdego adresu w kliknięciach, podobieństwo treści i liczba tygodni, przez które para utrzymuje się w raporcie. Ostatnia kolumna to pole decyzji z czterema wartościami: scalić, przepisać jeden z tekstów, dodać link i zróżnicować intencję, zaakceptować.

Mechanikę wysyłki zbudowaliśmy na tym samym schemacie co workflow agenta SEO w n8n: harmonogram, kilka kroków pobierania danych i jeden węzeł formatujący wynik. Decyzja wpisana w arkusz wraca do skryptu, więc para oznaczona jako zaakceptowana nie pojawia się ponownie, dopóki podobieństwo treści nie wzrośnie o więcej niż 0,05.

Ważna okazała się kolumna z liczbą tygodni. Para, która wisi w raporcie trzeci tydzień, to sygnał, że decyzja została odłożona, a nie że problem zniknął.

Efekty po pół roku działania

Skrypt działa od 26 tygodni. W tym czasie wskazał 54 pary, z których redakcja potwierdziła 46. Decyzje rozkładały się następująco:

  • 14 scaleń z przekierowaniem 301 na mocniejszy adres,
  • 19 przepisań, w których jeden tekst zmienił kąt i frazę główną,
  • 9 przypadków rozwiązanych dodaniem linku wewnętrznego i doprecyzowaniem nagłówków,
  • 4 pary zaakceptowane jako zamierzone warianty.

Czas od pojawienia się rotacji w wynikach do decyzji redakcyjnej spadł z 7–9 tygodni do 2–3 tygodni. Dwa tygodnie to celowe opóźnienie z reguły alarmu, więc w praktyce decyzja zapada w tygodniu, w którym para pojawia się w raporcie. W grupie scalonych artykułów adres docelowy po 8 tygodniach miał średnio o 23% więcej kliknięć niż suma obu adresów przed scaleniem. W grupie przepisań efekt był słabszy i bardziej rozproszony: około połowy par wyraźnie się rozdzieliła, reszta wymagała drugiej iteracji.

Koszt utrzymania to około pół godziny tygodniowo na przegląd raportu i kilka godzin miesięcznie na poprawki reguł. Kwartalny audyt ręczny zniknął z kalendarza. Najważniejszy wniosek nie dotyczy jednak samego skryptu: automatyzacja audytu treści działa tylko wtedy, gdy raport kończy się decyzją. Pierwsza wersja, która wysyłała listę bez kolumny decyzji, była czytana przez trzy tygodnie, a potem ignorowana.

FAQ

Czy do takiego skryptu potrzebne są embeddingi, czy wystarczą dane z Search Console?

Same dane z Search Console dają zbyt dużo alarmów, bo pokrewne artykuły naturalnie dzielą się zapytaniami długiego ogona. Podobieństwo treści odsiewa pary, które mają wspólne frazy, ale odpowiadają na inne pytania. W naszym przypadku dodanie tego warunku zmniejszyło raport z ponad 400 do kilkunastu pozycji.

Dlaczego okno 28 dni, a nie 7?

Przy oknie tygodniowym większość zapytań w serwisie tej wielkości ma zbyt mało wyświetleń, żeby wiarygodnie ocenić rotację adresów. 28 dni daje stabilny obraz, a cotygodniowy raport i tak wychwytuje zmiany z opóźnieniem nie większym niż tydzień.

Jak ustalić próg podobieństwa dla własnego serwisu?

Najprościej ręcznie ocenić 50–60 par z pierwszego, nieprzefiltrowanego raportu i sprawdzić, przy jakiej wartości tracimy najmniej prawdziwych przypadków. Próg zależy od modelu embeddingów i od tego, jak wąsko tematyczny jest serwis, więc wartości 0,82 nie warto przenosić bez sprawdzenia.

Czy każdą wykrytą kanibalizację trzeba rozwiązać scaleniem?

Nie. Scalenie było najlepszym rozwiązaniem w mniej niż jednej trzeciej przypadków. Często wystarczyło przepisać jeden tekst pod inną intencję albo dodać link wewnętrzny, który jasno wskazuje Google, który adres jest główny dla danego zapytania.

Ile kosztuje utrzymanie takiej automatyzacji?

Koszty infrastruktury są pomijalne: API Search Console jest bezpłatne, a embeddingi liczymy tylko dla zmienionych tekstów. Głównym kosztem jest czas człowieka, czyli około 30 minut tygodniowo na przegląd raportu i decyzje.