New Playbook: Cold Email Infrastructure Setup Guide

Read Now arrow_forward
Mailsfinder Mailsfinder
Mailsfinder Mailsfinder
Pricing
Compare
Contact
Log In Start Free Trial
Prospecting Playbook

How to Scrape Emails From a Website (legal and ethical guide for 2026)

Five practical methods, the legal lines that decide whether your list is usable, and a cleaner alternative that lands in the inbox instead of the spam folder.

By Harsh Published June 18, 2026 14 Min Read

Skip the scraping

Get verified emails for any company in seconds

Mailsfinder turns a company domain into a clean, verified contact list. 100 free credits daily.

Start free arrow_forward
Table of Contents expand_more
Section 01

Key takeaways

check_circle

Scraping publicly listed business emails for B2B outreach is usually permitted under GDPR (legitimate interest) and CAN-SPAM, as long as you identify yourself and offer opt-out.

check_circle

There are five practical methods, ranging from a Cmd+F scan to a Python script. The right one depends on the volume and how technical you are.

check_circle

Most companies do not list their decision makers on the contact page. Scraping caps out fast, which is why pattern detection plus verification beats it for sales prospecting.

check_circle

Catch-all domains, obfuscation, and honeypots are the three reasons scraped lists bounce. Verification before sending is non-negotiable.

check_circle

For hundreds or thousands of domains, an API plus a workflow tool like n8n or Make replaces the entire pipeline with about 20 minutes of setup.

Section 02

TL;DR

You can scrape a single page in 10 seconds with a regex pasted into Chrome DevTools. You can scrape an entire site in five minutes with a 30-line Python script. You can scrape 500 sites in an afternoon with a SaaS API and a workflow builder. The technique is the easy part.

The hard part is what happens after. Scraped lists are noisy. They include outdated addresses, role accounts (info@, sales@), catch-all responses that look valid but bounce, and the occasional honeypot planted to flag spammers. Every address you collect has to be verified before it sees a campaign.

And scraping has a ceiling. Most decision makers do not publish their email on the company website. If you need to reach a Head of Growth, a VP of Engineering, or a buyer at a 200-person company, scraping the contact page returns info@ at best. That is when you stop scraping and start using a domain-to-pattern finder. We will cover both paths here.

Section 04

5 methods to scrape emails from a website

Ranked from fastest-to-set-up to most-scalable. Pick based on your volume and your tolerance for writing code.

01

Manual: View Source or Cmd+F

Best for: 1 to 5 emails, one-off lookups.

Open the contact, about, or team page. Press Cmd+F (Ctrl+F on Windows) and search for "@". Most contact pages will surface the addresses immediately. For addresses tucked into the page source but not visually rendered, right-click and choose View Page Source, then search for "mailto:" or "@".

This works on roughly 60 percent of small business websites and almost no enterprise sites. The ceiling is hit fast, but for one-off research it is a zero-tool, zero-friction option.

02

Chrome extensions

Best for: 10 to 100 emails across a few sessions.

A handful of Chrome extensions walk the DOM, extract every email address on a page, and dump them into a CSV. Hunter, Apollo, Lusha, and Snov.io all ship one. They are convenient for browsing-and-scraping workflows where you are already on a prospect's site reading.

Trade-off: extensions pull addresses already visible on the page. They do not infer the head of marketing's email from the careers page. They also draw from the vendor's database, so what you get is a hybrid of "scraped from this page" and "matched from our database". Read the extension's behaviour carefully before relying on it.

03

Browser DevTools regex

Best for: 1 site, every address on every page, in 10 seconds.

The fastest method for a single page. Open Chrome DevTools (Cmd+Option+I), switch to the Console tab, and paste:

const emails = document.body.innerHTML.match(/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g);
console.log([...new Set(emails || [])]);

You will get a deduplicated array of every address that appears anywhere in the rendered HTML, including JSON-LD blocks, hidden mailto links, and inline scripts. View Source misses the JavaScript-injected ones. DevTools does not.

For sites that load content dynamically (single-page apps), wait until the page settles before running the snippet. For obfuscated addresses (more on that below), this regex catches the cleartext ones and misses the rest.

04

Python script with BeautifulSoup

Best for: 1 to 50 sites, repeatable, scriptable.

The classic scraping stack. 30 lines of Python, runs on a laptop, handles multiple pages per domain (contact, about, team), and writes results to a CSV. Here is a working starter:

import re, csv, requests
from bs4 import BeautifulSoup
from urllib.parse import urljoin

EMAIL_RE = re.compile(r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}")
CONTACT_PATHS = ["/", "/contact", "/contact-us", "/about", "/team"]
HEADERS = {"User-Agent": "Mozilla/5.0 (compatible; ResearchBot/1.0)"}

def scrape_domain(domain):
    found = set()
    for path in CONTACT_PATHS:
        url = urljoin(f"https://{domain}", path)
        try:
            r = requests.get(url, headers=HEADERS, timeout=10)
            if r.status_code != 200: continue
            soup = BeautifulSoup(r.text, "html.parser")
            found.update(EMAIL_RE.findall(soup.get_text()))
            for a in soup.find_all("a", href=True):
                if a["href"].startswith("mailto:"):
                    found.add(a["href"].replace("mailto:", "").split("?")[0])
        except Exception as e:
            print(f"skip {url}: {e}")
    return found

if __name__ == "__main__":
    domains = ["acme.com", "example.com"]
    with open("emails.csv", "w", newline="") as f:
        w = csv.writer(f)
        w.writerow(["domain", "email"])
        for d in domains:
            for e in scrape_domain(d):
                w.writerow([d, e])

What this gets you: every public-facing address listed on the contact, about, and team pages of each domain. What it does not get you: JavaScript-heavy sites that render emails client-side (use Playwright for those), obfuscated addresses, and decision makers who do not list themselves publicly.

Add rate limiting (a one-second sleep between requests) and respect robots.txt. Scraping responsibly keeps your IP off blocklists.

05

SaaS tools (the inversion)

Best for: any volume where decision makers are the target.

At a certain point, scraping the website is the wrong tool. The contact page does not list the Head of Sales. The team page does not list anyone in engineering. The about page links to LinkedIn. Scraping captures what is there, which is rarely what you want.

The inversion: use a SaaS tool that takes a domain and either returns the pattern (firstname@domain, firstname.lastname@domain) or returns the verified address for a named person at that company. You skip scraping entirely and get directly to the address.

Two relevant tools:

  • arrow_rightEmail Finder: enter a name and company domain, get a verified email back.
  • arrow_rightEmail Pattern Detector: enter a domain, get the company's email pattern back. Generate any address you need from there.

For most B2B prospecting use cases, this path produces a cleaner list in a fraction of the time. You will scrape less, send to better addresses, and spend less time cleaning up bounces.

Section 05

The cleaner alternative: domain to pattern to verify

If you remember one thing from this article, remember this sequence. It is the path most senior outbound operators use, and it produces lists with deliverability rates two to three times higher than raw scraped data.

01

Find the domain

Start with a company name. Confirm the right domain (acme.com vs acme.io vs acme.co) so the email you generate actually exists.

02

Detect the pattern

Run the domain through a pattern detector. You will get the format the company uses: firstname.lastname, firstinitial+lastname, or one of about a dozen common variants.

03

Generate and verify

Apply the pattern to your target's name. Verify the address with an email verifier before adding it to your campaign.

Why this beats raw scraping: scraping returns whatever is on the page (often info@ or careers@). Pattern detection returns the format used by the actual decision makers. Combine that pattern with a name from LinkedIn, an industry report, or a press release, and you have a clean targeted address without ever opening the company's website.

For a deeper breakdown of name-to-email workflows, see how to find someone's email address.

Section 06

Pitfalls: where scraping breaks

Three reasons scraped lists underperform. Each has a counter-move.

Catch-all domains

Some company domains are configured to accept every address sent to them, valid or not. Send to anything@acme.com and the SMTP server says "delivered". Behind the scenes, it goes to a black hole. Your verifier returns "valid" because the server accepted it. Your campaign reports a 95 percent inbox rate that produces zero replies.

Counter-move: use a verifier that flags catch-all status. Treat catch-all addresses as a separate, lower-confidence segment. Send a smaller volume to them and watch reply rates closely.

Obfuscated emails

A site that publishes its addresses as "john [at] acme [dot] com" or as an image, or that uses JavaScript to assemble the address client-side, is signalling that it does not want to be scraped. Respect the signal. The reply rate on addresses that resist obvious scraping is lower because the recipient does not expect cold email at that inbox.

Counter-move: skip the obfuscated address. Pattern-detect the domain and find the decision maker directly.

Anti-scraping and honeypots

Larger sites watch for scraping patterns: rapid-fire requests, missing User-Agent strings, repeat hits on contact pages from a single IP. They will rate-limit you, block you, or serve a honeypot address that exists only to flag spammers. Sending to a honeypot puts your domain on a blocklist.

