Pipeline that enriches a database of PE/VC firms with structured investment criteria (deal sizing, sector focus, activity signals) pulled from firm websites — seeded from CapIQ exports, crawled and extracted automatically.
- Preprocess —
preprocess_capiq.pyconverts a raw CapIQ buyer export (TSV/CSV) intodata/master.json, one record per firm. It does a regex first-pass on descriptions to seed revenue/EBITDA/check-size ranges, sector tier 1, deal types, and activity tier where possible. - Pipeline —
pipeline.pyloops over firms withstatus = pendingorstale, processing one at a time and writing back tomaster.jsonafter every firm (crash-safe, no batching). - Search —
search_provider.pyresolves a firm's website if missing, or finds an updated URL if a crawl fails (404/timeout). Currently stubbed with mock results; swap in a real provider (e.g. Exa) by settingEXA_API_KEYand flippingSEARCH_ENABLED = True. - Crawl —
crawler.pyuses Crawl4AI'sAdaptiveCrawlerto pull whatever it can from the homepage, then parses internal links for priority pages (criteria, strategy, portfolio) and fetches them directly with browser-header requests to work around 403s. Saves crawl state per firm and exports raw pages tokb/{slug}.jsonl. - Extract —
extractor.pycleans and combines crawled pages, then calls the Claude API (claude-haiku-4-5) to pull structured fields into the master schema. Never overwrites confirmed values with nulls; flags conflicts against CapIQ-seeded data asneeds_review. - Export —
export_to_xlsx.pyrendersmaster.jsonas a formatted, color-coded Excel workbook with a summary sheet.
pip install crawl4ai requests markdownify anthropic python-dotenv openpyxlCreate a .env file in the project root:
ANTHROPIC_API_KEY=your-key-here
EXA_API_KEY=your-key-here # optional, only if real search is enabled
# 1. Convert a CapIQ export into master.json
python pipeline/preprocess_capiq.py --input data/capiq_export.tsv --output data/master.json
# 2. Run the pipeline
python pipeline/pipeline.py # process all pending/stale firms
python pipeline/pipeline.py --limit 5 # test on first 5
python pipeline/pipeline.py --slug borgman-capital-llc # process one firm
python pipeline/pipeline.py --summary # status counts only, no processing
# 3. Export to Excel
python pipeline/export_to_xlsx.py
python pipeline/export_to_xlsx.py --status complete # only completed firmsEach firm in master.json includes: identity (firm_name, website, firm_type, HQ location), deal sizing (revenue_min/max, ebitda_min/max, enterprise_value_min/max, check_size_min/max, deal_types), sector coverage (sector_tier1/2/3, sector_keywords, sector_exclusions), activity signals (activity_tier, aum_usd_millions, last_known_deal_date), and data quality fields (confidence, needs_review, source_urls, last_checked_date, status).
Status values: pending → in_progress → complete, or needs_review on failure. Records become stale after 90 days and re-enter the queue on the next run.
pipeline/
preprocess_capiq.py # CapIQ export -> master.json
pipeline.py # main orchestration loop
search_provider.py # website resolution (mock or Exa)
crawler.py # Crawl4AI + direct-fetch crawling
extractor.py # Claude-based structured extraction
export_to_xlsx.py # master.json -> formatted .xlsx
data/
master.json # the master record file (gitignored)
crawl_states/ # per-firm crawl state (gitignored)
kb/ # per-firm raw crawled pages, .jsonl (gitignored)
.env,crawl_states/,kb/, anddata/master.jsonare gitignored.data/*.xlsxanddata/*.csvare currently not ignored — double-check before committing if those contain firm data you don't want public.search_provider.pyandcrawler.pyboth ship with mock modes for testing pipeline logic without hitting real APIs or websites.