AutoPodAutoPod

Mengukur Dampak Kutipan AI: Analitik, Log, dan Pengambilan Panel

•23 menit baca
Artikel Audio
Mengukur Dampak Kutipan AI: Analitik, Log, dan Pengambilan Panel
0:00—:—
Mengukur Dampak Kutipan AI: Analitik, Log, dan Pengambilan Panel

Mengukur Dampak Kutipan AI: Analitik, Log, dan Pengambilan Panel

Pendahuluan

Penemuan yang didorong AI kini terbagi dalam beberapa lapisan pengukuran. Seseorang mungkin melihat perusahaan dikutip dalam jawaban AI, mengeklik tautan tanpa perujuk yang berguna, mengunjungi melalui peramban yang menghilangkan data kampanye, atau membaca jawaban tanpa mengeklik sama sekali. Sementara itu, perayap AI mungkin meminta halaman perusahaan tanpa merepresentasikan pengunjung manusia.

Oleh karena itu, sistem pengukuran yang berguna harus memisahkan empat pertanyaan:

  1. Apakah situs dirayapi atau diambil?
  2. Apakah situs dikutip dalam jawaban AI?
  3. Apakah seseorang mengunjungi situs dari jawaban tersebut?
  4. Apakah kunjungan tersebut menghasilkan keterlibatan atau pendapatan yang berarti?

Per 22 September 2026, pelaporan pihak pertama membaik. Google Search Console menyediakan laporan performa Generative AI untuk AI Overviews dan AI Mode, termasuk tayangan, halaman, negara, perangkat, dan tanggal. Laporan ini tersedia di seluruh dunia pada 31 Agustus 2026. Laporan performa Generative AI Google (support.google.com)

Microsoft juga menyediakan laporan Performa AI di Bing Webmaster Tools. Pratinjau publiknya melaporkan total kutipan, halaman yang dikutip, kueri yang menjadi dasar, dan tren kutipan di seluruh pengalaman AI Microsoft yang didukung. Performa AI Bing Webmaster Tools (blogs.bing.com)

Alat-alat ini harus diperlakukan sebagai otoritatif untuk permukaannya sendiri, tetapi mereka tidak menciptakan sistem pengukuran lintas-asisten yang lengkap. Pendekatan yang direkomendasikan adalah menggabungkan laporan pihak pertama, log server, tautan pelacakan terkontrol, ekspor analitik, dan observasi panel jawaban terjadwal.


1. Mengapa Pengukuran Rujukan AI Sulit

Analitik web tradisional sangat bergantung pada header Referer dan parameter kampanye. Keduanya bisa hilang atau tidak lengkap.

Header HTTP Referer dapat berisi halaman sebelumnya, hanya asal situs web sebelumnya, atau tidak ada nilai sama sekali. Kebijakan perujuk peramban dapat secara sengaja membatasi atau menghapus informasi tersebut. Dokumentasi Referrer-Policy MDN (developer.mozilla.org)

Sistem AI menciptakan komplikasi tambahan:

  • Asisten dapat menautkan ke suatu halaman tanpa mengirim perujuk yang dapat dikenali.
  • Asisten dapat menggunakan URL pengalihan atau yang ditulis ulang.
  • Asisten dapat mengambil konten sisi server sebelum menampilkan jawaban.
  • Perayap AI dapat mengunjungi halaman tanpa ada manusia yang melihatnya.
  • Jawaban dapat mengutip URL kanonis alih-alih URL yang awalnya diambil.
  • Jawaban dapat berubah tergantung pada lokasi, perangkat, bahasa, waktu, atau personalisasi.

Sebagai contoh, OpenAI membedakan antara OAI-SearchBot untuk penemuan pencarian, GPTBot untuk kemungkinan penggunaan pelatihan, dan ChatGPT-User untuk tindakan yang diprakarsai pengguna. Perplexity secara terpisah mengidentifikasi PerplexityBot dan Perplexity-User. Anthropic mengidentifikasi ClaudeBot, Claude-SearchBot, dan Claude-User. Permintaan ini tidak boleh digabungkan menjadi satu angka “lalu lintas AI” generik. Dokumentasi perayap OpenAI (openai.com) Dokumentasi perayap Perplexity (docs.perplexity.ai) Dokumentasi perayap Anthropic (support.anthropic.com)

