跳转到主要内容

用 bea 把银行 CSV 导入 Beancount

用 bea 把银行 CSV 导入你的 Beancount 账本:映射列、按规则分类、预览条目、检查重复,然后入账。

普通的银行CSV不需要Python导入器。使用--csv映射其列,用--account指定来源账户,用--rules对行进行分类,然后使用bea import预览并应用条目。

你需要一个现有的账本。如果你刚开始记账,请遵循CLI快速入门。保留原始银行导出文件,以便与预览进行比较。

1. 映射CSV列​

将以下示例保存为statement.csv,然后从同一目录运行下面的命令:

Date,Payee,Narration,Amount
2026-08-02,Whole Foods,groceries,-20.00
2026-08-03,Shell,gas,-40.00
2026-08-04,Unknown Shop,mystery,-9.99

金额使用银行的符号约定:支出为负数,存款为正数。货币默认为账本的运营货币,因此此文件不需要货币列。将银行描述列放在narration中,并将payee保留给商户。

创建账本并打开下面使用的燃料子账户:

bea --no-input init books --currency USD --date 2026-08-01 \
  --opening-balance "Assets:Checking 1000"
bea --file books/main.bean add open --date 2026-08-01 --account Expenses:Transport:Fuel -c USD

模板已经打开了Expenses:Groceries和其他常见账户。它没有打开Expenses:Transport:Fuel,所以第二条命令在导入前打开它。全局选项如--file放在子命令之前。

2. 预览条目​

将以下分类规则保存为rules.toml,然后预览:

cat > rules.toml <<'EOF'
[[rule]]
match = "whole foods|trader joe|corner market"
account = "Expenses:Groceries"
 
[[rule]]
match = "shell|chevron|exxon"
account = "Expenses:Transport:Fuel"
EOF
bea --file books/main.bean import statement.csv --csv date=Date,amount=Amount,payee=Payee,narration=Narration --account Assets:Checking --rules rules.toml

规则首先匹配payee,然后是narration,忽略大小写。第一个匹配的规则获胜。没有规则匹配的行会以!标志过账到Expenses:Uncategorized,以便稍后审查。产品IMPORTING指南记录了完整的映射参考,包括debit和credit配对、category列以及--csv auto表头读取。

此时还没有写入账本。预览报告3 ready, 0 exact duplicates, 0 possible duplicates并以0退出。其RULE列按行命名获胜模式,或对Unknown Shop行显示unmatched。审查日期、payee、带符号的来源金额、目标账户、重复匹配和提议的文件差异。修正错误的规则或分类,然后再次预览。在应用导入前打开任何缺少的账户:规则指定的账户如果账本未打开,验证会失败。

3. 应用已审查的条目​

bea --file books/main.bean import statement.csv --apply
bea --file books/main.bean check
bea --file books/main.bean list transaction --flag '!'
bea --file books/main.bean query "SELECT account, sum(position) WHERE account = 'Assets:Checking' GROUP BY account"

列映射按账本、表头行和来源账户记住,因此--apply不带标志地重新运行,并使用记住的列映射报告。它根据当前文件重新计算预览,在写入前验证完整的候选账本,并写入3个条目。bea check报告无错误。!队列列出唯一不匹配的行:Unknown Shop,mystery,-9.99 USD。通过的检查仅证明账本平衡且验证有效。它对该行是否属于Expenses:Uncategorized不发表意见,因此在你的账本中刻意地重新分类它。检查结束于930.01 USD:1,000 USD的开户余额减去69.99 USD的支出。

4. 重复导入不会添加内容​

bea --file books/main.bean import statement.csv --apply

预览报告0 ready, 3 exact duplicates,运行写入0个条目并以0退出。每个写入的行都带有import-id元数据,包含内容哈希,因此相同文件匹配每一行。编辑导入的条目时保留该元数据。导入是添加条目;它不会更新或删除现有交易。在账本中刻意地进行修正,然后运行bea check。使用bea add transactions进行批量JSON条目没有重复检测。

5. 解决可能的重复项​

后续下载可能重复一行,但narration或银行ID不同。日期、归一化的payee和带符号的来源金额仍将其标记为可能的匹配:

预览状态含义该怎么办
new未发现重复证据检查金额和分类
duplicate稳定ID和交易详情匹配,或存在相同的非交易指令已跳过
possible_duplicate日期、归一化payee和带符号的来源金额/货币匹配将预览与现有条目进行比较
conflict稳定ID匹配不同的交易详情解决ID或数据差异,然后再次预览

不同的银行ID并不能排除重复。银行在后续下载时可能更改ID。两笔真实购买也可能共享日期、payee和金额,因此可能的匹配是证据而非证明。Bea不会用AI模型猜测,也绝不在你的规则之外为你分类。

