L'analyse des écarts de contenu identifie les sujets que votre audience recherche mais que votre site ne couvre pas. Les boîtes « Questions similaires » (People Also Ask, PAA) de Google révèlent les questions complémentaires que les utilisateurs posent après avoir cherché un sujet. En comparant les questions PAA de vos mots-clés cibles avec votre contenu existant, vous pouvez identifier des écarts et générer des briefs de contenu pour les sujets manquants. Ce tutoriel construit un analyseur d'écarts de contenu automatisé qui récupère les données PAA pour un ensemble de mots-clés, regroupe les questions par thème et produit une liste priorisée d'opportunités de contenu.
Prérequis
- Python 3.10 ou supérieur
- bibliothèque requests installée
- Une clé API Scavio
- Une liste de mots-clés cibles à analyser
Parcours
Étape 1: Récupérer les données PAA pour les mots-clés cibles
Interroger chaque mot-clé via l'API Scavio et collecter les questions People Also Ask.
def get_paa_questions(keyword: str) -> list[str]:
r = requests.post(
"https://api.scavio.dev/api/v1/search",
headers={"x-api-key": API_KEY},
json={"query": keyword, "country_code": "us"}
)
r.raise_for_status()
paa = r.json().get("people_also_ask", [])
return [item["question"] for item in paa]Étape 2: Collecter les questions sur tous les mots-clés
Construire une liste maîtresse de toutes les questions PAA, en suivant quel mot-clé a déclenché chaque question.
import time
def collect_all_paa(keywords: list[str]) -> list[dict]:
all_questions = []
for kw in keywords:
questions = get_paa_questions(kw)
for q in questions:
all_questions.append({"question": q, "seed_keyword": kw})
time.sleep(0.5)
return all_questionsÉtape 3: Dédupliquer et regrouper les questions
Supprimer les questions en double et regrouper les questions similaires en recherchant des mots communs.
from collections import defaultdict
def cluster_questions(questions: list[dict]) -> dict[str, list[str]]:
seen = set()
unique = []
for q in questions:
normalized = q["question"].lower().strip("?")
if normalized not in seen:
seen.add(normalized)
unique.append(q)
clusters = defaultdict(list)
for q in unique:
words = q["question"].lower().split()
topic = words[0] + " " + words[1] if len(words) > 1 else words[0]
clusters[topic].append(q["question"])
return dict(clusters)Étape 4: Générer des briefs de contenu
Pour chaque écart de contenu, générer un brief qui inclut la question cible, les questions connexes et le mot-clé source d'origine.
def generate_briefs(questions: list[dict]) -> list[dict]:
briefs = []
for q in questions[:20]:
brief = {
"target_question": q["question"],
"seed_keyword": q["seed_keyword"],
"suggested_title": q["question"].rstrip("?") + " - Complete Guide",
"content_type": "guide" if "how" in q["question"].lower() else "explainer",
}
briefs.append(brief)
return briefsExemple Python
import os
import json
import time
import requests
from collections import defaultdict
API_KEY = os.environ.get("SCAVIO_API_KEY", "your_scavio_api_key")
ENDPOINT = "https://api.scavio.dev/api/v1/search"
KEYWORDS = ["vector database", "rag pipeline", "embedding model", "ai agent framework"]
def get_paa(kw: str) -> list[str]:
r = requests.post(ENDPOINT, headers={"x-api-key": API_KEY},
json={"query": kw, "country_code": "us"})
r.raise_for_status()
return [item["question"] for item in r.json().get("people_also_ask", [])]
def analyze():
all_questions = []
seen = set()
for kw in KEYWORDS:
for q in get_paa(kw):
if q.lower() not in seen:
seen.add(q.lower())
all_questions.append({"question": q, "seed": kw})
time.sleep(0.5)
print(f"Found {len(all_questions)} unique content gaps:")
for q in all_questions:
print(f" [{q['seed']}] {q['question']}")
return all_questions
if __name__ == "__main__":
gaps = analyze()
with open("content_gaps.json", "w") as f:
json.dump(gaps, f, indent=2)Exemple JavaScript
const API_KEY = process.env.SCAVIO_API_KEY || "your_scavio_api_key";
const ENDPOINT = "https://api.scavio.dev/api/v1/search";
async function getPAA(kw) {
const res = await fetch(ENDPOINT, {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({ query: kw, country_code: "us" })
});
const data = await res.json();
return (data.people_also_ask || []).map(item => item.question);
}
async function main() {
const keywords = ["vector database", "rag pipeline", "embedding model"];
const seen = new Set();
const gaps = [];
for (const kw of keywords) {
const questions = await getPAA(kw);
for (const q of questions) {
if (!seen.has(q.toLowerCase())) {
seen.add(q.toLowerCase());
gaps.push({ question: q, seed: kw });
}
}
}
console.log(`${gaps.length} content gaps found:`);
gaps.forEach(g => console.log(` [${g.seed}] ${g.question}`));
}
main().catch(console.error);Sortie attendue
Found 16 unique content gaps:
[vector database] What is the best vector database in 2026?
[vector database] How does a vector database differ from a relational database?
[rag pipeline] What are the components of a RAG pipeline?
[rag pipeline] How do you evaluate RAG performance?
[embedding model] What is the difference between embeddings and fine-tuning?
[ai agent framework] What is the best AI agent framework for production?