i've scraped 570+ agency websites across 43 countries in the last two weeks. the script is 55 lines of python. here's the whole thing, explained.
the core: 3 lines that do 90% of the work
requests.get hits the URL. re.findall with an email regex pulls every email-shaped string from the HTML. a filter list removes the garbage. that's it.
the regex: [a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}
this catches 95%+ of emails on web pages. it's intentionally loose — i'd rather catch false positives and filter them than miss real emails.
the filter list (this is the real secret)
after scraping 570+ sites, my filter has 25+ patterns. without it, about 30% of "emails" are garbage:
every time i scrape a new batch and get a weird false positive, i add it to the filter. the list grows every round.
the multi-path trick
most agency websites don't put their email on the homepage. the contact page is where it lives. so for each agency, i try 3-5 URL paths:
this one change tripled my hit rate. before i was only hitting homepages and getting ~25% extraction rate. with multi-path it jumped to ~65%.
deduplication matters
many agencies list the same email 3-4 times across their pages (header, footer, contact form, sidebar). the script uses a set() to deduplicate per-agency, and checks against all previously found emails to avoid sending the same person two pitches from different agency listings.
what i do with the emails
each batch gets saved as a JSON file with the agency name, URL, and email. a separate script reads these batches, scans each agency's website with a custom SEO analyzer, and generates a personalized pitch email that references specific problems found on their site.
the personalization is the key differentiator. instead of "hey, we offer AI outreach services," the email says "your homepage is missing alt text on 23 images, has 4 broken links, and your page speed score is 34/100. we can fix this and find your clients' leads."
the numbers after 2 weeks
the pipeline works mechanically. the conversion is the part i'm iterating on. every week the pitch gets slightly better based on what gets replies and what gets ignored.
if you want to see the landing page agencies are being sent to: https://vemtrac-outreach.pages.dev
happy to answer questions about the technical setup or share my filter list if anyone's building something similar.
Tidy script. Two additions from running similar pipelines:
Try /privacy-policy and /terms as fallback paths — sites that hide contact emails behind forms often leak a real mailbox in their legal pages.
The post-scrape step matters more than the scrape: dedupe by domain (multiple addresses at one company = pick the most senior-looking, suppress the rest), sort role accounts (info@, hello@) into a separate low-expectation bucket, and verify everything before send. Scraped lists run 10–20% dead, and providers start filtering you at ~2% hard bounces — the math is unforgiving.
The 3-second scrape is the demo. The hygiene layer is the product.