Skip to main content

Scrape Dutch voting advice (StemWijzer) data for any election

Project description

nl-voting-data-scraper

PyPI Downloads Python package Python License

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.1.tar.gz (19.0 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.1-py3-none-any.whl (19.7 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: nl_voting_data_scraper-0.1.1.tar.gz
  • Upload date:
  • Size: 19.0 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.1.tar.gz
Algorithm Hash digest
SHA256 73c7d3cd45f05b69db66e62e00d7522eaa74d02ff0f17702d78fb6dbee78be9b
MD5 974ae65a4e22c738db1807653dc264f0
BLAKE2b-256 3a3fd7427583583f6fe554a58a1b87262219663896492c22135f41da54e8bce9

See more details on using hashes here.

Provenance

The following attestation bundles were made for nl_voting_data_scraper-0.1.1.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.1-py3-none-any.whl.

File metadata

File hashes

Hashes for nl_voting_data_scraper-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 ec2e0829a506f21e907f99ed54290a9d88b14ab46926d38388233e73019b959a
MD5 7fe0755fa76bc0d1618b9d4f044ed7d7
BLAKE2b-256 242d15e2cd44f1c027403892c634db1b9a7bdf632f665a17a09105022fd42b3d

See more details on using hashes here.

Provenance

The following attestation bundles were made for nl_voting_data_scraper-0.1.1-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