Aturan utamanya adalah:

Aktivitas perayap bukanlah lalu lintas rujukan, visibilitas kutipan bukanlah lalu lintas, dan lalu lintas bukanlah dampak bisnis.


2. Kerangka Pengukuran

Gunakan lima lapisan pengukuran.

LapisanPertanyaan utamaData yang direkomendasikan
AksesApakah sistem AI meminta halaman?Log server web, log firewall
VisibilitasApakah halaman ditampilkan atau dikutip?Google Search Console, Bing Webmaster Tools, observasi panel jawaban
AkuisisiApakah seseorang datang dari permukaan AI?Perujuk, parameter kampanye, klik tautan pendek, analitik
KeterlibatanApakah pengunjung berinteraksi secara berarti?Peristiwa analitik, tampilan halaman, waktu keterlibatan
NilaiApakah kunjungan menghasilkan hasil yang diinginkan?Pendaftaran, prospek, pembelian, langganan

Hierarki sumber kebenaran yang direkomendasikan

Gunakan sinyal terkuat yang tersedia untuk setiap pertanyaan:

  1. Laporan platform pihak pertama untuk tayangan dan kutipan.
  2. Observasi panel jawaban untuk visibilitas tingkat permintaan dan perbandingan kompetitif.
  3. Log pengalihan untuk klik tautan terkontrol.
  4. Analitik web untuk sesi, keterlibatan, dan konversi.
  5. Log server untuk aktivitas perayap dan pengambil.
  6. Estimasi model hanya jika bukti langsung tidak tersedia.

Jangan gabungkan semua sinyal menjadi satu angka tanpa mempertahankan sumber dan tingkat kepercayaan.


3. Model Data Inti

Buat gudang data dengan tabel terpisah untuk observasi mentah dan metrik turunan.

panel_runs

Satu baris untuk setiap pemeriksaan permintaan dan permukaan.

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

Satu baris untuk setiap tautan yang diamati dalam panel jawaban.

text run_id surface topic prompt_id cited_url cited_domain citation_position link_scope target_domain target_cited

ai_crawler_requests

Data log server yang diagregasikan.

text date bot_name bot_role path requests successful_requests error_requests unique_ip_count

shortener_clicks

Satu baris untuk setiap permintaan tautan terkontrol.

text click_id occurred_at slug surface topic prompt_id asset_id referrer_host user_agent_family ip_hmac destination_path

ai_sessions

Tabel analitik yang dinormalisasi.

text session_id date surface topic prompt_id landing_page attribution_method confidence engaged engagement_time_seconds key_event revenue

Kolom confidence penting. Nilai yang direkomendasikan adalah:

  • A — Langsung: klik tautan pendek terkontrol atau parameter kampanye tepercaya.
  • B — Kuat: perujuk asisten yang dikenal atau permintaan pengambilan pengguna yang diverifikasi.
  • C — Mungkin: bukti kampanye atau halaman arahan tanpa perujuk lengkap.
  • D — Dimodelkan: disimpulkan dari waktu, observasi panel, atau perubahan agregat.

Laporkan A dan B secara terpisah dari C dan D.


4. Definisi Metrik

Pangsa Kutipan berdasarkan Topik

Pangsa Kutipan berdasarkan Topik mengukur seberapa sering domain target muncul di antara sumber-sumber yang diamati untuk suatu topik.

[ \text{Pangsa Kutipan}_{topik}

\frac{\text{Kejadian kutipan domain target}} {\text{Semua kejadian kutipan}} ]

Contoh:

  • 100 total kutipan diamati untuk “perangkat lunak manajemen proyek”
  • 12 kutipan menunjuk ke domain Anda
  • Pangsa Kutipan berdasarkan Topik = 12%

Metrik ini mengukur pangsa sumber, bukan apakah suatu jawaban berisi situs Anda setidaknya sekali.

Cakupan Kutipan

Gunakan metrik kedua untuk keberadaan tingkat jawaban:

[ \text{Cakupan Kutipan}_{topik}

\frac{\text{Jalankan jawaban yang berisi kutipan target}} {\text{Jalankan jawaban tempat panel hadir}} ]

Cakupan Kutipan seringkali lebih mudah dipahami oleh audiens non-teknis:

“Situs kami muncul di 34% jawaban AI yang diamati untuk topik ini.”

Posisi Kutipan

Catat posisi kutipan dalam jawaban:

  • 1 = sumber pertama yang diamati
  • 2 = sumber kedua yang diamati
  • 3 = sumber ketiga yang diamati

Karena antarmuka AI sering mengelompokkan atau menyusun ulang sumber, perlakukan posisi sebagai observasi deskriptif, bukan peringkat universal.


Tingkat Rujukan Asisten

Tingkat Rujukan Asisten mengukur proporsi sesi situs manusia yang dapat diatribusikan ke permukaan AI.

[ \text{Tingkat Rujukan Asisten}

\frac{\text{Sesi manusia yang diatribusikan AI}} {\text{Semua sesi manusia}} ]

Laporkan setidaknya dua versi:

text Tingkat Rujukan Asisten yang Dikonfirmasi = sesi AI kepercayaan A / semua sesi manusia

Tingkat Rujukan Asisten yang Diperluas = sesi AI kepercayaan A + B + C / semua sesi manusia

Versi yang dikonfirmasi lebih konservatif. Versi yang diperluas berguna untuk analisis tren tetapi tidak boleh disajikan sebagai lalu lintas yang sepenuhnya diamati.

Jangan gunakan panel berjalan sebagai tayangan

Jika Anda mengambil 500 permintaan, itu tidak berarti 500 orang melihat jawaban tersebut. Scraper Anda mengukur sampel terkontrol dari status jawaban. Gunakan laporan tayangan platform pihak pertama jika tersedia.


Keterlibatan Pasca-Kutipan

Keterlibatan Pasca-Kutipan mengukur apakah orang yang datang melalui kutipan AI mengambil tindakan yang berarti.

[ \text{Keterlibatan Pasca-Kutipan}

\frac{\text{Sesi AI yang terlibat}} {\text{Sesi yang diatribusikan AI}} ]

Untuk Google Analytics 4, sesi yang terlibat berlangsung lebih dari 10 detik, menyertakan peristiwa penting, atau menyertakan setidaknya dua tampilan halaman atau layar. Dokumentasi tingkat keterlibatan Google Analytics (support.google.com)

Juga laporkan:

text Waktu Keterlibatan Pasca-Kutipan = median detik terlibat per sesi AI

Tingkat Konversi Pasca-Kutipan = sesi AI dengan peristiwa penting / sesi yang diatribusikan AI

Pendapatan Pasca-Kutipan per Sesi = pendapatan dari sesi AI / sesi yang diatribusikan AI

Waktu keterlibatan median biasanya lebih stabil daripada waktu keterlibatan rata-rata karena sejumlah kecil sesi yang sangat panjang dapat mendistorsi rata-rata.


Metrik Berguna Tambahan

Tingkat Keberhasilan Perayap AI

[ \text{Tingkat Keberhasilan Perayap}

\frac{\text{Permintaan perayap AI yang mengembalikan 2xx atau 3xx}} {\text{Permintaan perayap AI}} ]

Uraikan ini berdasarkan bot dan jalur. Penurunan kutipan yang tinggi dikombinasikan dengan tingkat kesalahan perayap yang meningkat dapat mengindikasikan masalah hosting, firewall, atau konfigurasi robots.

Tingkat Kutipan-ke-Klik

Untuk observasi panel jawaban yang terkontrol:

[ \text{Tingkat Kutipan-ke-Klik yang Diamati}

\frac{\text{Klik tautan pendek yang dikonfirmasi}} {\text{Kutipan target yang diamati}} ]

Ini bukan tingkat klik-tayang pengguna yang sebenarnya kecuali jika penyebutnya merepresentasikan tayangan pengguna yang sebenarnya. Labeli dengan jelas sebagai metrik operasional yang diamati.

Kesegaran Kutipan

Ukur seberapa sering URL yang dikutip mencerminkan versi halaman saat ini.

text Kesegaran Kutipan = halaman yang dikutip dengan hash konten saat ini / halaman yang dikutip

Ini berguna untuk halaman dokumentasi, harga, produk, dan kebijakan yang sering berubah.


5. Analisis Log Server

Konfigurasi Log Akses Terstruktur

Log terstruktur lebih mudah dianalisis daripada log tradisional yang dipisahkan spasi.

Contoh konfigurasi Nginx:

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;

Simpan file log di lokasi terbatas. Jangan kirim alamat IP mentah ke tabel analitik umum kecuali jika Anda memiliki alasan operasional yang jelas.

Identifikasi Jenis Permintaan AI

Minimal, pisahkan:

text OpenAI:

  • OAI-SearchBot
  • ChatGPT-User
  • GPTBot

Anthropic:

  • Claude-SearchBot
  • Claude-User
  • ClaudeBot

Perplexity:

  • PerplexityBot
  • Perplexity-User

Mesin pencari:

  • Googlebot
  • Bingbot

Fitur Pencarian AI Google umumnya menggunakan kontrol perayapan Google Search yang sudah ada daripada “bot AI Overview” universal yang terpisah. Google menyatakan bahwa Googlebot mengontrol akses untuk Search, termasuk fitur AI. Fitur AI dan situs web Google (developers.google.com)

String agen-pengguna dapat dipalsukan. Untuk keputusan operasional, gabungkan pencocokan agen-pengguna dengan rentang IP yang diterbitkan penyedia atau DNS terbalik yang terverifikasi jika tersedia. OpenAI dan Perplexity menerbitkan informasi IP untuk perayap dan pengambil relevan mereka. (openai.com)

Ringkasan Log yang Dapat Direproduksi

Simpan yang berikut sebagai 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])

Jalankan dengan:

bash HMAC_SECRET='a-long-random-secret'
python ai_log_summary.py /var/log/nginx/ai-access.jsonl \

data/ai_crawler_daily.csv

Ini menghasilkan bukti operasional tentang aktivitas pengambilan, bukan lalu lintas manusia.


6. Pemendek Tautan Kustom untuk Ringkasan yang Menghadap AI

Pemendek tautan pihak pertama menyediakan titik pengukuran terkontrol saat Anda menerbitkan ringkasan atau materi sumber yang mungkin dirujuk oleh sistem AI.

Gunakan domain yang Anda kontrol, seperti:

text https://go.example.com/ai-docs

Tujuan harus menyertakan parameter kampanye seperti:

text utm_source=perplexity utm_medium=ai_answer utm_campaign=product_documentation utm_content=pricing_summary_v3

Google merekomendasikan parameter kampanye untuk mengidentifikasi sumber lalu lintas dan kampanye. Dokumentasi URL kampanye Google Analytics (support.google.com)

Bidang tautan yang direkomendasikan

text surface topic prompt_id asset_id content_version destination_path

Jangan masukkan informasi pribadi, alamat email, atau kueri pencarian pengguna ke dalam tautan. Pengidentifikasi kampanye harus menggambarkan aset yang diterbitkan, bukan mengidentifikasi seseorang.

Keterbatasan penting

Tautan pendek adalah perangkat kalibrasi, bukan solusi universal:

  • Asisten dapat menulis ulang URL tujuan.
  • Asisten dapat mengutip URL kanonis sebagai gantinya.
  • Tautan mungkin ditampilkan tetapi tidak pernah diklik.
  • Peramban atau alat privasi dapat menghapus perujuk.
  • Beberapa sistem dapat mengambil tujuan sisi server.
  • Tautan pendek tidak mengukur pengguna yang mengingat merek dan kembali nanti.

Jangan gunakan pemendek untuk menyembunyikan tujuan, melewati sistem keamanan, atau memanipulasi kutipan. Gunakan pengalihan yang jelas ke halaman yang relevan dan kanonis.

Pemendek Pihak Pertama Minimal

Contoh berikut menggunakan FastAPI dan SQLite. Lindungi titik akhir pembuatan tautan dengan otentikasi sebelum menggunakannya dalam produksi.

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)

