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:
- API-first (fast): Tries to fetch data from StemWijzer data endpoints via HTTP. Handles base64-encoded responses and optional AES decryption.
- 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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
73c7d3cd45f05b69db66e62e00d7522eaa74d02ff0f17702d78fb6dbee78be9b
|
|
| MD5 |
974ae65a4e22c738db1807653dc264f0
|
|
| BLAKE2b-256 |
3a3fd7427583583f6fe554a58a1b87262219663896492c22135f41da54e8bce9
|
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
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
nl_voting_data_scraper-0.1.1.tar.gz -
Subject digest:
73c7d3cd45f05b69db66e62e00d7522eaa74d02ff0f17702d78fb6dbee78be9b - Sigstore transparency entry: 1104624880
- Sigstore integration time:
-
Permalink:
rhnfzl/nl-voting-data-scraper@9d512763c08419ba692a53e19fc6018a6e2de60a -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/rhnfzl
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@9d512763c08419ba692a53e19fc6018a6e2de60a -
Trigger Event:
release
-
Statement type:
File details
Details for the file nl_voting_data_scraper-0.1.1-py3-none-any.whl.
File metadata
- Download URL: nl_voting_data_scraper-0.1.1-py3-none-any.whl
- Upload date:
- Size: 19.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ec2e0829a506f21e907f99ed54290a9d88b14ab46926d38388233e73019b959a
|
|
| MD5 |
7fe0755fa76bc0d1618b9d4f044ed7d7
|
|
| BLAKE2b-256 |
242d15e2cd44f1c027403892c634db1b9a7bdf632f665a17a09105022fd42b3d
|
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
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
nl_voting_data_scraper-0.1.1-py3-none-any.whl -
Subject digest:
ec2e0829a506f21e907f99ed54290a9d88b14ab46926d38388233e73019b959a - Sigstore transparency entry: 1104624988
- Sigstore integration time:
-
Permalink:
rhnfzl/nl-voting-data-scraper@9d512763c08419ba692a53e19fc6018a6e2de60a -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/rhnfzl
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@9d512763c08419ba692a53e19fc6018a6e2de60a -
Trigger Event:
release
-
Statement type: