内容
<p align="right"><strong>简体中文</strong> · <a href="README.en.md">English</a></p>
<div align="center">
<h1>k-vote-cli</h1>
<p><strong>获取选举结果和本周民意调查结果。任何人都可以通过一个命令获取。</strong></p>
<p>韩国选举公开数据,无需特殊权限或注册,即可供人或AI使用,命令格式统一为<code>kvote</code>。</p>
<p><sub>历史选举结果<strong>按投票站整理</strong>,<strong>本周民意调查结果全收集</strong>,政党支持率时间序列等。<a href="#功能">完整功能列表↓</a></sub></p>
</div>
<p align="center">
<a href="#quick-start"><strong>快速开始</strong></a> ·
<a href="#功能"><strong>功能</strong></a> ·
<a href="#安装"><strong>安装</strong></a> ·
<a href="#验证示例"><strong>验证示例</strong></a> ·
<a href="CLAUDE.md"><strong>内部结构</strong></a>
</p>
<p align="center">
<a href="docs/k-vote-cli-promo.mp4"><img src="docs/k-vote-cli-promo.gif" alt="k-vote-cli宣传: 选举结果和本周民意调查结果,一个命令搞定" width="760" /></a>
</p>
<p align="center"><sub><a href="docs/k-vote-cli-promo.mp4">高清视频</a> · <a href="docs/k-vote-cli.gif">终端演示</a></sub></p>
> [!WARNING]
> **部分功能**(民意调查结果收集,数据集搜索和下载路径)依赖于公开门户的HTML结构,
> 如果网站改版,可能会导致功能异常 — 可以通过`kvote doctor`进行检查。
> 文件解析、本地数据库和MCP与网站改版无关。收集的数据是**依法公开的公共数据**(选举民意调查标准规定的注册和公开材料),
> 并应用基本速率限制,以礼貌地收集。请自行承担使用风险。
## 为什么创建这个工具
在选举管理方面,人们的信任度前所未有地降低。在这个时代,人们渴望透明和公开,
但选举数据却难以触及,不仅人难以获取,就连AI也无法直接分析。
这个工具并不解决不信任的问题,但作为开发者,我希望能够提供一个解决方案,
让人们更容易地获取和分析选举数据,让数据更加透明和公开。
## 有什么特别之处
- **历史选举结果一键获取** — 大选、总选举、地方选举等,可以同时获取并按投票站整理 (`nec corpus`)
- **本周民意调查结果全部收集** — 注册的所有调查和政党支持率时间序列 (`nesdc sync` · `bulk`)
- **认证密钥由AI代为申请** — data.go.kr注册、申请和认证密钥 발급 (`api login` · `apply`)
- **原始数据保留,结果可复现** — 保证数据原始性和一致性
## 变更记录
| 数据 | 现有 | k-vote-cli |
|---|---|---|
| **选举结果** (按投票站整理) | info.nec.go.kr被屏蔽 → data.go.kr手动搜索 → CSV(EUC-KR)下载 → 编码转换 → long-format手动解析和重复处理 | `kvote nec results <pk>` |
| **历史核心选举结果** | 重复上述过程,每个选举都需要 | `kvote nec corpus --normalize` |
| **民意调查结果** | 点击 게시판 → 每篇文章下载PDF → 人工阅读表格 *(无官方API)* | `kvote nesdc sync` |
| **投票率和当选人** (OpenAPI) | 注册→申请→审批→认证密钥 발급→代码查找→XML调用和分页解析 | `kvote api login` *(一次)* → `kvote nec winners <sgId>` |
## 功能
> 输出格式支持`-f json|jsonl|table`。**API密钥无关**的命令可以直接使用。
> **X** = 需要data.go.kr认证密钥(`kvote api`自动 발급)。OpenAPI和账户组是实验性功能。
| 分类 | 功能 | 命令 | 无需API密钥 |
|---|---|---|:--:|
| **核心** | 历史核心选举结果同时下载并按投票站整理 | `nec corpus --normalize` | **O** |
| 选举结果 | 选举结果按投票站整理(分类和汇总) | `nec results <pk>` | **O** |
| 选举结果 | 公开数据集搜索(选举结果等) | `nec datasets -q <搜索词>` | **O** |
| 选举结果 | 最新选举类型自动解析 | `nec latest <关键词>` | **O** |
| 选举结果 | 原始文件下载(CSV/XLSX自动) | `nec pull <pk>` | **O** |
| 投票率 | 投票率分析(ZIP)按性别、年龄和地区整理 | `nec turnout-analysis <pk>` | **O** |
| 民意调查 | 期间/条件全部JSONL收集 | `nesdc sync` | **O** |
| 民意调查 | 累积主Excel → 政党支持率(2023-10-30~ 1,400+件) | `nesdc bulk` | **O** |
| 民意调查 | 单个详细元数据+样本构成交叉表 | `nesdc show <nttId> --crosstab` | **O** |
| 民意调查 | 附件PDF(统计表和问卷)下载 | `nesdc pull <nttId>` | **O** |
| 民意调查 | 结果汇总PDF精准获取(单个 `--sync`批量) | `nesdc tabulation <nttId>` | **O** |
| 本地数据库 | 选举、民意调查和投票率导入SQLite + read-only SQL | `db ingest …` · `db query` | **O** |
| AI代理 | MCP服务器(探索、收集和SQL查询作为工具) | `mcp` | **O** |
| 检查 | 核心路径实时检查(网站改版崩溃检测) | `doctor` | **O** |
| OpenAPI | 投票率(按省、市、县别,本/事前分离) | `nec turnout <sgId> --sgtype N` | X |
| OpenAPI | 当选人(选举区、编号、政党、姓名、得票数) | `nec winners <sgId> --sgtype N` | X |
| OpenAPI | 选举代码注册(1987~所有sgId和选举类型) | `nec elections` | X |
| 账户 | 浏览器一次性登录→会话保持 | `api login` | **O** |
| 账户 | 我的申请状态(状态·**到期日**) | `api list` | X |
| 账户 | OpenAPI申请自动提交(必填目的·确认) | `api apply <pk> --purpose` | X |
<details>
<summary><strong>展开主要选项</strong></summary>
| 命令 | 核心选项 |
|---|---|
| `nec corpus` | `--normalize` 立即下载JSONL · `-o` 存储位置 · `--concurrency` |
| `nec results` | `--file` 本地解析 · `--aggregate {town\|sgg\|sido\|national}` · `--by-votetype` · `--race`/`--leaf-only`(XLSX) |
| `nec datasets`/`pull` | `--source datagokr\|openportal` (开放门户: data.nec.go.kr, dataId使用) |
| `nec turnout`/`winners` | `--sgtype` 1=总统 2=国会议员 3=省长 4=市长 5=省议员 6=市议员 · `--api-key`(默认 `KVOTE_DATAGOKR_KEY`) |
| `nesdc sync` | `-q`·`--field`·`--from`/`--to`·`--date-field`·`--gubun`·`--max-pages`·`--pull` |
| `api apply` | `--purpose`(必填) · `--category research\|web\|app\|ref\|etc` · `--yes` |
| `api config` | `--auto-apply true` (无需确认直接申请) </details>
### `nec corpus` — 代表性功能
`nec corpus --normalize` 一行代码即可下载历史核心选举结果(大选16、17、21届,总选举和地方5-8届),
并立即按投票站整理为JSONL格式。
```bash
kvote nec corpus --normalize -o ./corpus
duckdb -c "SELECT * FROM read_json_auto('./corpus/*.jsonl') LIMIT 5"
```
### 规范化带来的好处
| 维度 | 内容 |
|---|---|
| 投票类型 | 每行分为本投票/场外投票/场内投票/居所分类 |
| 多级汇总 | 投票站 → 镇/市/省 → 选举区 → 省 → 全国 (指标汇总 + 投票率) |
| 派生值 | 投票率(投票数/选民数) · 有效投票数(投票数-无效)·候选人得票率(得票数/有效票数),均有明确定义 |
| 原始数据保留 | 选民人数、投票数、无效票、弃票数、候选人得票数全部保留,可直接计算 |
| 样本构成 | 民意调查性别、年龄、地区×完成率 加权案例数 + 加权方法 |
### 可以验证的内容交叉表 |
| 机构 × 方式的民意调查分布 | `nesdc sync` → 汇总统计 |
| 政党支持率时间序列 | `nesdc bulk` |
| 性别、年龄段的投票率 | `nec turnout-analysis <pk>` — 与民意调查样本交叉表(`nesdc show --crosstab`)在同一维度,可以并行比较选举结果、民意调查结果和投票率。 |
## 支持的选举数据
kvote 不硬编码任何特定的选举 — **选举委员会发布门户后自动成为目标** (`nec datasets`/`nec latest` 实时查看最新列表)。
以下是在公开门户范围内可用的选举数据。
| 选举 | 可获取的届次 | 格式 | 备注 |
|---|---|---|---|
| 总统选举 | 第16、17、21届 | CSV | 第18~20届没有文件数据 |
| 国会选举 | 第22届(地区和比例代表制) | CSV | |
| 全国地方选举 | 第5~8届 | XLSX | 市长、教育长等7个选举同时进行 |
| 补选 | 综合文件 | XLSX | |
| 投票率分析(性别/年龄) | 有XLSX文件的届次(第20、21届总统选举,第21、22届国会选举,第8届地方选举等) | ZIP | 旧届次只有原始数据 (`nec pull`) |
| 民意调查 | 所有公开的民意调查+政党支持率累计(2023-10-30~ 1,400+件) | HTML/XLSX/PDF | |
**容易混淆的事项**
- **不同选举的编号体系不同**。总统选举、国会选举为“第N**届**”(第21届总统选举=2025,第22届国会选举=2024),
地方选举为“第N**回**”(第8届=2022,第9届=2026)。“第19届”这个编号在总统选举(2017)、国会选举(2012)中存在,
但在地方选举中不存在。
- **刚刚结束的选举不会立即公开**。选举委员会需要时间整理数据并发布到门户上(例如: 2026-06 第9届地方选举尚未公开)。
可以使用`kvote nec latest "地方选举结果"` 自动查找最新届次。
- **一个地方选举文件中包含多个选举**。市长、省长、教育长、省市县长、市县议员、比例代表制议员等分别在不同的表格中,
可以使用`kvote nec results --race 省长` 进行筛选。
## 安装
**`go install` (推荐)**
```bash
go install github.com/JungHoonGhae/k-vote-cli/cmd/kvote@latest
```
**Homebrew (macOS/Linux)**
```bash
brew install JungHoonGhae/k-vote-cli/kvote
```
**源码编译**
```bash
git clone https://github.com/JungHoonGhae/k-vote-cli
cd k-vote-cli
make build # -> bin/kvote
```
> 跨平台二进制文件(macOS/Linux/Windows · arm64/amd64)会在标签(`vX.Y.Z`)发布时,
> 通过goreleaser自动发布到[GitHub Releases](https://github.com/JungHoonGhae/k-vote-cli/releases)。
## 快速开始
### 适用于代理
```text
安装 k-vote-cli: go install github.com/JungHoonGhae/k-vote-cli/cmd/kvote@latest
使用 `kvote doctor` 检查核心路径是否可用。
选举结果可以直接获取,无需API密钥:
kvote nec corpus --normalize -o ./corpus # 历史核心选举结果 → 按投票站整理JSONL
kvote nec results <pk> -f jsonl # 单个数据集规范化
输出可以使用 `-f jsonl` 接收,然后使用 jq/duckdb 进行查询。
OpenAPI(投票率/当选人)需要密钥,但投票率/当选人也可以通过 results 推导。
```
### 适用于人类
```bash
# 核心功能:历史选举结果一键获取
kvote nec corpus --normalize -o ./corpus
duckdb -c "SELECT * FROM read_json_auto('./corpus/*.jsonl') LIMIT 5"
# 单个数据集:搜索 → 规范化
kvote nec datasets -q 选举结果 -f table
kvote nec results 15025527 -f jsonl > votes.jsonl # 按投票站整理,包括候选人得票
kvote nec results 15025527 --aggregate sgg --by-votetype -f jsonl # 选举区×投票类型
# 民意调查:全部收集
kvote nesdc sync --from 2026-01-01 > surveys.jsonl
kvote nesdc show 19366 --crosstab -f table # 单个详细 + 样本构成
kvote nesdc bulk -f jsonl > polls.jsonl # 政党支持率累计
# data.go.kr OpenAPI: 从密钥申请到调用 (实验性)
kvote api login # 浏览器一次性登录
kvote api apply 15000900 --purpose "选举数据分析研究"
export KVOTE_DATAGOKR_KEY=<一般认证密钥>
kvote nec winners 20240410 --sgtype 2 -f jsonl # 2024年国会选举当选人254人
```
## MCP 服务器 (适用于AI代理)
`kvote mcp` 启动 [Model Context Protocol](https://modelcontextprotocol.io) 服务器,
允许代理使用工具调用而不是shell命令访问相同的数据。
### 为什么导入到本地数据库?
本地数据库**不是必须的**,但如果以下情况,可以提高使用价值。
- **交叉查询**:多个选举结果、民意调查结果汇总到一个地方,通过SQL进行连接和聚合。
比起分别处理多个JSONL文件更高效。
- **重复使用**:将数十万个投票站数据下载并规范化后,可以重复使用。
之后的查询可以立即执行,无需下载和解析。
- **标准派生值一键查询**:投票率、得票率、有效票数等派生值,
作为视图预定义,可以直接查询,无需每次重新计算。
## 支持工具/资源
| 工具/资源 | 内容 |
|---|---|
| `search_datasets` | data.go.kr 选举结果文件数据关键词搜索 |
| `list_elections` | 选举类型关键词查找最新届次数据集 |
| `ingest_results` | 下载选举结果并导入本地SQLite数据库(幂等) |
| `ingest_polls` | NESDC累计民意调查Excel导入本地数据库(幂等) |
| `ingest_turnout` | 投票率分析(性别/年龄)导入本地数据库(幂等) |
| `query` | 本地数据库只读SQL查询 |
| `kvote://schema` | 表/视图模式 + 派生值定义 (资源使用前先阅读) |
原始数据按原样存储,派生值(投票率、得票率、有效票数等)仅作为视图SQL定义存在。
`query` 为只读连接,写SQL会被数据库引擎拒绝 — 判断仍为代理/人工的责任。
## 过滤 (`nesdc sync`)
| 选项 | 含义 | 值 |
|---|----|----|
| `-q`, `--query` | 搜索词 | 自由字符串 |
| `--field` | 搜索字段 | `agency` `client` `method` `frame` `name` `sido` `regno` |
| `--from` / `--to` | 时间范围 | `YYYY-MM-DD` |
| `--date-field` | 时间范围基准 | `registered`(默认) `published` `surveyed` |
| `--gubun` | 选举区分类 | 选举区代码(例: `VT044` 第22届总统选举) |
> 门户不会处理 `--date-field` 以外的过滤条件。 如果指定时间范围,
> 自动应用 `registered` 作为默认。
### 板块 (`[board]` 参数)
| 名称 | 内容 |
|---|---|
| `results` | 民意调查结果视图(详细元数据+PDF),默认 |
| `data` | 民意调查结果主要数据(按周) |
| `notices` · `library` · `actions` · `violations` | 公告/资料库/措施/违法案例 |
## 输出格式 · 全局选项
| `-f` | 用途 |
|---|---|
| `json` | 默认. 人类易读或 `jq` 处理 |
| `jsonl` | 单行记录. 批量收集/流式处理 (jq/duckdb/pandas 直接处理) |
| `table` | 终端直接显示 (汉字宽度对齐) |
| 全局选项 | 默认值 | 说明 |
|---|---|---|
| `-f, --format` | `json` | 输出格式 |
| `--delay` | `700ms` | 请求间隔 (速率限制) |
| `--base-url` | - | 门户URL自定义 (测试用) |
| `--db` | 系统目录 | 本地SQLite数据库路径 (`kvote mcp`/`kvote db` 使用) |
## 数据来源
| 提供方 | 网站 | 内容 | 无需API密钥 |
|---|---|---|:--:|
| `nec` | 韩国选举委员会 → data.go.kr | 选举结果(文件) + 投票率/当选人(OpenAPI) | 文件 **O** / OpenAPI X |
| `nesdc` | 韩国选举舆论调查委员会 (nesdc.go.kr) | 民意调查结果/样本构成 | **O** |
| `api` | data.go.kr 账户 | OpenAPI申请/认证密钥/到期管理 | X |
`nec` 由于选举统计系统(info.nec.go.kr)被robots.txt全面禁止爬虫,
**不直接抓取**。相反,使用**data.go.kr官方发布的选举结果文件(CSV/XLSX)** ,
无需API密钥进行搜索和下载。`nesdc.go.kr` 没有官方API,只能爬取。
## 开发
```bash
make build # 编译
make test # 测试 (无需网络,使用测试数据)
make fmt # gofmt
go vet ./...
```
内部结构请参考 [CLAUDE.md](CLAUDE.md),代理示例请参考 [AGENTS.md](AGENTS.md)。
## 贡献
欢迎提交issues和PR。在开始前,请阅读 [CONTRIBUTING.md](CONTRIBUTING.md) 的**两项不可妥协原则** (中立性 · 无需API密钥)。
- 更新日志: [CHANGELOG.md](CHANGELOG.md)
- 行为准则: [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md)
- 安全报告: [SECURITY.md](SECURITY.md) (请不要在公开的issues上报告)
## 许可
[MIT](LICENSE)