Skip to the content.

How to extract emails, phone numbers and social links from a website

Finding a company’s contact email and social profiles from its website is a core lead-generation task. A regex over the homepage gets you started, but it also returns image names like logo@2x.png, error-tracker IDs and share buttons. Here’s a version that avoids those.

import re, httpx
from urllib.parse import urljoin, urlparse

EMAIL = re.compile(r"[a-z0-9][a-z0-9._%+-]*@(?:[a-z0-9-]+\.)+[a-z]{2,24}", re.I)
JUNK_TLDS = {"png", "jpg", "jpeg", "gif", "webp", "svg", "css", "js"}
JUNK_DOMAINS = ("sentry.io", "wixpress.com", "example.com")
SOCIAL = {
    "linkedin": r"linkedin\.com/(company|in)/[^/?#\"']+",
    "twitter": r"(?:twitter|x)\.com/(?!intent|share)[A-Za-z0-9_]{1,15}(?=[/?#\"']|$)",
    "facebook": r"facebook\.com/(?!sharer|share|dialog|plugins)[^/?#\"']+",
    "instagram": r"instagram\.com/(?!p/|reel/)[A-Za-z0-9_.]+",
}

def page_contacts(html):
    emails = {e.lower() for e in EMAIL.findall(html)
              if e.rsplit(".", 1)[-1].lower() not in JUNK_TLDS and not e.lower().endswith(JUNK_DOMAINS)}
    phones = {re.sub(r"[^\d+]", "", t) for t in re.findall(r'href=["\']tel:([^"\']+)', html, re.I)}
    socials = {k: sorted({m.group(0) for m in re.finditer(r"https?://(?:www\.)?" + p, html, re.I)}) for k, p in SOCIAL.items()}
    return emails, phones, socials

def site_contacts(url):
    c = httpx.Client(follow_redirects=True, timeout=20, headers={"User-Agent": "contact-lookup/1.0"})
    home = c.get(url)
    emails, phones, socials = page_contacts(home.text)
    # Then check the contact/about pages, where emails usually live.
    links = {urljoin(str(home.url), h) for h in re.findall(r'href=["\']([^"\']+)', home.text)}
    for link in [l for l in links if re.search(r"contact|about|impressum", l, re.I)
                 and urlparse(l).hostname == home.url.host][:4]:
        e, p, _ = page_contacts(c.get(link).text)
        emails |= e; phones |= p
    return {"emails": sorted(emails), "phones": sorted(phones), "socials": socials}

print(site_contacts("https://plausible.io"))

Pitfalls

Need it for hundreds of websites?

Website Contact Extractor handles all of the above (obfuscation, team-page filtering, ranking the site’s own inboxes first) and returns one clean row per website. $2.50 per 1,000 websites, and sites that fail to load are free. (Built by the author of this guide.)

See also: detect a website’s tech stack · find which email provider a company uses

← Home