Técnicas profissionais de scraping com SerpAPI para monitorar concorrentes, analisar rankings e descobrir oportunidades de SEO
Técnicas avançadas de scraping com SerpAPI para monitorar rankings, descobrir palavras-chave dos concorrentes e identificar oportunidades de mercado.
SerpAPI fornece dados de SERP em tempo real, incluindo posições, featured snippets, ads e resultados locais com 99.9% de precisão.
Rastreie mudanças de posição ao longo do tempo, identifique padrões sazonais e monitore estratégias dos concorrentes.
Monitore concorrentes em 240+ países e idiomas, com dados localizados e específicos por região geográfica.
Processe milhares de consultas simultaneamente, com rate limiting inteligente e retry automático para máxima eficiência.
Métodos profissionais para extrair insights valiosos dos concorrentes e identificar oportunidades de mercado.
Rastreamento contínuo de posições dos concorrentes
// Configuração SerpAPI para Monitoramento
const serpApiConfig = {
api_key: process.env.SERPAPI_KEY,
engine: "google",
location: "Brazil",
hl: "pt",
gl: "br",
num: 100, // Primeiros 100 resultados
device: "desktop"
};
// Lista de concorrentes para monitorar
const competitors = [
{
domain: "concorrente1.com",
keywords: ["react native", "desenvolvimento mobile", "app ios android"],
priority: "high"
},
{
domain: "concorrente2.com",
keywords: ["flutter", "ionic", "xamarin"],
priority: "medium"
}
];
// Função de monitoramento
async function monitorCompetitorRankings() {
const results = [];
for (const competitor of competitors) {
for (const keyword of competitor.keywords) {
try {
const response = await fetch(`https://serpapi.com/search?${new URLSearchParams({
...serpApiConfig,
q: keyword,
start: 0
})}`);
const data = await response.json();
const organicResults = data.organic_results || [];
// Encontrar posição do concorrente
const competitorResult = organicResults.find(result =>
result.link && result.link.includes(competitor.domain)
);
if (competitorResult) {
results.push({
competitor: competitor.domain,
keyword: keyword,
position: competitorResult.position,
title: competitorResult.title,
snippet: competitorResult.snippet,
url: competitorResult.link,
timestamp: new Date().toISOString(),
featured_snippet: data.answer_box ? true : false
});
}
// Rate limiting
await new Promise(resolve => setTimeout(resolve, 1000));
} catch (error) {
console.error(`Erro ao monitorar ${keyword} para ${competitor.domain}:`, error);
}
}
}
return results;
}
// Análise de mudanças históricas
function analyzePositionChanges(currentData, historicalData) {
const analysis = [];
currentData.forEach(current => {
const historical = historicalData.find(h =>
h.competitor === current.competitor &&
h.keyword === current.keyword
);
if (historical) {
const positionChange = historical.position - current.position;
const changeType = positionChange > 0 ? 'improvement' :
positionChange < 0 ? 'decline' : 'stable';
analysis.push({
...current,
previous_position: historical.position,
position_change: positionChange,
change_type: changeType,
change_percentage: Math.abs(positionChange / historical.position * 100),
days_since_change: Math.floor(
(new Date(current.timestamp) - new Date(historical.timestamp)) /
(1000 * 60 * 60 * 24)
)
});
}
});
// Identificar tendências
const trends = {
biggest_gainers: analysis
.filter(a => a.change_type === 'improvement')
.sort((a, b) => b.position_change - a.position_change)
.slice(0, 5),
biggest_losers: analysis
.filter(a => a.change_type === 'decline')
.sort((a, b) => a.position_change - b.position_change)
.slice(0, 5),
new_entries: currentData.filter(c =>
!historicalData.some(h =>
h.competitor === c.competitor && h.keyword === c.keyword
)
)
};
return { analysis, trends };
}
Identificação de palavras-chave em ascensão, estratégias que funcionam para concorrentes, oportunidades de posicionamento e alertas de mudanças significativas.
Identificação de oportunidades em resultados especiais
// Análise completa de SERP Features
async function analyzeSerpFeatures(keywords) {
const serpFeatures = [];
for (const keyword of keywords) {
try {
const response = await fetch(`https://serpapi.com/search?${new URLSearchParams({
...serpApiConfig,
q: keyword
})}`);
const data = await response.json();
// Extrair diferentes tipos de features
const features = {
keyword: keyword,
timestamp: new Date().toISOString(),
// Featured Snippet
featured_snippet: data.answer_box ? {
type: data.answer_box.type,
title: data.answer_box.title,
snippet: data.answer_box.snippet,
source: data.answer_box.link,
domain: new URL(data.answer_box.link).hostname
} : null,
// People Also Ask
people_also_ask: data.related_questions ?
data.related_questions.map(q => ({
question: q.question,
snippet: q.snippet,
source: q.link,
domain: new URL(q.link).hostname
})) : [],
// Knowledge Graph
knowledge_graph: data.knowledge_graph ? {
title: data.knowledge_graph.title,
type: data.knowledge_graph.type,
description: data.knowledge_graph.description,
source: data.knowledge_graph.source
} : null,
// Local Pack
local_results: data.local_results ?
data.local_results.map(local => ({
title: local.title,
address: local.address,
rating: local.rating,
reviews: local.reviews,
type: local.type
})) : [],
// Shopping Results
shopping_results: data.shopping_results ?
data.shopping_results.slice(0, 5).map(product => ({
title: product.title,
price: product.price,
source: product.source,
rating: product.rating,
reviews: product.reviews
})) : [],
// Top Stories
top_stories: data.top_stories ?
data.top_stories.map(story => ({
title: story.title,
source: story.source,
date: story.date,
thumbnail: story.thumbnail
})) : [],
// Videos
video_results: data.video_results ?
data.video_results.slice(0, 3).map(video => ({
title: video.title,
channel: video.channel,
duration: video.duration,
views: video.views,
date: video.date
})) : []
};
serpFeatures.push(features);
// Rate limiting
await new Promise(resolve => setTimeout(resolve, 1500));
} catch (error) {
console.error(`Erro ao analisar SERP features para ${keyword}:`, error);
}
}
return serpFeatures;
}
// Análise de oportunidades em SERP Features
function identifySnippetOpportunities(serpData, competitorDomains) {
const opportunities = {
featured_snippet_gaps: [],
paa_opportunities: [],
competitor_dominance: {},
content_gaps: []
};
serpData.forEach(serp => {
// Oportunidades de Featured Snippet
if (!serp.featured_snippet) {
opportunities.featured_snippet_gaps.push({
keyword: serp.keyword,
reason: 'No featured snippet present',
opportunity_type: 'create_snippet_content'
});
} else if (!competitorDomains.includes(serp.featured_snippet.domain)) {
opportunities.featured_snippet_gaps.push({
keyword: serp.keyword,
current_holder: serp.featured_snippet.domain,
reason: 'Competitor not holding snippet',
opportunity_type: 'steal_snippet'
});
}
// Análise People Also Ask
serp.people_also_ask.forEach(paa => {
if (!competitorDomains.includes(paa.domain)) {
opportunities.paa_opportunities.push({
keyword: serp.keyword,
question: paa.question,
current_holder: paa.domain,
opportunity_type: 'answer_question'
});
}
});
// Dominância por domínio
const allDomains = [
serp.featured_snippet?.domain,
...serp.people_also_ask.map(p => p.domain),
...serp.local_results.map(l => new URL(l.website || '').hostname),
...serp.shopping_results.map(s => s.source)
].filter(Boolean);
allDomains.forEach(domain => {
if (!opportunities.competitor_dominance[domain]) {
opportunities.competitor_dominance[domain] = 0;
}
opportunities.competitor_dominance[domain]++;
});
});
// Identificar gaps de conteúdo
const contentTypes = ['how-to', 'what-is', 'best', 'vs', 'review'];
contentTypes.forEach(type => {
const typeKeywords = serpData.filter(s =>
s.keyword.toLowerCase().includes(type)
);
if (typeKeywords.length > 0) {
const competitorCoverage = typeKeywords.filter(s =>
s.featured_snippet &&
competitorDomains.includes(s.featured_snippet.domain)
).length;
const coveragePercentage = (competitorCoverage / typeKeywords.length) * 100;
if (coveragePercentage < 30) {
opportunities.content_gaps.push({
content_type: type,
total_keywords: typeKeywords.length,
competitor_coverage: competitorCoverage,
coverage_percentage: coveragePercentage,
opportunity_score: 100 - coveragePercentage
});
}
}
});
return opportunities;
}
Palavras-chave sem featured snippets, perguntas não respondidas pelos concorrentes, gaps de conteúdo e oportunidades de posicionamento em SERP features.