Jalankan dengan:

bash pip install fastapi uvicorn uvicorn shortener:app --host 0.0.0.0 --port 8080

Gunakan pengalihan 302 untuk tautan pengukuran agar peramban dan sistem perantara cenderung tidak menyimpan pengalihan secara permanen.


7. Pengambilan Terjadwal Panel Jawaban AI

Pengambilan panel berguna untuk menjawab pertanyaan yang mungkin tidak diungkapkan oleh analitik pihak pertama:

  • Sumber pesaing mana yang dikutip?
  • Halaman mana yang dikutip untuk permintaan tertentu?
  • Apakah jawaban berubah berdasarkan negara atau perangkat?
  • Apakah kutipan muncul dalam jawaban, daftar sumber, atau keduanya?
  • Seberapa stabil jawaban seiring waktu?
  • Apakah asisten mengutip tautan pendek atau URL kanonis?

Namun, pengambilan harus dilakukan dengan hati-hati.

Aturan untuk pengumpulan yang bertanggung jawab

  1. Pilih API resmi, penyedia data pencarian berlisensi, atau laporan pihak pertama.
  2. Tinjau ketentuan penyedia sebelum mengotomatiskan permintaan.
  3. Hormati instruksi robot jika berlaku.
  4. Jangan melewati CAPTCHA, penghalang masuk, batas laju, atau perlindungan bot.
  5. Gunakan kumpulan permintaan kecil dan tetap.
  6. Jalankan pada frekuensi rendah.
  7. Simpan respons dan stempel waktu agar hasilnya dapat diaudit.
  8. Berhenti ketika penyedia mengembalikan tantangan atau respons akses ditolak.

Protokol Pengecualian Robot adalah mekanisme preferensi perayap, bukan sistem keamanan. Ini tidak mengizinkan akses ke konten yang dilindungi. RFC 9309: Robots Exclusion Protocol (rfc-editor.org)

Bangun Manifest Permintaan

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

Pertahankan permintaan tetap stabil. Jika permintaan berubah, perbandingan tren menjadi kurang dapat diandalkan.

Juga simpan:

  • negara
  • bahasa
  • jenis perangkat
  • versi peramban
  • versi permintaan
  • versi konten
  • waktu pengumpulan dalam UTC

Google mencatat bahwa respons AI Overview dan AI Mode dapat bervariasi karena mereka menggunakan sistem yang berbeda dan dapat menggunakan pencarian terkait atau “query fan-out.” Dokumentasi fitur AI dan situs web Google (developers.google.com)

Skrip Pengumpulan Playwright

Skrip berikut adalah kerangka penelitian tujuan umum. Ini merekam halaman yang dirender, tangkapan layar, tautan halaman, dan teks jawaban. Secara sengaja, ini tidak melewati tantangan atau mencoba mengalahkan kontrol akses.

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) + "\n")
            output.flush()

            # Keep request volume low.
            await asyncio.sleep(8)

    await browser.close()

if name == "main": asyncio.run(main("prompts.csv"))

Instal dan jalankan:

bash pip install playwright playwright install chromium python scrape_panels.py

Jadwalkan dengan kunci agar dua proses tidak tumpang tindih:

cron 15 3 * * * flock -n /tmp/ai-panel.lock
/usr/bin/python3 /opt/ai-measure/scrape_panels.py

Tingkatkan akurasi dengan adaptor permukaan

Skrip fallback merekam tautan tingkat halaman. Ini berguna untuk penemuan tetapi mungkin menyertakan tautan hasil pencarian biasa. Untuk penggunaan produksi:

  • Gunakan API panel jawaban berlisensi jika tersedia.
  • Buat adaptor selektor terpisah untuk setiap permukaan.
  • Simpan versi selektor.
  • Simpan HTML mentah dan tangkapan layar.
  • Tandai setiap rekaman dengan link_scope = "panel_selector" atau link_scope = "page_fallback".

Hanya hitung Pangsa Kutipan dari data panel_selector ketika Anda dapat mengidentifikasi panel dengan andal. Simpan observasi fallback dalam laporan eksplorasi terpisah.


