Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

prospect-forge

Build a B2B prospect file from French open data — and know which contacts are wrong before you use them.

Scraping contact details is easy. The hard part is that a meaningful share of what you collect belongs to someone else, and every one of those rows is a commercial e-mail sent to a stranger under your own name.

prospect-forge collects companies from the national business register, enriches them with contact details from OpenStreetMap, and then refuses to hand you a contact it cannot tie to the right company.


The problem this solves

On a real run over 1,286 construction companies around Grenoble, a naive "the company name appears on the page" filter returned these:

Address found What it actually was
sassenage.mairie@wanadoo.fr The town hall of Fontaine — matched "Sassenage Étanchéité"
avocat@delmas.fr A law firm, homonym of "Entreprise Delmas"
press@novabat.com A global battery recycler, unrelated to the local NOVABAT SARL
delegue.protectiondesdonnees@grandgroupe.fr Grand Groupe' data protection officer
privacy@ofenbau.de A German stove manufacturer
you@company.com An unfilled website template

Roughly a third of the results. Each one is syntactically valid, reachable, and completely wrong.

That last one deserves a note, because it shows how these get in. The joinery Fenêtres Most is tagged in OpenStreetMap with website=https://www.securitas.fr/ — a contributor's mistake. Crawl that without checking and you scrape a multinational, find its DPO, and add them to a mailing list for local roofers. The pipeline never does anything invalid; it is simply working on the wrong company from the first step onwards.

So the website gets verified before it is crawled, not after.


Install

git clone https://github.com/Kerneth/prospect-forge
cd prospect-forge
pip install -r requirements.txt

Python 3.10+. No API key: every source is public and free.

Use

python -m prospect_forge \
    --naf 43.91A 43.32A 43.34Z \
    --postcode 38130 38100 \
    --insee 38151 38185 \
    -o prospects.csv
Option Meaning
--naf Activity codes, dotted form. 43.91A is roof carpentry. Full list
--postcode Postcodes for the register lookup
--insee Commune codes for the OpenStreetMap pass — find yours
-o Output CSV, ;-separated with a BOM so Excel opens it correctly

Output columns: score, name, trade, officer, address, postcode, town, created, headcount, NAF, SIREN, SIRET, phone, email, website, confidence, notes.

The notes column keeps why every contact was accepted or discarded, so any row can be audited without re-running anything.

As a library

from prospect_forge.verify import verify_email, Confidence

verdict = verify_email("press@novabat.com", "NOVABAT SARL")
verdict.confidence   # <Confidence.REVIEW: 'review'>
verdict.reason       # "'press' mailbox — only large organisations run one, ..."

How verification works

Four ordered filters. Each is cheap, and each rejects a failure mode the others cannot see.

0 — Is this really their website? Compares the domain to the company name before crawling anything. Catches the securitas.fr class of error at the source.

1 — Is the domain distinctive? plombier.com describes a trade, not a company. Whoever owns it is not the plumber you are looking for. Trade-word domains are rejected outright.

2 — Does the address line up with this company? Either the mailbox is on the company's own domain, or the company name appears in the domain, or an acronym matches — "ATELIER CONSTR. MÉTALL. (ACMTS)" against acmts.com. Legal forms and accents are folded away first.

3 — Blocklists. Template placeholders, hosting providers, noreply@, data protection officers, public bodies (mairie, prefecture, cci), regulated professions (avocat, notaire, cabinet), and mailboxes only large organisations run (press@, investor@, careers@) — a two-person roofing company has no press office, so finding one means you found a namesake.

Anything that survives is labelled high. Anything plausible but unproven is labelled review and kept, with the reason. Nothing ambiguous is silently dropped, and nothing doubtful is silently promoted.

Tested against the real failures

Every false positive above is a test case:

pytest -q

The suite asserts both directions — the bad addresses are caught and the genuine ones survive. A filter that rejects everything would pass half a test suite and be useless.


What this does not do

  • It will not find many e-mail addresses. On the Grenoble construction run, 1,286 companies yielded 54 verified addresses. That is not a limitation of the tool: most small tradespeople have no website and no published e-mail. They work by phone. The phone column is the useful one on that market — and knowing that is itself a result.
  • It cannot separate true homonyms. If a local company and a multinational share a name and the mailbox looks ordinary, no textual filter can tell them apart. Those land in review, which is where a human belongs.
  • It does not use directory sites. Pages Jaunes and similar forbid automated extraction in their terms of use. Nothing here comes from them.

Sources

Source Licence Used for
recherche-entreprises.api.gouv.fr Open Licence 2.0 Legal identity, NAF, headcount, officers
OpenStreetMap via Overpass ODbL Phone, website, e-mail

Sole traders who have opted out of public distribution appear as [NON-DIFFUSIBLE] in the register and are skipped — there is no name to contact.

Overpass instances are frequently saturated, and a busy one answers HTTP 200 with an HTML error page. The client checks the content type rather than the status code, and rotates across three public endpoints before giving up.

Before you send anything

B2B prospecting in France is lawful without prior consent when the message concerns the recipient's professional role, the sender is identifiable, and an opt-out is provided. That is a floor, not a strategy:

  • send in small batches, never the whole file at once
  • include an opt-out in every message
  • record an opt-out permanently, or it reappears on the next run

Licence

MIT — see LICENSE.

About

Build a verified B2B prospect file from French open data - and know which contacts are wrong before you use them.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages