Tehisintellekti viidete mõju mõõtmine: analüütika, logid ja paneelide kraapimine
Sissejuhatus
AI-põhine avastamine jaguneb nüüd mitme mõõtmiskihiga. Inimene võib näha ettevõtet viidatuna tehisintellekti vastuses, klõpsata lingil ilma kasuliku viitajateta, külastada brauseri kaudu, mis eemaldab kampaaniaandmed, või lugeda vastust üldse klikkimata. Samal ajal võib tehisintellekti roomaja küsida ettevõtte lehekülge ilma inimkülastajat esindamata.
Seega peab kasulik mõõtmissüsteem eraldama neli küsimust:
- Kas saiti roomati või hangiti?
- Kas saiti viidati tehisintellekti vastuses?
- Kas inimene külastas saiti sellest vastusest?
- Kas see külastus tõi kaasa sisulise kaasatuse või tulu?
Alates 22. septembrist 2026 on esimese osapoole aruandlus paranemas. Google Search Console pakub generatiivse tehisintellekti jõudluse aruannet AI ülevaadete ja AI režiimi kohta, hõlmates näitamisi, lehekülgi, riike, seadmeid ja kuupäevi. Aruanne sai ülemaailmselt kättesaadavaks 31. augustil 2026. Google'i generatiivse tehisintellekti jõudluse aruanne (support.google.com)
Microsoft pakub ka tehisintellekti jõudluse aruannet Bing Webmaster Tools'is. Selle avalik eelvaade annab aruannet viidete koguarvu, viidatud lehekülgede, alusküsimuste ja viidete trendide kohta toetatud Microsofti tehisintellekti kogemustes. Bing Webmaster Tools'i tehisintellekti jõudlus (blogs.bing.com)
Neid tööriistu tuleks pidada autoriteetseteks oma platvormide jaoks, kuid need ei loo terviklikku abistajateülest mõõtmissüsteemi. Soovitatav lähenemine on kombineerida esimese osapoole aruanded, serverilogid, kontrollitud jälgimislingid, analüütika ekspordid ja ajastatud vastuspaneeli vaatlused.
1. Miks tehisintellekti suunamiste mõõtmine on keeruline
Traditsiooniline veebianalüütika sõltub suuresti Referer päisest ja kampaania parameetritest. Mõlemad võivad puududa või olla mittetäielikud.
HTTP Referer päis võib sisaldada eelmist lehekülge, ainult eelmise veebisaidi päritolu või üldse mitte mingit väärtust. Brauseri viitaja poliitikad võivad seda teavet tahtlikult piirata või eemaldada. MDN Referrer-Policy dokumentatsioon (developer.mozilla.org)
Tehisintellekti süsteemid loovad täiendavaid keerukusi:
- Abistaja võib linkida lehele ilma äratuntavat viitajat saatmata.
- Abistaja võib kasutada ümbersuunavat või ümberkirjutatud URL-i.
- Abistaja võib sisu serveri poolel enne vastuse näitamist alla laadida.
- Tehisintellekti roomaja võib külastada lehte ilma, et inimene seda näeks.
- Vastus võib viidata kanoonilisele URL-ile algselt hangitud URL-i asemel.
- Vastus võib muutuda sõltuvalt asukohast, seadmest, keelest, ajast või isikupärastamisest.
Näiteks eristab OpenAI OAI-SearchBot'i otsingu avastamiseks, GPTBot'i võimaliku treeningkasutuse jaoks ja ChatGPT-User'i kasutaja algatatud tegevuste jaoks. Perplexity eristab eraldi PerplexityBot'i ja Perplexity-User'i. Anthropic eristab ClaudeBot'i, Claude-SearchBot'i ja Claude-User'i. Neid päringuid ei tohiks kombineerida üheks üldiseks “AI liikluse” numbriks. OpenAI roomaja dokumentatsioon (openai.com) Perplexity roomaja dokumentatsioon (docs.perplexity.ai) Anthropic roomaja dokumentatsioon (support.anthropic.com)
Keskne reegel on:
Roomaja tegevus ei ole suunav liiklus, viidete nähtavus ei ole liiklus ja liiklus ei ole äritegevuse mõju.
2. Mõõtmisraamistik
Kasutage viit mõõtmiskihti.
| Kiht | Peamine küsimus | Soovitatavad andmed |
|---|---|---|
| Juurdepääs | Kas tehisintellekti süsteem küsis lehte? | Veebiserveri logid, tulemüüri logid |
| Nähtavus | Kas lehte näidati või viidati? | Google Search Console, Bing Webmaster Tools, vastuspaneeli vaatlused |
| Omandamine | Kas inimene saabus tehisintellekti platvormilt? | Viitaja, kampaania parameetrid, lühilingi klikid, analüütika |
| Kaasamine | Kas külastaja suhtles sisukalt? | Analüütika sündmused, lehevaatamised, kaasamise aeg |
| Väärtus | Kas külastus tõi soovitud tulemuse? | Registreerumised, müügivihjed, ostud, tellimused |
Soovitatav tõeallika hierarhia
Kasutage iga küsimuse jaoks tugevaimat saadaolevat signaali:
- Esimese osapoole platvormi aruanded näitamiste ja viidete kohta.
- Vastuspaneeli vaatlused päringutaseme nähtavuse ja konkurentsivõrdluste jaoks.
- Ümbersuunamislogid kontrollitud lingiklikkide jaoks.
- Veebianalüütika seansside, kaasamise ja konversioonide jaoks.
- Serverilogid roomaja ja sisu laadimise tegevuse kohta.
- Modelleeritud hinnangud ainult siis, kui otseseid tõendeid pole saadaval.
Ärge kombineerige kõiki signaale ühte numbriks ilma allikat ja usaldusväärsuse taset säilitamata.
3. Põhiandmemudel
Looge andmeladu eraldi tabelitega toorvaatluste ja tuletatud mõõdikute jaoks.
panel_runs
Üks rida iga päringu ja platvormi kontrolli kohta.
text run_id observed_at surface topic prompt_id query_text locale country device panel_present panel_type answer_hash raw_html_path screenshot_path collector_version status
panel_citations
Üks rida iga vastuspaneelis vaadeldud lingi kohta.
text run_id surface topic prompt_id cited_url cited_domain citation_position link_scope target_domain target_cited
ai_crawler_requests
Agregeeritud serverilogide andmed.
text date bot_name bot_role path requests successful_requests error_requests unique_ip_count
shortener_clicks
Üks rida iga kontrollitud lingipäringu kohta.
text click_id occurred_at slug surface topic prompt_id asset_id referrer_host user_agent_family ip_hmac destination_path
ai_sessions
Normaliseeritud analüütika tabel.
text session_id date surface topic prompt_id landing_page attribution_method confidence engaged engagement_time_seconds key_event revenue
Väli confidence on oluline. Soovitatavad väärtused on:
- A — Otse: kontrollitud lühilingi klikk või usaldusväärne kampaania parameeter.
- B — Tugev: tuntud abistaja viitaja või kinnitatud kasutaja-saatmise päring.
- C — Tõenäoline: kampaania või sihtlehe tõend ilma täieliku viitajata.
- D — Modelleeritud: tuletatud ajastuse, paneeli vaatluste või koondmuutuste põhjal.
Aruandlus A ja B eraldi C-st ja D-st.
4. Mõõdikute definitsioonid
Viidete osakaal teema järgi
Viidete osakaal teema järgi mõõdab, kui tihti sihtdomeen esineb teema kohta vaadeldud allikate hulgas.
\[ \text{Viidete osakaal}_{teema}
\frac{\text{Sihtdomeeni viidete esinemised}} {\text{Kõik viidete esinemised}} \]
Näide:
- 100 viidet kokku täheldati teema “projektijuhtimistarkvara” kohta
- 12 viidet viitavad teie domeenile
- Viidete osakaal teema järgi = 12%
See mõõdik mõõdab allika osakaalu, mitte seda, kas vastus sisaldas teie saiti vähemalt korra.
Viidete katvus
Kasutage teist mõõdikut vastuse tasandi olemasolu jaoks:
\[ \text{Viidete katvus}_{teema}
\frac{\text{Vastuse käivitused, mis sisaldavad sihtviidet}} {\text{Vastuse käivitused, kus paneel oli kohal}} \]
Viidete katvust on mittetehnilistel auditooriumidel sageli lihtsam mõista:
"Meie sait ilmus 34% teema kohta vaadeldud tehisintellekti vastustest."
Viite positsioon
Salvestage viite positsioon vastuses:
1= esimene täheldatud allikas2= teine täheldatud allikas3= kolmas täheldatud allikas
Kuna tehisintellekti liidesed sageli rühmitavad või järjestavad allikaid ümber, käsitlege positsiooni kirjeldava vaatlusena, mitte universaalse järjestusena.
Abistaja suunamismäär
Abistaja suunamismäär mõõdab inimeste veebisaidi seansside osakaalu, mida saab omistada tehisintellekti platvormile.
\[ \text{Abistaja suunamismäär}
\frac{\text{AI-le omistatud inimeste seansid}} {\text{Kõik inimeste seansid}} \]
Esitage vähemalt kaks versiooni:
text Kinnitatud abistaja suunamismäär = A-usaldusväärsusega AI seansid / kõik inimeste seansid
Laiendatud abistaja suunamismäär = A + B + C-usaldusväärsusega AI seansid / kõik inimeste seansid
Kinnitatud versioon on konservatiivsem. Laiendatud versioon on kasulik trendianalüüsiks, kuid seda ei tohiks esitada kui täielikult vaadeldud liiklust.
Ärge kasutage paneeli käivitusi näitamistena
Kui kraabite 500 päringut, ei tähenda see, et 500 inimest neid vastuseid nägi. Teie kraapija mõõdab kontrollitud valimit vastuse olekutest. Kasutage esimese osapoole platvormi näitamisarvude aruandeid, kui need on saadaval.
Viitejärgne kaasamine
Viitejärgne kaasamine mõõdab, kas tehisintellekti viite kaudu saabunud inimesed sooritasid sisuka tegevuse.
\[ \text{Viitejärgne kaasamine}
\frac{\text{Kaasatud AI seansid}} {\text{AI-le omistatud seansid}} \]
Google Analytics 4 puhul kestab kaasatud seanss üle 10 sekundi, sisaldab olulist sündmust või sisaldab vähemalt kahte lehe- või ekraanivaatamist. Google Analyticsi kaasamise määra dokumentatsioon (support.google.com)
Aruandluses esitage ka:
text Viitejärgne kaasamise aeg = keskmine kaasamise aeg sekundites AI seansi kohta
Viitejärgne konversioonimäär = AI seansid olulise sündmusega / AI-le omistatud seansid
Viitejärgne tulu seansi kohta = tulu AI seanssidest / AI-le omistatud seansid
Keskmine kaasamise aeg on tavaliselt stabiilsem kui keskmine kaasamise aeg, kuna väike arv väga pikki seansse võib keskmist moonutada.
Täiendavad kasulikud mõõdikud
AI roomaja edukuse määr
\[ \text{Roomaja edukuse määr}
\frac{\text{AI roomaja päringud, mis tagastavad 2xx või 3xx}} {\text{AI roomaja päringud}} \]
Jagage see roomajate ja teede kaupa. Suur viidete langus koos kasvava roomajate veamääraga võib viidata hostingu, tulemüüri või robotite konfiguratsiooniprobleemile.
Viite-kliki määr
Kontrollitud vastuspaneeli vaatluste puhul:
\[ \text{Vaadeldud viite-kliki määr}
\frac{\text{Kinnitatud lühilingi klikid}} {\text{Vaadeldud sihtviited}} \]
See ei ole tõeline kasutaja klikimismäär, välja arvatud juhul, kui nimetaja esindab tegelikke kasutaja näitamisi. Märgistage see selgelt kui vaadeldud operatiivne mõõdik.
Viite värskus
Mõõtke, kui tihti viidatud URL peegeldab lehe praegust versiooni.
text Viite värskus = viidatud lehed praeguse sisu räsiga / viidatud lehed
See on kasulik dokumentatsiooni-, hinnakujunduse-, toote- ja poliitikalehtede puhul, mis sageli muutuvad.
5. Serverilogide analüüs
Konfigureerige struktureeritud juurdepääsulogid
Struktureeritud logid on lihtsamini analüüsitavad kui traditsioonilised tühikutega eraldatud logid.
Nginxi konfiguratsiooni näide:
nginx log_format ai_json escape=json '{' '"ts":"$time_iso8601",' '"remote_addr":"$remote_addr",' '"request":"$request",' '"status":$status,' '"bytes":$body_bytes_sent,' '"referer":"$http_referer",' '"user_agent":"$http_user_agent",' '"host":"$host"' '}';
access_log /var/log/nginx/ai-access.jsonl ai_json;
Salvestage logifailid piiratud asukohta. Ärge saatke töötlemata IP-aadresse üldisesse analüütikatabelisse, välja arvatud juhul, kui teil on selge operatiivne põhjus.
Tehiseintellekti päringute tüüpide tuvastamine
Vähemalt eraldage:
text OpenAI:
- OAI-SearchBot
- ChatGPT-User
- GPTBot
Anthropic:
- Claude-SearchBot
- Claude-User
- ClaudeBot
Perplexity:
- PerplexityBot
- Perplexity-User
Otsingumootorid:
- Googlebot
- Bingbot
Google'i AI otsingufunktsioonid kasutavad üldiselt olemasolevaid Google Searchi roomamise kontrolle, mitte eraldi universaalset “AI Overview bot'i”. Google teatab, et Googlebot kontrollib otsingule juurdepääsu, sealhulgas AI funktsioone. Google'i AI funktsioonid ja veebisaidid (developers.google.com)
User-agent stringe saab võltsida. Operatiivsete otsuste jaoks kombineerige user-agent vastavust pakkuja avaldatud IP-vahemikega või kinnitatud pöörd-DNS-iga, kui see on saadaval. OpenAI ja Perplexity avaldavad oma asjakohaste roomajate ja hangitavate kohta IP-teavet. (openai.com)
Korratav logikokkuvõtja
Salvestage järgmine kui ai_log_summary.py.
python #!/usr/bin/env python3
import csv import hashlib import hmac import json import os import re import sys from collections import Counter, defaultdict from datetime import datetime from urllib.parse import urlsplit
BOT_PATTERNS = { "OAI-SearchBot": ("openai", re.compile(r"\bOAI-SearchBot\b", re.I)), "ChatGPT-User": ("openai_user", re.compile(r"\bChatGPT-User\b", re.I)), "GPTBot": ("openai_training", re.compile(r"\bGPTBot\b", re.I)), "Claude-SearchBot": ("anthropic", re.compile(r"\bClaude-SearchBot\b", re.I)), "Claude-User": ("anthropic_user", re.compile(r"\bClaude-User\b", re.I)), "ClaudeBot": ("anthropic_training", re.compile(r"\bClaudeBot\b", re.I)), "PerplexityBot": ("perplexity", re.compile(r"\bPerplexityBot\b", re.I)), "Perplexity-User": ("perplexity_user", re.compile(r"\bPerplexity-User\b", re.I)), "Googlebot": ("google_search", re.compile(r"\bGooglebot\b", re.I)), "Bingbot": ("bing_search", re.compile(r"\bbingbot\b", re.I)), }
SECRET = os.environ.get("HMAC_SECRET", "replace-this-in-production").encode()
def hmac_ip(ip): return hmac.new(SECRET, ip.encode(), hashlib.sha256).hexdigest()[:16]
def classify_bot(user_agent): for bot_name, (role, pattern) in BOT_PATTERNS.items(): if pattern.search(user_agent or ""): return bot_name, role return None, None
def parse_path(request_line): try: parts = request_line.split() return urlsplit(parts[1]).path except Exception: return "/"
def main(input_path): totals = defaultdict(lambda: { "requests": 0, "success": 0, "errors": 0, "ips": set(), "paths": Counter(), })
with open(input_path, "r", encoding="utf-8") as source:
for line in source:
try:
record = json.loads(line)
except json.JSONDecodeError:
continue
bot_name, bot_role = classify_bot(record.get("user_agent", ""))
if not bot_name:
continue
timestamp = record.get("ts", "")
date = datetime.fromisoformat(
timestamp.replace("Z", "+00:00")
).date().isoformat()
status = int(record.get("status", 0))
path = parse_path(record.get("request", ""))
key = (date, bot_name, bot_role)
totals[key]["requests"] += 1
totals[key]["ips"].add(hmac_ip(record.get("remote_addr", "")))
totals[key]["paths"][path] += 1
if 200 <= status < 400:
totals[key]["success"] += 1
else:
totals[key]["errors"] += 1
writer = csv.writer(sys.stdout)
writer.writerow([
"date",
"bot_name",
"bot_role",
"requests",
"successful_requests",
"error_requests",
"unique_ip_count",
"top_paths",
])
for (date, bot_name, bot_role), values in sorted(totals.items()):
top_paths = values["paths"].most_common(10)
writer.writerow([
date,
bot_name,
bot_role,
values["requests"],
values["success"],
values["errors"],
len(values["ips"]),
json.dumps(top_paths),
])
if name == "main": if len(sys.argv) != 2: print("Usage: python ai_log_summary.py /path/to/access.jsonl") sys.exit(1)
main(sys.argv[1])
Käivitage see järgmiselt:
bash HMAC_SECRET='a-long-random-secret' \ python ai_log_summary.py /var/log/nginx/ai-access.jsonl \
data/ai_crawler_daily.csv
See annab operatiivseid tõendeid sisu hankimise tegevuse, mitte inimliikluse kohta.
6. Kohandatud lingilühendajad tehisintellektile suunatud kokkuvõtete jaoks
Esimese osapoole lingilühendaja pakub kontrollitud mõõtepunkti, kui avaldate kokkuvõtteid või lähtematerjale, millele tehisintellekti süsteemid võivad viidata.
Kasutage domeeni, mida te kontrollite, näiteks:
text https://go.example.com/ai-docs
Sihtkohas peaksid olema kampaania parameetrid, näiteks:
text utm_source=perplexity utm_medium=ai_answer utm_campaign=product_documentation utm_content=pricing_summary_v3
Google soovitab kasutada kampaania parameetreid liikluse allikate ja kampaaniate tuvastamiseks. Google Analyticsi kampaania URL-i dokumentatsioon (support.google.com)
Soovitatavad lingiväljad
text surface topic prompt_id asset_id content_version destination_path
Ärge lisage linki isiklikke andmeid, e-posti aadresse ega kasutaja otsingupäringuid. Kampaania identifikaatorid peaksid kirjeldama avaldatud vara, mitte identifitseerima isikut.
Olulised piirangud
Lühilink on kalibreerimisseade, mitte universaalne lahendus:
- Abistaja võib siht-URL-i ümber kirjutada.
- Abistaja võib selle asemel viidata kanoonilisele URL-ile.
- Link võidakse kuvada, kuid seda ei klõpsata kunagi.
- Brauser või privaatsustööriist võib viitaja eemaldada.
- Mõned süsteemid võivad sihtkoha serveri poolel alla laadida.
- Lühilink ei mõõda kasutajaid, kes mäletavad brändi ja naasevad hiljem.
Ärge kasutage lühendajat sihtkoha peitmiseks, turvasüsteemidest möödahiilimiseks ega viidete manipuleerimiseks. Kasutage selget ümbersuunamist asjakohasele, kanoonilisele lehele.
Minimaalne esimese osapoole lühendaja
Järgmine näide kasutab FastAPI-t ja SQLite-i. Enne tootmises kasutamist kaitske lingi loomise lõpp-punkt autentimisega.
python
shortener.py
import hashlib import hmac import os import secrets import sqlite3 from datetime import datetime, timezone from urllib.parse import parse_qsl, urlencode, urlsplit, urlunsplit
from fastapi import FastAPI, HTTPException, Request from fastapi.responses import RedirectResponse from pydantic import BaseModel
DB_PATH = os.environ.get("SHORTENER_DB", "shortener.db") HMAC_SECRET = os.environ.get( "HMAC_SECRET", "replace-this-in-production" ).encode()
ALLOWED_HOST = "www.example.com"
app = FastAPI()
def db(): connection = sqlite3.connect(DB_PATH) connection.row_factory = sqlite3.Row return connection
def init_db(): connection = db() connection.execute(""" CREATE TABLE IF NOT EXISTS links ( slug TEXT PRIMARY KEY, destination TEXT NOT NULL, surface TEXT NOT NULL, topic TEXT, prompt_id TEXT, asset_id TEXT, created_at TEXT NOT NULL ) """) connection.execute(""" CREATE TABLE IF NOT EXISTS clicks ( id INTEGER PRIMARY KEY AUTOINCREMENT, occurred_at TEXT NOT NULL, slug TEXT NOT NULL, surface TEXT, topic TEXT, prompt_id TEXT, referrer_host TEXT, user_agent TEXT, ip_hmac TEXT ) """) connection.commit() connection.close()
def append_query(url, params): parsed = urlsplit(url) query = dict(parse_qsl(parsed.query, keep_blank_values=True)) query.update(params) return urlunsplit(( parsed.scheme, parsed.netloc, parsed.path, urlencode(query), parsed.fragment, ))
def hash_ip(ip): return hmac.new( HMAC_SECRET, (ip or "").encode(), hashlib.sha256 ).hexdigest()[:16]
class LinkRequest(BaseModel): destination_path: str surface: str topic: str | None = None prompt_id: str | None = None asset_id: str | None = None
@app.on_event("startup") def startup(): init_db()
@app.post("/links") def create_link(payload: LinkRequest): destination = f"https://{ALLOWED_HOST}{payload.destination_path}"
if not payload.destination_path.startswith("/"):
raise HTTPException(400, "destination_path must be a site-relative path")
slug = secrets.token_urlsafe(7).replace("_", "-").replace("/", "-")
target = append_query(destination, {
"utm_source": payload.surface,
"utm_medium": "ai_answer",
"utm_campaign": "citation_measurement",
"utm_content": payload.asset_id or "unspecified",
})
connection = db()
connection.execute("""
INSERT INTO links
(slug, destination, surface, topic, prompt_id, asset_id, created_at)
VALUES (?, ?, ?, ?, ?, ?, ?)
""", (
slug,
target,
payload.surface,
payload.topic,
payload.prompt_id,
payload.asset_id,
datetime.now(timezone.utc).isoformat(),
))
connection.commit()
connection.close()
return {"slug": slug, "url": f"https://go.example.com/{slug}"}
@app.get("/{slug}") def redirect(slug: str, request: Request): connection = db() row = connection.execute( "SELECT * FROM links WHERE slug = ?", (slug,) ).fetchone()
if not row:
connection.close()
raise HTTPException(404, "Unknown link")
referrer = request.headers.get("referer", "")
referrer_host = urlsplit(referrer).hostname or ""
connection.execute("""
INSERT INTO clicks
(occurred_at, slug, surface, topic, prompt_id,
referrer_host, user_agent, ip_hmac)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
""", (
datetime.now(timezone.utc).isoformat(),
slug,
row["surface"],
row["topic"],
row["prompt_id"],
referrer_host,
request.headers.get("user-agent", "")[:300],
hash_ip(request.client.host if request.client else ""),
))
connection.commit()
connection.close()
return RedirectResponse(row["destination"], status_code=302)
Käivitage see järgmiselt:
bash pip install fastapi uvicorn uvicorn shortener:app --host 0.0.0.0 --port 8080
Kasutage mõõtmislinkide jaoks 302 ümbersuunamist, et brauserid ja vahesüsteemid püsivat ümbersuunamist püsivalt vähem tõenäoliselt vahemällu talletaksid.
7. Tehisintellekti vastuspaneelide ajastatud kraapimine
Paneelide kraapimine on kasulik küsimuste vastamiseks, mida esimese osapoole analüütika ei pruugi paljastada:
- Milliseid konkureerivaid allikaid viidatakse?
- Millist lehte viidatakse konkreetse päringu jaoks?
- Kas vastus muutub riigi või seadme järgi?
- Kas viide ilmub vastuses, allikate loendis või mõlemas?
- Kui stabiilne on vastus aja jooksul?
- Kas abistaja viitab lühilingile või kanoonilisele URL-ile?
Kraapimist tuleb aga teha hoolikalt.
Vastutustundliku kogumise reeglid
- Eelistage ametlikku API-t, litsentseeritud otsinguandmete pakkujat või esimese osapoole aruannet.
- Enne päringute automatiseerimist vaadake üle teenusepakkuja tingimused.
- Austage robotite juhiseid, kus see on kohaldatav.
- Ärge mööduge CAPTCHA-dest, sisselogimisbarjääridest, päringute piirangutest ega botikaitsetest.
- Kasutage väikest, fikseeritud päringute komplekti.
- Käivitage madala sagedusega.
- Salvestage vastus ja ajatempel, et tulemusi saaks auditeerida.
- Peatuge, kui teenusepakkuja tagastab väljakutse või juurdepääs keelatud vastuse.
Robotite välistamise protokoll on roomajate eelistuste mehhanism, mitte turvasüsteem. See ei luba juurdepääsu kaitstud sisule. RFC 9309: Robots Exclusion Protocol (rfc-editor.org)
Koostage päringute manifest
csv prompt_id,topic,query,locale,surface,target_domain pm_001,project-management,"best project management software for remote teams",en-US,google,example.com pm_001,project-management,"best project management software for remote teams",en-US,bing,example.com pm_002,analytics,"how to measure AI referral traffic",en-US,google,example.com
Hoidke päringud stabiilsena. Kui päringud muutuvad, muutuvad trendivõrdlused vähem usaldusväärseks.
Salvestage ka:
- riik
- keel
- seadme tüüp
- brauseri versioon
- päringu versioon
- sisu versioon
- kogumisaeg UTC-s
Google märgib, et AI ülevaate ja AI režiimi vastused võivad varieeruda, kuna need kasutavad erinevaid süsteeme ja võivad kasutada seotud otsinguid või “päringu hargnemist”. Google'i AI funktsioonid ja veebisaidi dokumentatsioon (developers.google.com)
Playwrighti kogumisskript
Järgnev skript on üldotstarbeline uurimisrakendus. See salvestab renderdatud lehe, ekraanipildi, lehelingid ja vastuse teksti. See ei ürita tahtlikult väljakutsetest mööda minna ega juurdepääsukontrolle murda.
python
scrape_panels.py
import asyncio import csv import hashlib import json import os import re from datetime import datetime, timezone from pathlib import Path from urllib.parse import quote_plus, urljoin, urlsplit
from playwright.async_api import async_playwright
DATA_DIR = Path("data/panels") DATA_DIR.mkdir(parents=True, exist_ok=True)
SURFACE_URLS = { "google": "https://www.google.com/search?q={query}&hl=en&gl=us", "bing": "https://www.bing.com/search?q={query}&setlang=en-us", }
PANEL_MARKERS = { "google": ["AI Overview", "AI Mode"], "bing": ["Copilot", "Generative search"], }
BLOCK_MARKERS = [ "captcha", "unusual traffic", "verify you are human", "access denied", ]
def clean_text(value): return re.sub(r"\s+", " ", value or "").strip()
def host_matches(url, target_domain): host = (urlsplit(url).hostname or "").lower() target = target_domain.lower().lstrip("www.") return host == target or host.endswith("." + target)
def page_hash(text): normalized = clean_text(text).encode("utf-8") return hashlib.sha256(normalized).hexdigest()
async def collect_one(page, row): surface = row["surface"] query = row["query"] target_domain = row["target_domain"]
search_url = SURFACE_URLS[surface].format(
query=quote_plus(query)
)
await page.goto(
search_url,
wait_until="domcontentloaded",
timeout=45_000,
)
await page.wait_for_timeout(2500)
text = await page.locator("body").inner_text()
lowered = text.lower()
if any(marker in lowered for marker in BLOCK_MARKERS):
return {
**row,
"observed_at": datetime.now(timezone.utc).isoformat(),
"status": "blocked_or_challenged",
"panel_present": False,
"links": [],
}
links = await page.locator("a").evaluate_all("""
anchors => anchors.map(a => ({
href: a.href || "",
text: (a.innerText || "").trim()
})).filter(x => x.href)
""")
panel_present = any(
marker.lower() in lowered
for marker in PANEL_MARKERS.get(surface, [])
)
observed_at = datetime.now(timezone.utc)
run_id = hashlib.sha256(
f"{surface}|{row['prompt_id']}|{observed_at.isoformat()}".encode()
).hexdigest()[:20]
html_path = DATA_DIR / f"{run_id}.html"
screenshot_path = DATA_DIR / f"{run_id}.png"
await page.screenshot(path=str(screenshot_path), full_page=True)
html_path.write_text(await page.content(), encoding="utf-8")
output_links = []
for position, link in enumerate(links, start=1):
output_links.append({
"href": urljoin(search_url, link["href"]),
"text": clean_text(link["text"])[:300],
"position": position,
"target_cited": host_matches(
link["href"],
target_domain
),
})
return {
**row,
"run_id": run_id,
"observed_at": observed_at.isoformat(),
"status": "ok",
"panel_present": panel_present,
"answer_hash": page_hash(text),
"answer_text": clean_text(text)[:50_000],
"html_path": str(html_path),
"screenshot_path": str(screenshot_path),
"link_scope": "page_fallback",
"links": output_links,
}
async def main(manifest_path): rows = [] with open(manifest_path, newline="", encoding="utf-8") as source: rows = list(csv.DictReader(source))
output_path = DATA_DIR / (
datetime.now(timezone.utc).strftime("%Y-%m-%d") + ".jsonl"
)
async with async_playwright() as playwright:
browser = await playwright.chromium.launch(headless=True)
context = await browser.new_context(
locale="en-US",
timezone_id="UTC",
)
page = await context.new_page()
with open(output_path, "w", encoding="utf-8") as output:
for row in rows:
try:
result = await collect_one(page, row)
except Exception as exc:
result = {
**row,
"observed_at": datetime.now(timezone.utc).isoformat(),
"status": f"error:{type(exc).__name__}",
"panel_present": False,
"links": [],
}
output.write(json.dumps(result) + "\
") output.flush()
# Keep request volume low.
await asyncio.sleep(8)
await browser.close()
if name == "main": asyncio.run(main("prompts.csv"))
Paigaldage ja käivitage:
bash pip install playwright playwright install chromium python scrape_panels.py
Ajastage see lukuga, et kaks käivitust ei kattuks:
cron 15 3 * * * flock -n /tmp/ai-panel.lock \ /usr/bin/python3 /opt/ai-measure/scrape_panels.py
Täpsuse parandamine pinnadapteritega
Varuskript salvestab leheküljetaseme lingid. See on kasulik avastamiseks, kuid võib sisaldada tavalisi otsingutulemuste linke. Tootmiskasutuseks:
- Kasutage litsentseeritud vastuspaneeli API-t, kui see on saadaval.
- Looge iga pinna jaoks eraldi valija adapter.
- Salvestage valija versioon.
- Salvestage toores HTML ja ekraanipilt.
- Märgistage iga kirje
link_scope = "panel_selector"võilink_scope = "page_fallback"abil.
Arvutage viidete osakaalu ainult panel_selector andmete põhjal, kui suudate paneeli usaldusväärselt tuvastada. Hoidke varuvaatlused eraldi uurimuslikus aruandes.
8. Google Analyticsi ja BigQuery integreerimine
Google Analytics 4 saab eksportida toores sündmuseandmeid BigQuerysse. Igapäevased eksportimised pakuvad stabiilseid tabeleid nagu events_YYYYMMDD; voogedastuse eksportimised on kasulikud peaaegu reaalajas jälgimiseks, kuid võivad olla mittetäielikud kuni igapäevase tabeli lõplikuks valmimiseni. Google Analyticsi BigQuery eksport (support.google.com)
Lihtsustatud seansipäring võiks välja näha selline:
sql WITH page_events AS ( SELECT event_date, user_pseudo_id,
(
SELECT value.int_value
FROM UNNEST(event_params)
WHERE key = 'ga_session_id'
) AS session_id,
(
SELECT value.string_value
FROM UNNEST(event_params)
WHERE key = 'page_location'
) AS page_location,
(
SELECT value.string_value
FROM UNNEST(event_params)
WHERE key = 'session_engaged'
) AS session_engaged,
event_name
FROM PROJECT.analytics_PROPERTY.events_*
WHERE _TABLE_SUFFIX BETWEEN '20260901' AND '20260921'
),
session_rollup AS ( SELECT event_date, user_pseudo_id, session_id,
MAX(
CASE
WHEN REGEXP_CONTAINS(
LOWER(page_location),
r'[?&]utm_medium=ai_answer'
)
THEN 1 ELSE 0
END
) AS ai_session,
MAX(
CASE
WHEN session_engaged = '1'
THEN 1 ELSE 0
END
) AS engaged_session,
MAX(
CASE
WHEN event_name IN ('purchase', 'generate_lead', 'sign_up')
THEN 1 ELSE 0
END
) AS key_event
FROM page_events GROUP BY event_date, user_pseudo_id, session_id )
SELECT event_date, COUNTIF(ai_session = 1) AS ai_sessions, COUNTIF(ai_session = 1 AND engaged_session = 1) AS engaged_ai_sessions, COUNTIF(ai_session = 1 AND key_event = 1) AS ai_key_events, SAFE_DIVIDE( COUNTIF(ai_session = 1 AND engaged_session = 1), COUNTIF(ai_session = 1) ) AS post_citation_engagement_rate FROM session_rollup GROUP BY event_date ORDER BY event_date;
Küpse implementatsiooni puhul lisage surface, topic ja prompt_id kampaania struktuuri või ühendage need lühendaja klikkide tabelist.
Ärge looge igale üksikule kasutajale või seansile kõrge kardinaalsusega analüütikadimõõdet. Google hoiatab, et liigsed kõrge kardinaalsusega kohandatud dimensioonid võivad aruandluse kvaliteeti vähendada. Google Analyticsi kohandatud dimensioonid ja mõõdikud (support.google.com)
9. Armatuurlaua disain
Praktiline armatuurlaud peaks sisaldama viit vaadet.
Juhtkonna kokkuvõte
Kuvage:
- Viidete osakaal teema järgi
- Viidete katvus teema järgi
- Kinnitatud abistaja suunamismäär
- Laiendatud abistaja suunamismäär
- Viitejärgne kaasamine
- AI roomaja edukuse määr
- AI-le omistatud põhisündmused või tulu
Viidete osakaalu trend
Joongraafik:
text Kuupäev × Teema × Platvorm
Näidake mõlemat:
- sihtdomeeni viidete esinemised
- kõik täheldatud viidete esinemised
Abistaja liikluse lehter
text Vaadeldud viited ↓ Kontrollitud lingiklikid ↓ AI-le omistatud seansid ↓ Kaasatud AI seansid ↓ Põhisündmused
Iga etapp peaks näitama oma omistamise usaldusväärsust.
Roomaja seisund
Tabel roomajate kaupa:
text roomaja_nimi päringud edukad_päringud veamäär unikaalsete_IP-de_arv populaarsemad_teed
Teavita, kui:
- veamäär ületab 2 protsenti
- roomaja kaob ootamatult
- varem ligipääsetav sisuosa tagastab 403 või 429
- roomaja päringud koondavad soovimatule privaatsele teele
Sisu võimaluste tabel
Järjestage lehed ja teemad järgmiselt:
text kõrge viidete osakaal + madal suunamismäär kõrged näitamised + madal viidete katvus kõrge kaasamine + madal viidete nähtavus kõrged roomaja vead + kõrge äriline tähtsus
Need kombinatsioonid annavad tavaliselt paremaid otsuseid kui ainult liikluse mahu vaatlemine.
Minimaalne Streamlit armatuurlaud
Salvestage kui dashboard.py.
python import glob import json from pathlib import Path
import pandas as pd import streamlit as st
st.set_page_config( page_title="AI Citation Impact", layout="wide", )
@st.cache_data(ttl=300) def load_panel_rows(): rows = []
for filename in glob.glob("data/panels/*.jsonl"):
with open(filename, encoding="utf-8") as source:
for line in source:
record = json.loads(line)
run_id = record.get("run_id")
for link in record.get("links", []):
hostname = link.get("href", "").split("/")[2]
rows.append({
"run_id": run_id,
"observed_at": record.get("observed_at"),
"surface": record.get("surface"),
"topic": record.get("topic"),
"prompt_id": record.get("prompt_id"),
"panel_present": record.get("panel_present"),
"target_domain": record.get("target_domain"),
"cited_domain": hostname,
"target_cited": link.get("target_cited", False),
"position": link.get("position"),
"link_scope": record.get("link_scope"),
})
return pd.DataFrame(rows)
panels = load_panel_rows()
st.title("AI Citation Impact Dashboard")
if panels.empty: st.warning("No panel observations found.") st.stop()
panels["date"] = pd.to_datetime( panels["observed_at"], errors="coerce" ).dt.date
citation_share = ( panels.groupby("topic") .apply( lambda group: group["target_cited"].sum() / max(len(group), 1) ) .reset_index(name="citation_share") )
coverage = ( panels.groupby(["topic", "run_id"], dropna=False) .agg( panel_present=("panel_present", "max"), target_cited=("target_cited", "max"), ) .reset_index() )
coverage = ( coverage[coverage["panel_present"] == True] .groupby("topic") .apply(lambda group: group["target_cited"].mean()) .reset_index(name="citation_coverage") )
col1, col2 = st.columns(2)
with col1: st.subheader("Citation Share by Topic") st.dataframe(citation_share, use_container_width=True) st.bar_chart( citation_share.set_index("topic")["citation_share"] )
with col2: st.subheader("Citation Coverage by Topic") st.dataframe(coverage, use_container_width=True) st.bar_chart( coverage.set_index("topic")["citation_coverage"] )
st.subheader("Citation Position") st.dataframe( panels.groupby(["topic", "surface"], dropna=False) .agg( citations=("target_cited", "sum"), median_position=("position", "median"), observations=("run_id", "nunique"), ) .reset_index(), use_container_width=True, )
st.subheader("Daily Observations") daily = ( panels.groupby(["date", "surface"], dropna=False) .agg( citations=("target_cited", "sum"), observed_links=("cited_domain", "count"), ) .reset_index() )
st.line_chart( daily.pivot( index="date", columns="surface", values="citations" ).fillna(0) )
Käivitage see järgmiselt:
bash pip install streamlit pandas streamlit run dashboard.py
Tootmises laadige normaliseeritud tabelid otse DuckDB-sse, BigQuerysse, Snowflakisse või PostgreSQL-i, selle asemel et lugeda toorfailidest otse.
10. Kvaliteedikontrollid
Mõõtmissüsteem võib anda täpse välimusega, kuid eksitavaid numbreid. Lisage need kontrollid.
Eraldage robotid inimestest
Ärge kunagi loendage OAI-SearchBot'i, GPTBot'i, ClaudeBot'i ega PerplexityBot'i inimeste seanssidena. Need on roomaja- või hangitavate signaalid.
Säilitage toortõendid
Iga paneeli käivituse kohta säilitage:
- toores HTML
- ekraanipilt
- vastuse tekst
- ajatempel
- brauseri ja kogumise versioon
- päring ja lokaat
- vastuse räsi
See võimaldab selgitada, miks mõõdik muutus.
Jälgige paneeli puudumist
Puuduv AI paneel ei ole sama mis vastus ilma teie viiteta.
Kasutage:
text paneel_ei_ole_kohal paneel_kohal_sihtmärk_puudub paneel_kohal_sihtmärk_viidatud blokeeritud_või_väljakutse_esitatud kogumise_viga
Kontrollige valimi kallutatust
Ajastatud kraapimist mõjutavad:
- asukoht
- seade
- keel
- aeg
- isikupärastamine
- brauseri olek
- päringu sõnastus
Kasutage fikseeritud põhipäringute komplekti ja roteeruvat uurivate päringute komplekti. Hoidke need kaks rühma aruannetes eraldi.
Vältige vale täpsust
Esitage usaldusvahemikud või minimaalsed valimi suurused väikeste teemagruppide kohta. Kolme vaatlusega teemat ei tohiks otse võrrelda teemaga, mis sisaldab 300 vaatlust.
Võrrelge võrreldavaid
Platvormide võrdlemisel hoidke konstantsena:
- päring
- lokaat
- riik
- seade
- kogumistund
- sihtdomeeni reeglid
- viiteparseri versioon
11. Soovitatav juurutamiskava
1. nädal: Instrumenteerimine
- Konfigureerige struktureeritud serverilogid.
- Looge roboti register.
- Looge lühendaja domeen.
- Lisage kampaania nimetamise reeglid.
- Määratlege teema taksonoomia.
- Lubage Google Analytics 4 BigQuery eksport.
2. nädal: Kontrollitud lingid
- Avaldage viis kuni kümme jälgitavat kokkuvõtet.
- Looge lingid erinevatele teemadele ja sisuversioonidele.
- Valideerige ümbersuunamislogid analüütika seansside vastu.
- Kinnitage, et URL-idesse ega analüütika parameetritesse ei sisestata isiklikke andmeid.
3. nädal: Paneeli vaatlused
- Looge päringute manifest.
- Alustage kahe platvormi ja 20 kuni 50 stabiilse päringuga.
- Käivitage üks kord päevas.
- Salvestage ekraanipildid ja HTML.
- Mõõtke kogumise ebaõnnestumisi viidete puudumisest eraldi.
4. nädal: Armatuurlaud ja haldus
- Looge teema- ja platvormipõhised armatuurlauad.
- Lisage roomajate seisundi hoiatused.
- Määratlege usaldusreeglid.
- Võrrelge esimese osapoole aruandeid paneeli vaatlustega.
- Dokumendi säilitamise, juurdepääsu ja kustutamise poliitikad.
Kokkuvõte
Tehisintellekti viidete mõõtmine tuleks ehitada mitme signaali omistamissüsteemina, mitte ühe suunamisaruandena.
Kõige usaldusväärsem raamistik kombineerib:
- Google'i ja Bingi esimese osapoole nähtavuse aruanded
- serverilogid roomaja ja sisu laadimise tegevuse kohta
- esimese osapoole lühilingid kontrollitud klikimõõtmiseks
- ajastatud, nõuetele vastavad vastuspaneeli vaatlused
- analüütika ja konversiooniandmed allavoolu väärtuse jaoks
Kolm kõige olulisemat mõõdikut on:
- Viidete osakaal teema järgi — kui suur osa vaadeldud allikate ruumist kuulub teie domeenile.
- Abistaja suunamismäär — kui suur osa inimliiklusest on omistatav tehisintellekti platvormidele.
- Viitejärgne kaasamine — kas tehisintellekti kaudu suunatud külastajad teevad sisukaid tegevusi.
Süsteem muutub kasulikuks, kui iga number sisaldab oma platvormi, ajaperioodi, nimetajat, omistamise meetodit ja usaldusväärsuse taset. See distsipliin takistab roomaja tabamuste eksimist kasutajate vastu, kraabitud vastuste eksimist näitamiste vastu ja viidete eksimist äritulemuste vastu.
Auto