8. Integrasi Google Analytics dan BigQuery

Google Analytics 4 dapat mengekspor data peristiwa mentah ke BigQuery. Ekspor harian menyediakan tabel stabil seperti events_YYYYMMDD; ekspor streaming berguna untuk pemantauan mendekati waktu nyata tetapi mungkin tidak lengkap sampai tabel harian difinalisasi. Ekspor BigQuery Google Analytics (support.google.com)

Kueri sesi yang disederhanakan mungkin terlihat seperti ini:

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;

Untuk implementasi yang matang, tambahkan surface, topic, dan prompt_id ke struktur kampanye atau gabungkan dari tabel klik pemendek.

Jangan membuat dimensi analitik berkapasitas tinggi untuk setiap pengguna atau sesi individu. Google memperingatkan bahwa dimensi kustom berkapasitas tinggi yang berlebihan dapat mengurangi kualitas pelaporan. Dimensi dan metrik kustom Google Analytics (support.google.com)


9. Desain Dasbor

Dasbor praktis harus berisi lima tampilan.

Ringkasan Eksekutif

Tampilkan:

  • Pangsa Kutipan berdasarkan Topik
  • Cakupan Kutipan berdasarkan Topik
  • Tingkat Rujukan Asisten yang Dikonfirmasi
  • Tingkat Rujukan Asisten yang Diperluas
  • Keterlibatan Pasca-Kutipan
  • Tingkat keberhasilan perayap AI
  • Peristiwa penting atau pendapatan yang diatribusikan AI

Tren Pangsa Kutipan

Bagan garis:

text Tanggal × Topik × Permukaan

Tampilkan keduanya:

  • kejadian kutipan domain target
  • semua kejadian kutipan yang diamati

Corong Lalu Lintas Asisten

text Kutipan yang diamati ↓ Klik tautan terkontrol ↓ Sesi yang diatribusikan AI ↓ Sesi AI yang terlibat ↓ Peristiwa penting

Setiap tahap harus menampilkan kepercayaan atribusinya.

Kesehatan Perayap

Tabel berdasarkan bot:

text bot_name requests successful_requests error_rate unique_ip_count top_paths

Peringatan ketika:

  • tingkat kesalahan melebihi 2 persen
  • bot tiba-tiba menghilang
  • bagian konten yang sebelumnya dapat diakses mengembalikan 403 atau 429
  • permintaan perayap terkonsentrasi pada jalur pribadi yang tidak disengaja

Tabel Peluang Konten

Peringkat halaman dan topik berdasarkan:

text pangsa kutipan tinggi + tingkat rujukan rendah tayangan tinggi + cakupan kutipan rendah keterlibatan tinggi + visibilitas kutipan rendah kesalahan perayap tinggi + kepentingan bisnis tinggi

Kombinasi ini biasanya menghasilkan keputusan yang lebih baik daripada hanya melihat volume lalu lintas.

Dasbor Streamlit Minimal

Simpan sebagai 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) )

Jalankan dengan:

bash pip install streamlit pandas streamlit run dashboard.py

Untuk produksi, muat tabel yang dinormalisasi ke DuckDB, BigQuery, Snowflake, atau PostgreSQL alih-alih membaca file mentah secara langsung.


10. Kontrol Kualitas

Sistem pengukuran dapat menghasilkan angka yang terlihat presisi tetapi menyesatkan. Tambahkan kontrol ini.

Pisahkan bot dari manusia

Jangan pernah menghitung OAI-SearchBot, GPTBot, ClaudeBot, atau PerplexityBot sebagai sesi manusia. Ini adalah sinyal perayap atau pengambilan.

Pertahankan bukti mentah

Untuk setiap proses panel, pertahankan:

  • HTML mentah
  • tangkapan layar
  • teks jawaban
  • stempel waktu
  • versi peramban dan pengumpul
  • permintaan dan lokal
  • hash jawaban

Ini memungkinkan untuk menjelaskan mengapa metrik berubah.

Lacak ketidakhadiran panel

Panel AI yang hilang tidak sama dengan jawaban tanpa kutipan Anda.

Gunakan:

text panel_not_present panel_present_no_target panel_present_target_cited blocked_or_challenged collection_error

Kontrol bias pengambilan sampel

Pengambilan terjadwal dipengaruhi oleh:

  • lokasi
  • perangkat
  • bahasa
  • waktu
  • personalisasi
  • status peramban
  • kata-kata permintaan

Gunakan kumpulan inti permintaan tetap dan kumpulan permintaan eksplorasi yang berotasi. Pisahkan kedua kelompok dalam laporan.

Hindari presisi palsu

Laporkan interval kepercayaan atau ukuran sampel minimum untuk kelompok topik kecil. Topik dengan tiga observasi tidak boleh dibandingkan langsung dengan topik yang berisi 300 observasi.

Bandingkan yang serupa dengan yang serupa

Saat membandingkan permukaan, pertahankan konstanta:

  • permintaan
  • lokal
  • negara
  • perangkat
  • jam pengumpulan
  • aturan domain target
  • versi parser kutipan

11. Rencana Peluncuran yang Direkomendasikan

Minggu 1: Instrumentasi

  • Konfigurasi log server terstruktur.
  • Buat registri bot.
  • Buat domain pemendek.
  • Tambahkan aturan penamaan kampanye.
  • Definisikan taksonomi topik.
  • Aktifkan ekspor Google Analytics 4 BigQuery.

Minggu 2: Tautan terkontrol

  • Publikasikan lima hingga sepuluh ringkasan yang dilacak.
  • Buat tautan untuk berbagai topik dan versi konten.
  • Validasi log pengalihan terhadap sesi analitik.
  • Konfirmasi bahwa tidak ada informasi pribadi yang masuk ke URL atau parameter analitik.

Minggu 3: Observasi panel

  • Buat manifest permintaan.
  • Mulai dengan dua permukaan dan 20 hingga 50 permintaan stabil.
  • Jalankan sekali sehari.
  • Simpan tangkapan layar dan HTML.
  • Ukur kegagalan pengumpulan secara terpisah dari ketidakhadiran kutipan.

Minggu 4: Dasbor dan tata kelola

  • Buat dasbor topik dan permukaan.
  • Tambahkan peringatan kesehatan perayap.
  • Definisikan aturan kepercayaan.
  • Bandingkan laporan pihak pertama dengan observasi panel.
  • Dokumentasikan kebijakan retensi, akses, dan penghapusan.

Kesimpulan

Pengukuran kutipan AI harus dibangun sebagai sistem atribusi multi-sinyal, bukan sebagai laporan rujukan tunggal.

Kerangka kerja yang paling dapat diandalkan menggabungkan:

  • Laporan visibilitas pihak pertama Google dan Bing
  • log server untuk aktivitas perayap dan pengambil
  • tautan pendek pihak pertama untuk pengukuran klik terkontrol
  • observasi panel jawaban terjadwal yang patuh
  • data analitik dan konversi untuk nilai hilir

Tiga metrik terpenting adalah:

  1. Pangsa Kutipan berdasarkan Topik — seberapa besar ruang sumber yang diamati milik domain Anda.
  2. Tingkat Rujukan Asisten — berapa banyak lalu lintas manusia yang dapat diatribusikan ke permukaan AI.
  3. Keterlibatan Pasca-Kutipan — apakah pengunjung yang dirujuk AI mengambil tindakan yang berarti.

Sistem ini menjadi berguna ketika setiap angka menyertakan permukaan, periode waktu, penyebut, metode atribusi, dan tingkat kepercayaan. Disiplin tersebut mencegah kunjungan perayap disalahartikan sebagai pengguna, jawaban yang diambil disalahartikan sebagai tayangan, dan kutipan disalahartikan sebagai hasil bisnis.

Artikel terkait

Suka konten ini?

Berlangganan buletin kami untuk wawasan pemasaran konten terbaru dan panduan pertumbuhan.

Artikel ini hanya untuk tujuan informasi. Konten dan strategi dapat bervariasi berdasarkan kebutuhan spesifik Anda.
Mengukur Dampak Kutipan AI: Analitik, Log, dan Pengambilan Panel | AutoPod