默认的--duplicates review拒绝应用未解决的匹配。在验证运行中,第二个文件在另一narration下重复2026-08-02 Whole Foods -20.00 USD行,预览为1个可能的重复,--apply以4退出且未写入任何内容。审查每个可能的匹配后,选择以下替代方案之一:

bea --file books/main.bean import statement.csv --apply --duplicates skip
bea --file books/main.bean import statement.csv --apply --duplicates include

选择include以保留合法的重复购买。该决定适用于该次调用中的所有可能的匹配。精确重复项仍被跳过。ID冲突仍阻止写入。--no-input和--yes不会绕过该审查。故意决定跳过每一行会以0退出且不添加账本条目。

6. 对其他格式使用Python导入器​

对于列映射无法表达格式,如OFX或QIF或布局异常的CSV,bea import使用当前Beangulp接口调用配置的导入器:identify(filepath)、account(filepath)和extract(filepath, existing)。导入器拥有银行特定的解析和分类。它必须在来源账户过账上提供明确的金额,以便重复匹配使用实际的银行金额。Python导入器仍然是这些格式的高级路径。对于银行的原生CSV,先尝试--csv。

对于首次练习,将示例分类CSV配置保存为根账本旁边的importers.py。它只使用Beancount和Python标准库,因此适用于Homebrew安装。其示例bank.csv使用带符号的支票账户金额:-5.25 USD的餐饮支出和1,000 USD的工资存款。示例配置期望其文档中规定的确切列。只执行你信任的Python配置。

bea --file books/main.bean import bank.csv --config importers.py
bea --file books/main.bean import bank.csv --config importers.py --importer categorized-checking
bea --file books/main.bean import bank.csv --config importers.py --apply

你的importers.py配置导出CONFIG = [importer, ...]。如果多个导入器识别该文件,请按名称选择一个。未知名称会列出配置的名称。已知导入器未识别该文件时,会单独报告。

CLI记住此根账本的配置路径。未来的运行先选择显式的--config,然后是记住的路径,最后是根旁边的importers.py。输出会命名路径及其来源。

--apply根据当前文件重新计算预览。它在写入前验证完整的候选账本。验证失败会保持原始账本不变并以1退出。并发账本更改以4退出;检查更改并在重试前运行新的预览。

保持导入可重复​

默认情况下,重复匹配检查导入器来源账户内的bank_id、fitid、transaction_id和imported_id元数据。使用重复的--id-key KEY选项替换该集合。

具有稳定银行ID的行会写入import-id元数据,命名其种类,如bank:或ofx:前缀。没有的行会写入csv:sha256:内容哈希,覆盖其日期、金额、描述和账户,因此重新导入相同文件会跳过每一行。在此约定之前写入的条目可能仍带有bea_import_id元数据,那些重新导入时仍会匹配。可能的匹配会与现有交易和同一批次中已接受的行进行检查。

Payees、narrations和字符串元数据在预览和写入前将换行符替换为空格。引号和反斜杠保留其内容。因此导入的商户文本在单行账本上保持可读。

写入包含的文件​

将--file指向根,并用--into选择目标:

bea --file books/main.bean import statement.csv --into 2026.bean
bea --file books/main.bean import statement.csv --into 2026.bean --apply

2026.bean必须已存在并由根包含。其路径相对于根目录。导出路径仍相对于你的工作目录。预览会识别将要更改的文件。

在脚本中使用导入​

bea --file books/main.bean --json --no-input import statement.csv --apply --duplicates skip

仅当skip是你的预期策略时才选择它。JSON在data中返回预览和写入计数。被拒绝的应用将预览放在stderr上的error.result中,written: 0。始终检查退出状态。在调度无人值守导入之前,请参阅JSON和退出码参考。

排查导入器问题​

导入器配置在托管引擎中运行。如果配置导入Beangulp,请安装系统libmagic库并在那里启用一次Beangulp:

bea engine enable beangulp
bea --file books/main.bean import bank.ofx --config importers.py
bea --debug --file books/main.bean import bank.csv --config importers.py

bea engine status报告已启用的功能。在bea前端旁边安装银行导入器并不会使其在引擎内可用。导入额外包的配置需要这些依赖项在引擎中;仅启用Beangulp不会安装它们。当这些导入器依赖项不可用时,请使用上述CSV映射器或转换器。

对于导入器异常,在命令前放置--debug以显示其traceback。导入器输出捕获在importer_output中,因此不会破坏JSON。在JSON调试模式下,traceback是error.traceback。

对于一次性转换而不使用Python导入器,请尝试CSV转换器或OFX和QIF转换器。在将它们添加到你的账簿之前,审查生成的条目。

来源:https://beancount.io/zh/docs/Solutions/import-bank-exports-cli