Skip to main content

Scrape Dutch voting advice (StemWijzer) data for any election

Project description

nl-voting-data-scraper

Scrape Dutch voting advice (StemWijzer) data for any election — municipal, national, European, or provincial.

Outputs structured JSON with party positions, policy statements, and metadata. Reusable across election cycles.

Installation

pip install nl-voting-data-scraper

For browser automation fallback (optional):

pip install "nl-voting-data-scraper[browser]"
playwright install chromium

Quick Start

CLI

# List known elections
nl-voting-data-scraper list-elections

# Scrape all municipalities for 2026 municipal elections
nl-voting-data-scraper scrape gr2026 -o ./output

# Scrape a specific municipality
nl-voting-data-scraper scrape gr2026 -m GM0014 -o ./output

# Scrape national election
nl-voting-data-scraper scrape tk2025 -o ./output

# List municipalities for an election
nl-voting-data-scraper list-municipalities gr2026

# Discover API endpoints
nl-voting-data-scraper discover gr2026

Python Library

import asyncio
from nl_voting_data_scraper import StemwijzerScraper

async def main():
    async with StemwijzerScraper("gr2026") as scraper:
        # Scrape a single municipality
        data = await scraper.scrape_one("GM0014")
        print(f"{data.votematch.name}: {len(data.parties)} parties, {len(data.statements)} statements")

        # Scrape all
        results = await scraper.scrape()
        print(f"Scraped {len(results)} entries")

asyncio.run(main())

Supported Elections

Slug Type Year Description
gr2026 Municipal 2026 Gemeenteraadsverkiezingen 2026
tk2025 National 2025 Tweede Kamerverkiezingen 2025
tk2023 National 2023 Tweede Kamerverkiezingen 2023
eu2024 European 2024 Europees Parlement 2024
ps2023 Provincial 2023 Provinciale Staten 2023

New elections are auto-detected from URL patterns. You can also pass custom election slugs.

How It Works

Hybrid approach:

  1. API-first (fast): Tries to fetch data from StemWijzer data endpoints via HTTP. Handles base64-encoded responses and optional AES decryption.
  2. Browser fallback: If the API fails, uses Playwright to load the frontend, intercept network requests, and capture the data. Falls back to DOM extraction as a last resort.

Output Format

Each municipality/election produces a JSON file:

{
  "parties": [
    {
      "id": 206919,
      "name": "Party Name",
      "fullName": "Full Party Name",
      "website": "https://...",
      "hasSeats": true,
      "statements": [
        { "id": 206987, "position": "agree", "explanation": "..." }
      ]
    }
  ],
  "statements": [
    {
      "id": 206987,
      "theme": "Housing",
      "title": "The municipality should build more affordable housing.",
      "index": 1
    }
  ],
  "shootoutStatements": [...],
  "votematch": {
    "id": 206918,
    "name": "Municipality Name",
    "context": "2026GR",
    "remote_id": "GM0014",
    "langcode": "nl"
  }
}

CLI Options

nl-voting-data-scraper scrape ELECTION [OPTIONS]

Options:
  -m, --municipality TEXT   Specific GM codes (repeatable)
  -l, --language TEXT       Languages to scrape (default: nl)
  -o, --output TEXT         Output directory (default: ./output)
  --combined                Also write combined.json
  --rate-limit FLOAT        Requests per second (default: 2.0)
  --no-cache                Disable caching
  --resume                  Resume interrupted scrape
  --browser-only            Only use browser scraping
  --api-only                Only use API scraping
  -v, --verbose             Verbose output

Development

git clone https://github.com/rhnfzl/nl-voting-data-scraper.git
cd nl-voting-data-scraper
pip install -e ".[dev,browser]"
playwright install chromium
pytest

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

nl_voting_data_scraper-0.1.0.tar.gz (18.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

nl_voting_data_scraper-0.1.0-py3-none-any.whl (19.6 kB view details)

Uploaded Python 3

File details

Details for the file nl_voting_data_scraper-0.1.0.tar.gz.

File metadata

  • Download URL: nl_voting_data_scraper-0.1.0.tar.gz
  • Upload date:
  • Size: 18.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for nl_voting_data_scraper-0.1.0.tar.gz
Algorithm Hash digest
SHA256 f7ee59b29f3383a8ccdc61ea7edd2ac335776616e95eacae88129da0c73e1e39
MD5 57b3c3717457d26d7d8c4458c4c3f37f
BLAKE2b-256 c9d0347309f01e01903edf91d961e616ac0f33eee1905ce827f64f0fe9854d87

See more details on using hashes here.

Provenance

The following attestation bundles were made for nl_voting_data_scraper-0.1.0.tar.gz:

Publisher: publish.yml on rhnfzl/nl-voting-data-scraper

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file nl_voting_data_scraper-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for nl_voting_data_scraper-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 7548b610088cd324a46865a0390275579870e8ba0f27f9b6d371a94cb3e7f97d
MD5 3b9f322e4748644def7494858a23a7eb
BLAKE2b-256 a5b2f1c7edbfb5583ef29c530f20c4fc378df9a71b44bdd3c3e0d468c16cb9de

See more details on using hashes here.

Provenance

The following attestation bundles were made for nl_voting_data_scraper-0.1.0-py3-none-any.whl:

Publisher: publish.yml on rhnfzl/nl-voting-data-scraper

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page