fr
English
Español
中國人
Tiếng Việt
Deutsch
Українська
Português
भारतीय
Türkçe
한국인
Italiano
Gaeilge
اردو
Indonesia
Polski Pour que la prospection directe fonctionne, vous avez besoin d'une base solide - une base de données d'adresses électroniques réelles et à jour. C'est là qu'intervient le scraping d'adresses électroniques avec Python: un moyen de collecter de manière programmatique des adresses à partir de sites web.
Dans ce guide, nous verrons comment créer un système de scraping d'emails avec Python à partir de zéro, comment gérer les pages dynamiques, comment filtrer et valider les adresses collectées, et comment utiliser les données résultantes dans des flux de travail marketing ou commerciaux réels.
Ce matériel est utile si vous avez besoin de:
Ensuite, nous verrons comment transformer des pages accessibles au public en un canal de communication directe avec des personnes susceptibles de devenir vos clients - à l'aide de Python.
À la base, ce type de scraping consiste à analyser automatiquement des pages HTML ou dynamiques et à rechercher dans le contenu ou les attributs des modèles correspondant à des formats d'adresse (par exemple, username@domain.tld). Les résultats sont ensuite filtrés, validés et enregistrés.
Il est largement utilisé dans les affaires, le marketing, la recherche et l'automatisation des processus de routine. Il est particulièrement utile lorsqu'il s'agit de rassembler et de structurer un grand volume d'informations publiques provenant de sources multiples.
Exemples de tâches spécifiques où le scraping d'emails avec Python est appliqué:
Si vous souhaitez recueillir des données de contact pour des projets de commerce électronique, consultez notre guide sur les récupération de données sur le commerce électronique.
Pour que le scraping soit efficace, vous devez préparer l'environnement et choisir les bons outils. Ils vous aident à récupérer les données plus rapidement, à gérer des pages complexes ou dynamiques et à organiser des projets de plus grande envergure.
Outils Python courants pour le scraping:
| Tool | Utilisation |
|---|---|
| requests / httpx | Récupérer des pages statiques |
| BeautifulSoup | Analyse HTML / recherche d'éléments |
| re (expressions régulières) | Extraire des modèles |
| lxml | Une analyse plus rapide |
| Selenium / Playwright | Gestion des pages pilotées par JavaScript |
| Scrapy | Un cadre complet pour les grandes recherches |
pip install requests beautifulsoup4 lxml
pip install selenium # if you need dynamic rendering Pour voir comment des méthodes similaires sont appliquées à d'autres plateformes, consultez notre guide détaillé sur les scraper Reddit à l'aide de Python.
# 1. Create an HTTP session with timeouts and retries
session = make_session()
# 2. Load the page
html = session.get(url)
# 3. Look for email addresses:
# - via regex across the entire text
# - via mailto: links in HTML
emails = extract_emails_from_text(html)
emails.update(find_mailto_links(html))
# 4. Return a unique list of addresses
return emails
"""
Iterate over internal links within one domain and collect email addresses.
Highlights:
- Page limit (max_pages) to stop safely
- Verifying that a link belongs to the base domain
- Avoiding re-visits
- Optional respect for robots.txt
"""
from __future__ import annotations
from collections import deque
from typing import Set
from urllib.parse import urljoin, urlparse, urlsplit, urlunsplit
import time
import requests
from bs4 import BeautifulSoup
import lxml # Import lxml to ensure it's available for BeautifulSoup
from urllib import robotparser # standard robots.txt parser
# We use functions from the previous block:
# - make_session()
# - scrape_emails_from_url()
import re
# General regular expression for email addresses
EMAIL_RE = re.compile(r"[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9.-]+")
def scrape_emails_from_url(url: str, session: requests.Session) -> Set[str]:
"""Collect email addresses from the given URL page."""
emails: Set[str] = set()
try:
resp = session.get(url, timeout=getattr(session, "_default_timeout", 10.0))
resp.raise_for_status()
# Regular expression for email addresses
# Note: this regex isn't perfect, but it's sufficient for typical cases
email_pattern = re.compile(r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}")
emails.update(email_pattern.findall(resp.text))
except requests.RequestException:
pass
return emails
def make_session() -> requests.Session:
"""Create and return a requests session with basic settings."""
session = requests.Session()
session.headers.update({
"User-Agent": "EmailScraper/1.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.9",
# Don't force Accept-Encoding to avoid br issues without brotli
"Connection": "keep-alive",
})
return session
def same_host(url: str, base_netloc: str) -> bool:
"""True if the link belongs to the same host (domain/subdomain)."""
return urlparse(url).netloc == base_netloc
def load_robots(start_url: str, user_agent: str = "EmailScraper") -> robotparser.RobotFileParser:
"""Read robots.txt and return a parser for permission checks."""
base = urlparse(start_url)
robots_url = f"{base.scheme}://{base.netloc}/robots.txt"
rp = robotparser.RobotFileParser()
rp.set_url(robots_url)
try:
rp.read()
except Exception:
pass
rp.useragent = user_agent
return rp
def normalize_url(url: str, base: str | None = None) -> str | None:
try:
abs_url = urljoin(base, url) if base else url
parts = urlsplit(abs_url)
if parts.scheme not in ("http", "https"):
return None
host = parts.hostname
if not host:
return None
host = host.lower()
netloc = host
if parts.port:
netloc = f"{host}:{parts.port}"
parts = parts._replace(fragment="")
return urlunsplit((parts.scheme.lower(), netloc, parts.path or "/", parts.query, ""))
except Exception:
return None
def in_scope(url: str, base_host: str, include_subdomains: bool) -> bool:
try:
host = urlsplit(url).hostname
if not host:
return False
host = host.lower()
base_host = (base_host or "").lower()
if include_subdomains:
return host == base_host or host.endswith("." + base_host)
else:
return host == base_host
except Exception:
return False
def collect_emails_from_site(
start_url: str,
max_pages: int = 100,
delay_sec: float = 0.5,
respect_robots: bool = True,
include_subdomains: bool = True,
) -> Set[str]:
"""
Traverse pages within a domain and return unique email addresses.
- max_pages: hard limit on visited pages.
- delay_sec: polite pause between requests.
- respect_robots: if True — checks access rules.
- include_subdomains: if True — allows subdomains (www, etc.).
"""
session = make_session()
base_host = (urlparse(start_url).netloc or "").lower()
visited: Set[str] = set()
queue: deque[str] = deque()
enqueued: Set[str] = set()
all_emails: Set[str] = set()
start_norm = normalize_url(start_url)
if start_norm:
queue.append(start_norm)
enqueued.add(start_norm)
rp = load_robots(start_url, user_agent="EmailScraper/1.0") if respect_robots else None
while queue and len(visited) < max_pages:
url = queue.popleft()
if url in visited:
continue
# robots.txt check
if respect_robots and rp is not None:
try:
if not rp.can_fetch("EmailScraper/1.0", url):
continue
except Exception:
pass
# One request: used both for emails and links
try:
resp = session.get(url, timeout=10)
resp.raise_for_status()
html_text = resp.text or ""
except requests.RequestException:
continue
visited.add(url)
# Skip non-HTML pages
ctype = resp.headers.get("Content-Type", "")
if ctype and "text/html" not in ctype:
continue
# Collect emails
for m in EMAIL_RE.findall(html_text):
all_emails.add(m.lower())
# Parse links
soup = BeautifulSoup(html_text, "lxml")
# Emails from mailto:
for a in soup.find_all("a", href=True):
href = a["href"].strip()
if href.lower().startswith("mailto:"):
addr_part = href[7:].split("?", 1)[0]
for piece in addr_part.split(","):
email = piece.strip()
if EMAIL_RE.fullmatch(email):
all_emails.add(email.lower())
for a in soup.find_all("a", href=True):
href = a["href"].strip()
if not href or href.startswith(("javascript:", "mailto:", "tel:", "data:")):
continue
next_url = normalize_url(href, base=url)
if not next_url:
continue
if not in_scope(next_url, base_host, include_subdomains):
continue
if next_url not in visited and next_url not in enqueued:
queue.append(next_url)
enqueued.add(next_url)
if delay_sec > 0:
time.sleep(delay_sec)
try:
session.close()
except Exception:
pass
return all_emails
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(
description="An email scraper that traverses pages within a site and prints discovered addresses."
)
parser.add_argument(
"start_url",
help="Starting URL, for example: https://example.com"
)
parser.add_argument(
"--max-pages",
type=int,
default=100,
dest="max_pages",
help="Maximum number of pages to traverse (default: 100)"
)
parser.add_argument(
"--delay",
type=float,
default=0.5,
help="Delay between requests in seconds (default: 0.5)"
)
parser.add_argument(
"--no-robots",
action="store_true",
help="Ignore robots.txt (use carefully)"
)
scope = parser.add_mutually_exclusive_group()
scope.add_argument(
"--include-subdomains",
dest="include_subdomains",
action="store_true",
default=True,
help="Include subdomains (default)"
)
scope.add_argument(
"--exact-host",
dest="include_subdomains",
action="store_false",
help="Restrict traversal to the exact host (no subdomains)"
)
parser.add_argument(
"--output",
type=str,
default=None,
help="Optional: path to a file to save found email addresses (one per line)"
args = parser.parse_args()
emails = collect_emails_from_site(
args.start_url,
max_pages=args.max_pages,
delay_sec=args.delay,
respect_robots=not args.no_robots,
include_subdomains=args.include_subdomains,
)
for e in sorted(emails):
print(e)
print(f"Found {len(emails)} unique emails.")
if args.output:
try:
with open(args.output, "w", encoding="utf-8") as f:
for e in sorted(emails):
f.write(e + "\n")
except Exception as ex:
print(f"Could not write the output file: {ex}")
main.py https://example.com
Lorsque vous exécutez un script, les choses ne sont pas toujours simples: de nombreux sites cachent délibérément les adresses électroniques ou ne les exposent qu'après le rendu du JavaScript. Voici ce qui peut vous gêner, et comment y remédier.
1. Obscurcissement
Les sites utilisent souvent des techniques pour cacher les adresses aux robots:
2. Pages dynamiques
Les sites modernes chargent souvent le contenu via JavaScript (par exemple, fetch, AJAX). Un simple requests.get() peut renvoyer un shell HTML "vide" sans le contenu de l'e-mail.
Approches pratiques lorsque vous rencontrez de telles pages:
Lancez un navigateur, laissez la page se "charger", attendez les éléments requis, puis capturez le code HTML complet. Cela fonctionne lorsque l'e-mail est injecté par JS après le rendu.
Souvent, la page tire réellement des données d'une API. Vérifiez les requêtes réseau (DevTools → Network) pour voir s'il existe une requête qui renvoie l'adresse électronique ou les informations de contact en JSON. Si c'est le cas, il est préférable d'utiliser l'API directement.
Parfois, l'adresse est "intégrée" dans JavaScript (par exemple, une chaîne Base64 ou divisée en plusieurs parties). Vous pouvez interpréter ce JavaScript, extraire la chaîne et décoder l'adresse.
Télécharger l'image et appliquer la ROC (Reconnaissance Optique de Caractères), par exemple avec Tesseract. Cette opération demande plus de ressources, mais elle est parfois nécessaire.
Certains éléments apparaissent après quelques secondes ou après des événements spécifiques (défilement, clic). Il est judicieux de:
En appliquant les techniques discutées dans cet article pour le scraping d'emails avec Python, vous pouvez faire fonctionner vos scripts de manière fiable dans des conditions réelles. Gardez à l'esprit que la qualité des données affecte directement l'efficacité des campagnes ultérieures, il est donc utile de mettre en œuvre le filtrage, la validation et l'enregistrement dans un format pratique dès le départ.
Commentaires: 0