PYTHON DATA PROCESSING PROJECT

UK Company Data Tool: Companies House Data to Excel Lead Lists

A Python project that streams the public Companies House company data file in chunks, filters it, and writes Excel lead lists by postcode area, with an optional website-enrichment step. This case study follows what the repository documents, including its limits.

Overview and problem

The Companies House Free Company Data Product is a CSV of about 2.7 GB covering live UK companies. A file of that size is awkward to filter by hand, and the useful question is usually narrow: which companies match a particular status, category, industry code and date window, and where are they?

UK Company Data Tool answers that by turning the file into filtered Excel workbooks, one folder per postcode area. Its default configuration looks for active private limited companies in construction (SIC 41 and 43) incorporated in the last 90 days; these are default settings that can be changed.

This is a repository project. This page makes no claim of client use, production deployment or business results.

Scope and boundaries

The tool reads only company-level fields. It does not use officer or personal data. The enrichment step is separate and optional, and it needs internet access; the main filtering run does not.

Several limits are part of the design and are stated in the repository: the date window is relative to the day the tool runs, so a later run gives a different window; address matching is exact after normalisation, so address variants are not merged; the registered address is often not the trading address; and the “Possible Virtual Office” flag is a keyword heuristic that can flag genuine addresses.

What I built

  • Chunked, two-pass filteringThe CSV is read in chunks of 200,000 rows, and only the columns the tool needs are loaded.
  • Postcode-area Excel workbooksSurviving companies are written to a leads workbook per postcode area using xlsxwriter in constant-memory mode. Areas above 1,000,000 rows are split into parts.
  • Data-quality reportingEach area gets a report showing how many rows each filter dropped, so the effect of every rule can be inspected, plus a run summary.
  • Optional website enrichmentA separate script looks for a company’s website and records publicly listed business contact details only when the site can be verified as belonging to that company. It saves progress after every company so interrupted runs can resume.

Architecture

main.py · filtering and exportalways runs

  1. Companies House CSVFree Company Data Product, read in 200,000-row chunks
  2. Pass 1: shared addressesNormalised addresses are hashed and counted to find those used by many companies
  3. Pass 2: filtersStatus, category, SIC prefix, date window and shared-address checks
  4. Excel by postcode areaLeads workbook and data-quality report per area, plus a summary

enrich.py · website enrichmentoptional

  1. Candidate domainsLikely domains guessed from the company name and checked in DNS
  2. Fetch a few pagesHomepage and some linked pages, obeying robots.txt and rate limits
  3. Verify the matchRegistration number, or company name plus registered postcode
  4. Record with sourcesBusiness contact details saved with the URL each came from
Original diagram of the two scripts described in the repository README. It is not a screenshot, and it does not show real output.

Technical approach

Pass 1 hashes each normalised registered address to a 64-bit integer and marks addresses used by more than a configurable number of companies (the default is 10), such as formation agents and accountants. Pass 2 keeps companies that are active, are private limited companies, have at least one SIC code starting with a configured prefix, fall inside the date window, and do not use a shared address. Duplicate company numbers are then removed.

In enrichment, a website is accepted at one of three confidence levels. High means the site shows the company’s registration number. Medium means the exact company name appears in the title, heading or footer and the registered postcode appears on the site. Uncertain means only the name matches: the URL is recorded, but no contact details are taken from it, and it should not be treated as verified.

Only business channels are kept: role addresses on the company’s own domain, free-mail addresses named after the company, and phone numbers listed on the site, each with its source URL. The crawler obeys robots.txt, limits itself to one request per second overall and one every three seconds per host, checks each redirect hop, caps page size and identifies itself with a contact address.

Technologies

Technologies: Python 3.11 or newer, pandas and xlsxwriter. The README notes development on Python 3.13 with pandas 3.0, about 3 GB of disk space for the input CSV, and internet access for the enrichment script only.

Reported figures

The figures below are measurements that the README reports from particular runs on one machine. I have not independently reproduced them, and they are not benchmarks or guarantees.

Filtering run. For a full run on the 2.7 GB input file (5,704,711 rows) on a Windows 11 desktop, the README reports about 150 seconds for both passes plus writing 122 area workbooks, and a peak working set of about 504 MB. The README notes that peak memory is reported differently on other platforms, so those figures are not directly comparable.

Rows kept. The README reports 10,218 rows kept for the default filter (SIC 41 and 43, incorporated in the last 90 days). The date window is relative to the day the tool runs, so this count depends on the run date, which the README does not state. The README also does not specify whether the count is before or after duplicate company numbers are removed.

Enrichment trial. The README reports a 2,000-company trial that averaged about 7.4 seconds per company and verified websites for 3.5% of the companies in that trial. It does not document how those 2,000 companies were selected, so 3.5% should not be read as a rate for all UK companies. The README attributes the low coverage to most new companies not having a website yet.

Limitations and responsible use

  • Enrichment coverage is lowMost newly incorporated companies have no website yet, so many companies receive no enriched details. Nothing here promises contact details for every company or any particular lead quality.
  • The search fallback can failThe optional DuckDuckGo fallback can break or be rate-limited, and it switches off at the first CAPTCHA.
  • No automated test suite yetThe repository does not yet include automated tests.
  • No licence has been chosenThe repository is public, but no licence is selected, so I do not describe it as open source. The author holds all rights until a licence is added.
  • Contact details carry legal dutiesThe input is public data, but the enriched output contains business contact details. Anyone using them for marketing is responsible for complying with UK GDPR and PECR, for example by obtaining consent where it is required and including an opt-out in every message. The tool does not make a user compliant, and this page is not legal advice.

Result and related services

The result is a public repository that converts a very large public CSV into filtered, postcode-area Excel workbooks by reading it in chunks, with an optional enrichment step that records where each detail came from.

It is an example of the Python data-processing work I can bring to a custom workflow. The right filters, data sources and checks for your own project depend on the data and the rules involved.

WORK TOGETHER

Tell me about the software you need.

Share the current situation, the work involved and the outcome you want. We can use that to discuss scope and how I can help.

Work with Ayman Atif