W tym artykule pokażę krok po kroku, jak stworzyć program, którym jest własny agent AI w Pythonie do audytu SEO i WCAG strony na WordPressie.
Narzędzie to nie tylko zbierze kody błędów, ale dzięki modelom językowym zinterpretuje je w kontekście intencji użytkownika i natychmiast zaproponuje gotowe poprawki w kodzie Twojego motywu. Taka architektura pozwala na pełną kontrolę nad danymi, brak limitów typowych dla rozwiązań chmurowych oraz możliwość głębokiej personalizacji audytów pod konkretnych klientów.
Architektura agenta: Python, Gemini API i SQLite
Aby nasz agent był użyteczny w codziennej pracy dewelopera i specjalisty SEO, potrzebujemy trzech solidnych filarów:
- Ekstrakcji danych i symulacji przeglądarki: Zwykła biblioteka
requeststo za mało dla nowoczesnego WordPressa. Musimy wyrenderować bloki Gutenberga, skrypty JS oraz asynchronicznie ładowane obrazy. Użyjemy do tego bibliotekiPlaywright, która uruchomi przeglądarkę w trybie headless, a za audyt dostępności odpowie wstrzyknięty silnikaxe-core. - Mózgu operacyjnego: Wykorzystamy API Google Gemini (model
gemini-1.5-proze względu na potężne okno kontekstowe), który przeanalizuje wyciągnięty kod HTML, nagłówki i wyniki z axe-core. - Pamięci i raportowania: Wyniki zapiszemy do lokalnej bazy
SQLite. Relacyjna baza danych na dysku jest lekka, nie wymaga serwera, a pozwala na generowanie comiesięcznych raportów i porównywanie historii zmian na stronie.
Przygotowanie środowiska
Zacznijmy od stworzenia wirtualnego środowiska i instalacji niezbędnych paczek.
Bash
python -m venv venv
source venv/bin/activate # na Windows: venv\Scripts\activate
pip install playwright axe-playwright-python google-genai beautifulsoup4
playwright install chromium
Krok 1: Logika crawlera i audyt WCAG z Playwright
Rozpoczynamy od napisania klasy naszego agenta. Poniższy skrypt w języku Python wchodzi na podany adres URL (np. stronę usługi lub wpis blogowy na WordPressie), pobiera zrenderowany DOM i uruchamia testy dostępności za pomocą silnika Axe.
Python
import asyncio
import sqlite3
import json
from playwright.async_api import async_playwright
from axe_playwright_python.async_playwright import Axe
from bs4 import BeautifulSoup
from google import genai
from google.genai import types
class SEOWCAGAgent:
def __init__(self, gemini_api_key: str, db_path: str = "seo_audits.db"):
self.client = genai.Client(api_key=gemini_api_key)
self.db_path = db_path
self._init_db()
def _init_db(self):
"""Inicjalizacja lokalnej bazy SQLite do przechowywania raportów."""
with sqlite3.connect(self.db_path) as conn:
cursor = conn.cursor()
cursor.execute('''
CREATE TABLE IF NOT EXISTS audits (
id INTEGER PRIMARY KEY AUTOINCREMENT,
url TEXT NOT NULL,
audit_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
seo_score INTEGER,
wcag_violations INTEGER,
ai_recommendations TEXT
)
''')
conn.commit()
async def extract_and_audit(self, url: str) -> dict:
"""Pobiera treść strony i wykonuje testy WCAG."""
print(f"[*] Rozpoczynam skanowanie: {url}")
async with async_playwright() as p:
browser = await p.chromium.launch(headless=True)
page = await browser.new_page()
# Maskujemy agenta, aby uniknąć blokad przez zapory WAF (np. Cloudflare)
await page.set_extra_http_headers({"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"})
await page.goto(url, wait_until="networkidle")
# 1. Ekstrakcja czystego tekstu i nagłówków do audytu SEO/AEO
html_content = await page.content()
soup = BeautifulSoup(html_content, 'html.parser')
# Usuwamy zbędne tagi, aby oszczędzać tokeny
for element in soup(["script", "style", "nav", "footer"]):
element.extract()
clean_text = soup.get_text(separator='\n', strip=True)
h1 = soup.find('h1').text if soup.find('h1') else "BRAK H1"
title = soup.title.string if soup.title else "BRAK TITLE"
# 2. Uruchomienie audytu WCAG za pomocą axe-core
print("[*] Wykonuję testy dostępności (Axe)...")
results = await Axe().run(page)
# Filtrujemy tylko rzeczywiste błędy (violations)
violations = [{"id": v.id, "description": v.description, "impact": v.impact, "nodes": len(v.nodes)} for v in results.violations]
await browser.close()
return {
"url": url,
"title": title,
"h1": h1,
"content": clean_text[:5000], # Ograniczamy treść do 5000 znaków
"wcag_violations": violations
}
Użycie networkidle w Playwright gwarantuje, że WordPress załaduje wszystkie skrypty w tym dynamiczne slidery czy bloki ładujące się z opóźnieniem. Jeśli pominiesz ten krok i użyjesz standardowego żądania GET, Twój audyt WCAG nie sprawdzi faktycznego, wyrenderowanego interfejsu.
Krok 2: Analiza danych przez model Gemini
Teraz najciekawsza część. Zebrane surowe dane (tekst i lista błędów WCAG) wysyłamy do modelu językowego. Używamy tu precyzyjnego systemu promptowania (System Prompt), aby agent nie pisał „lania wody”, lecz podawał konkretne techniczne wskazówki dla programisty WordPressa.
Python
def analyze_with_gemini(self, extracted_data: dict) -> str:
"""Wysyła zebrane dane do Gemini w celu eksperckiej analizy."""
print("[*] Generowanie rekomendacji AI...")
system_instruction = """
Jesteś Senior WordPress Developerem i ekspertem ds. SEO/WCAG.
Otrzymasz dane ze zescrapowanej strony oraz wyniki audytu axe-core.
Twoim zadaniem jest:
1. Ocenić strukturę H1/Title pod kątem Answer Engine Optimization (AEO).
2. Zaproponować kod (np. w PHP dla functions.php lub JS), który naprawi zgłoszone błędy WCAG 2.2.
3. Podać szacunkowy wynik SEO w skali 0-100.
Zwróć odpowiedź w czystym formacie JSON o strukturze:
{"seo_score": 85, "summary": "tekst", "code_fixes": "przykłady kodu"}
"""
prompt = f"""
URL: {extracted_data['url']}
Title: {extracted_data['title']}
H1: {extracted_data['h1']}
Błędy WCAG: {json.dumps(extracted_data['wcag_violations'], ensure_ascii=False)}
Treść: {extracted_data['content']}
"""
response = self.client.models.generate_content(
model='gemini-1.5-pro',
contents=prompt,
config=types.GenerateContentConfig(
system_instruction=system_instruction,
response_mime_type="application/json",
temperature=0.2 # Niski parametr temp dla maksymalnej determinacji i technicznego konkretu
),
)
return response.text
Zwróć uwagę na wymuszenie response_mime_type="application/json". Dzięki temu Gemini nie odpowie formatowaniem Markdown, a czystym obiektem JSON, co pozwala naszej aplikacji natychmiast sparsować odpowiedź i zapisać ją do bazy danych bez ryzyka błędów parsowania.
Krok 3: Zapis do bazy danych i integracja całości
Ostatnim elementem jest połączenie wyciągania danych z ich zapisem do SQLite. Baza ta pozwoli w przyszłości na podpięcie prostego interfejsu w Flasku lub FastAPI i generowanie plików PDF z comiesięcznymi raportami dla klientów agencji.
Python
async def run_full_audit(self, url: str):
# 1. Pobierz dane
data = await self.extract_and_audit(url)
# 2. Przeanalizuj w AI
ai_analysis_raw = self.analyze_with_gemini(data)
ai_result = json.loads(ai_analysis_raw)
violations_count = len(data['wcag_violations'])
# 3. Zapisz do bazy
with sqlite3.connect(self.db_path) as conn:
cursor = conn.cursor()
cursor.execute('''
INSERT INTO audits (url, seo_score, wcag_violations, ai_recommendations)
VALUES (?, ?, ?, ?)
''', (url, ai_result['seo_score'], violations_count, json.dumps(ai_result)))
conn.commit()
print(f"[+] Zakończono audyt dla {url}. Zapisano do bazy SQLite.")
print(f"[+] Wynik SEO: {ai_result['seo_score']}/100 | Błędy WCAG: {violations_count}")
# Przykładowe użycie:
# if __name__ == "__main__":
# API_KEY = "TWÓJ_KLUCZ_GEMINI"
# agent = SEOWCAGAgent(API_KEY)
# asyncio.run(agent.run_full_audit("https://designsolutions.pl"))
Do automatyzacji cyklicznych analiz i comiesięcznych raportów (np. dla stałych klientów z branży nieruchomości lub lokalnych firm we Wrocławiu), taką aplikację uruchamia się zwykle jako zadanie Crona na serwerze Linux, celując w listę zdefiniowanych wcześniej adresów URL.
Najczęstsze błędy przy budowie własnych agentów SEO
- Scraping bez środowiska JS: Używanie
BeautifulSoupdo pobierania DOM na stronach zbudowanych w headless WordPress (React/Vue) kończy się zaindeksowaniem pustego kontenera<div id="root"></div>. Zawsze używaj Playwright do analizy frontendów. - Halinucjacje modelu przy poprawkach WCAG: Modele LLM mają tendencję do proponowania atrybutów
aria-*tam, gdzie nie są potrzebne. W system prompcie warto dodać restrykcję: „Zanim zaproponujesz aria-label, sprawdź czy problemu nie rozwiązuje semantyczny tag HTML5”. - Przekroczenie okna kontekstowego: Na potężnych stronach głównych wyrenderowany kod HTML może zajmować setki kilobajtów. Przed wysłaniem do Gemini należy oczyścić DOM ze stylów inline, tagów SVG oraz długich łańcuchów Base64 w obrazkach.
Podsumowanie i checklist wdrożeniowa
Stworzenie własnego agenta to doskonały sposób na przewagę technologiczną w SEO. Twoje analizy stają się szybsze, a wygenerowane rekomendacje mogą zawierać dedykowany kod, dokładnie pod Twój stack technologiczny (Tailwind, natywne bloki wp).
Zanim zdeployujesz ten skrypt u siebie, sprawdź:
- [ ] Czy baza SQLite jest zabezpieczona przed przypadkowym usunięciem w repozytorium (np. dodana do
.gitignore)? - [ ] Czy limit zapytań (Rate Limit) do API Gemini nie zablokuje asynchronicznej pętli przy sprawdzaniu 100 podstron naraz? (Zalecamy dodanie
asyncio.sleep()). - [ ] Czy Twoje serwery proxy są skonfigurowane, by Playwright nie dostał bana od Cloudflare na stronach klientów?
W Design Solutions optymalizujemy procesy analityczne, wykorzystując podobne rozwiązania oparte na Pythonie do skalowania audytów cyfrowych i zagwarantowania stabilnego wzrostu widoczności marek w nowoczesnych silnikach wyszukiwania.