Les fiches Google Maps contiennent des données commerciales riches : nom, adresse, téléphone, site web, horaires, notes et avis. Plutôt que de scraper Google Maps directement (ce qui viole les CGU et se casse constamment), ce tutoriel extrait les données Maps des résultats SERP. Recherchez une catégorie d'entreprise et un lieu, et le SERP renvoie les résultats du pack local avec toutes les données nécessaires pour le cold emailing. Chaque recherche coûte 0,005 $.
Prérequis
- Python 3.9+ installé
- bibliothèque requests installée
- Une clé API Scavio depuis scavio.dev
Parcours
Étape 1: Rechercher des entreprises locales via le SERP
Recherchez une catégorie d'entreprise et un lieu. Les résultats SERP incluent à la fois les listings organiques et les données du pack local avec les détails de l'entreprise.
import requests, os, re
SCAVIO_KEY = os.environ['SCAVIO_API_KEY']
def find_local_businesses(category: str, city: str, count: int = 10) -> list:
query = f'{category} near {city}'
resp = requests.post('https://api.scavio.dev/api/v1/search',
headers={'x-api-key': SCAVIO_KEY, 'Content-Type': 'application/json'},
json={'query': query, 'country_code': 'us', 'num_results': count})
resp.raise_for_status()
data = resp.json()
businesses = []
# Extract from organic results
for r in data.get('organic_results', []):
domain = r['link'].split('/')[2] if '/' in r['link'] else ''
# Skip aggregator sites
if any(a in domain for a in ['yelp.com', 'yellowpages.com', 'angi.com', 'thumbtack.com']):
continue
phones = re.findall(r'\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}', r.get('snippet', ''))
businesses.append({
'name': r['title'].split(' - ')[0].split(' | ')[0].strip(),
'website': r['link'],
'domain': domain,
'phone': phones[0] if phones else '',
'snippet': r.get('snippet', ''),
'category': category,
'city': city
})
return businesses
leads = find_local_businesses('dentist', 'Austin TX')
print(f'Found {len(leads)} businesses')
for l in leads[:5]:
print(f' {l["name"]}: {l["domain"]} {l["phone"]}')Étape 2: Enrichir les leads avec une analyse de site web
Recherchez chaque entreprise pour trouver des signaux supplémentaires : présence d'un blog, d'une présence sur les réseaux sociaux, ou d'un design de site web obsolète.
import time
def enrich_lead(lead: dict) -> dict:
domain = lead['domain']
if not domain:
return lead
# Search for the business to find additional info
resp = requests.post('https://api.scavio.dev/api/v1/search',
headers={'x-api-key': SCAVIO_KEY, 'Content-Type': 'application/json'},
json={'query': f'site:{domain}', 'country_code': 'us', 'num_results': 5})
pages = resp.json().get('organic_results', [])
lead['page_count'] = len(pages)
lead['has_blog'] = any('blog' in p.get('link', '').lower() for p in pages)
lead['has_booking'] = any(w in ' '.join(p.get('title', '') for p in pages).lower()
for w in ['book', 'schedule', 'appointment'])
# Estimate website quality from page count
if lead['page_count'] <= 2:
lead['website_quality'] = 'minimal'
elif lead['page_count'] <= 5:
lead['website_quality'] = 'basic'
else:
lead['website_quality'] = 'established'
return lead
# Enrich top leads
for i, lead in enumerate(leads[:3]):
leads[i] = enrich_lead(lead)
print(f' {lead["name"]}: {lead["page_count"]} pages, '
f'blog={lead.get("has_blog", False)}, '
f'quality={lead.get("website_quality", "unknown")}')
time.sleep(0.3)Étape 3: Générer des modèles d'email froid personnalisés
Utilisez les données d'enrichissement pour créer des modèles d'email personnalisés pour chaque lead. Référencez leur situation spécifique de site web.
def generate_email(lead: dict) -> str:
name = lead['name']
quality = lead.get('website_quality', 'unknown')
has_blog = lead.get('has_blog', False)
has_booking = lead.get('has_booking', False)
# Personalized opening based on website analysis
if quality == 'minimal':
opening = (f'I noticed {name} has a fairly simple website. '
'Many of your competitors in the area have sites with '
'online booking and patient reviews that help them stand out.')
elif not has_booking:
opening = (f'I visited the {name} website and noticed you do not have '
'an online booking system. Adding one typically increases '
'new patient inquiries by 20-30%.')
elif not has_blog:
opening = (f'{name} has a solid website, but I noticed there is no blog. '
'Local dental practices with blogs rank for 3-5x more '
'keywords than those without.')
else:
opening = (f'I have been following {name} online and your web presence '
'is strong. I think there is an opportunity to improve '
'your local search visibility even further.')
email = f"""Subject: Quick question about {name}'s website
Hi,
{opening}
I help {lead['category']}s in {lead['city']} get more patients through
their website. Would a 10-minute call this week make sense?
Best regards"""
return email
for lead in leads[:2]:
if lead.get('website_quality'):
email = generate_email(lead)
print(email)
print('---')Exemple Python
import requests, os, re, time
SCAVIO_KEY = os.environ['SCAVIO_API_KEY']
def find_leads(category, city):
resp = requests.post('https://api.scavio.dev/api/v1/search',
headers={'x-api-key': SCAVIO_KEY, 'Content-Type': 'application/json'},
json={'query': f'{category} near {city}', 'country_code': 'us', 'num_results': 10})
leads = []
for r in resp.json().get('organic_results', []):
domain = r['link'].split('/')[2] if '/' in r['link'] else ''
if not any(a in domain for a in ['yelp.com', 'yellowpages.com']):
phones = re.findall(r'\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}', r.get('snippet', ''))
leads.append({'name': r['title'].split(' - ')[0], 'domain': domain,
'phone': phones[0] if phones else ''})
return leads
for l in find_leads('dentist', 'Austin TX')[:5]:
print(f'{l["name"]}: {l["domain"]} {l["phone"]}')Exemple JavaScript
const SCAVIO_KEY = process.env.SCAVIO_API_KEY;
async function findLeads(category, city) {
const resp = await fetch('https://api.scavio.dev/api/v1/search', {
method: 'POST',
headers: { 'x-api-key': SCAVIO_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify({ query: `${category} near ${city}`, country_code: 'us', num_results: 10 })
});
return (await resp.json()).organic_results?.filter(r => {
const domain = new URL(r.link).hostname;
return !['yelp.com', 'yellowpages.com'].some(a => domain.includes(a));
}).map(r => ({
name: r.title.split(' - ')[0],
domain: new URL(r.link).hostname,
phone: (r.snippet || '').match(/\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}/)?.[0] || ''
})) || [];
}
findLeads('dentist', 'Austin TX').then(l => l.forEach(x => console.log(`${x.name}: ${x.phone}`)));Sortie attendue
Found 7 businesses
Austin Dental Care: austindentalcare.com (512) 555-0123
Smile Solutions: smilesolutionsatx.com (512) 555-0456
Capital City Dental: capitalcitydental.com
Austin Dental Care: 3 pages, blog=False, quality=basic
Smile Solutions: 8 pages, blog=True, quality=established
Subject: Quick question about Austin Dental Care's website
Hi,
I visited the Austin Dental Care website and noticed you do not have
an online booking system. Adding one typically increases
new patient inquiries by 20-30%.
I help dentists in Austin TX get more patients through their website.
Would a 10-minute call this week make sense?