Counter-move: rate-limit your scraper (one to three seconds between requests), set a real User-Agent, respect robots.txt, and verify every address against multiple signals before sending. Better yet, switch to a database-backed finder so you never trigger the anti-scrape system in the first place.

Section 07

Bulk: scraping hundreds of websites at once

Hand-running a Python script for 500 domains is a poor use of time. The modern bulk pattern uses three components glued together with a workflow tool.

database

Input: a domain list

A Google Sheet, an Airtable base, a CSV. Each row is a company domain (and optionally a target name or job title).

api

Engine: an email-finder API

For each domain, the workflow calls a finder API that returns the address and pattern. This replaces the scraping step entirely and works against decision makers, not whoever is published on the contact page.

verified

Filter: an email verifier API

Pipe the returned address through a verifier. Only addresses that come back as deliverable land in the output. Run the rest into a "review" pile.

The n8n / Make workflow

In n8n or Make, this is a four-node flow: trigger on a new row in your sheet, call the finder API, call the verifier API, write the result back to the sheet. About 20 minutes of setup, then it runs forever. Push 500 domains in at 9am, walk away, come back to a clean list at noon.

The same pattern works with Zapier, Pipedream, or a custom backend. The point is that the heavy lift (scraping, parsing, deduping, verifying) is offloaded to APIs that do it better than a one-off script.

Section 08

Verifying scraped emails before sending

This is the step that separates a campaign that hits the inbox from a campaign that gets flagged as spam. Sending to a list with a 15 percent bounce rate is a fast way to ruin your sender reputation, get the sending domain blocklisted, and drop future deliverability across every campaign you run.

Verification checks four things in roughly two seconds per address: syntax (is it a valid email format), domain (does the MX record exist), mailbox (does the SMTP server accept this specific address), and risk (is this a role account, disposable address, or known catch-all). Addresses that pass all four are safe to send.

Single-address verifier

For one-off checks. Paste an address, get an instant verdict.

Open the Email Verifier arrow_forward

Bulk verifier

For lists of 100 to 100,000 addresses. Upload a CSV, get a clean file back with status flags per row.

Open the Bulk Verifier arrow_forward

Rule of thumb: never send to a list that has not been verified within the last 30 days. Email addresses go stale at roughly 2 to 3 percent per month as people change jobs and companies update their domains.

Section 09

Frequently asked questions

Is it legal to scrape emails from a website? expand_more
Scraping publicly listed business email addresses for legitimate B2B outreach is generally permitted under GDPR (legitimate interest) and CAN-SPAM in the United States, provided you identify yourself, allow opt-out, and do not bypass technical access controls. Scraping personal email addresses or addresses behind a login is much riskier and often violates the site's terms of service. This is a pragmatic summary, not legal advice.
What is the fastest way to scrape emails from a single page? expand_more
Open Chrome DevTools, switch to the Console tab, and paste a regex that matches the @ pattern across the rendered DOM. You will have every visible address in two seconds, including ones tucked into mailto links and JSON blobs that View Source misses.
How do I scrape emails from a website without coding? expand_more
Use a Chrome extension that walks the page DOM, or use a SaaS email finder that takes a domain and returns the addresses and pattern. Both options work without writing a script, and the SaaS route adds verification so the addresses are safe to send to.
Why are scraped emails bouncing? expand_more
Three reasons: the address was outdated and the person left the company, the address belongs to a catch-all domain that accepts everything then drops it silently, or the address is a honeypot used to flag spammers. Always run scraped lists through an email verifier before sending.
Can I scrape emails from LinkedIn? expand_more
LinkedIn explicitly prohibits scraping in its terms of service and actively blocks automated access. Instead of scraping LinkedIn, use the public profile URL with a tool that turns a name and company into a verified business email through pattern detection. See how to find emails on LinkedIn for the safer workflow.
What is the difference between scraping and finding emails? expand_more
Scraping pulls addresses that are already published on a page. Finding generates the address from a name and company using known patterns and verifies it. Finding scales better, hits people who never list their email publicly, and produces cleaner lists.
How do I scrape emails from hundreds of websites at once? expand_more
Run a domain list through an API in a no-code workflow tool like n8n or Make. For each domain, call an email finder API, then a verifier API, then push the result into your CRM or spreadsheet. This pattern handles thousands of domains a day without writing custom infrastructure.
Section 10

Related reading

Stop scraping. Start sending to verified inboxes.

Mailsfinder turns a domain into a clean, verified contact list at 5x lower cost than the alternatives. 100 free credits daily, no credit card required.

Start Free Trial Talk to Sales