# 電子帳單每份都要輸身分證？實測 pikepdf：60 份 PDF 0.53 秒解開，但一個壞檔就讓程式停擺

- 網址：https://blog.valenvan.uk/posts/pikepdf-pikepdf.html
- 發布日期：2026-10-06
- 測試設備：Mac mini M4 Pro（12 核、64GB）
- 專案：pikepdf/pikepdf（https://github.com/pikepdf/pikepdf），MPL-2.0 授權
- 測試版本：pikepdf 10.16.0（qpdf 12.4.2），Python 3.12
- 實測影片：https://www.youtube.com/shorts/m_k1xcWYyz4

## 重點摘要

- 用 pikepdf 寫十幾行 Python，輪流試自己的身分證與生日密碼，60 份 AES-256 加密的假帳單 PDF 在 0.53 秒內全部解開，600 份約 5.08 秒。
- pikepdf 對 RC4-40、RC4-128、AES-128、AES-256 四種 PDF 加密方式，只要密碼正確都能解開；密碼大小寫不同（小寫 a123456789）就打不開。
- 資料夾混進一個損壞的 PDF 時，最簡單的批次腳本會因 PdfError 直接中斷，實測 60 份只解了 36 份，需要另外處理壞檔。
- pikepdf 沒有破解功能，只能用已知密碼打開自己的檔案；它是 Python 套件，需要會執行程式。
- **結論：** 會寫一點 Python 的人，用 pikepdf 批次解開自己的加密電子帳單又快又可靠，但要自己處理壞檔，否則只會做一半。
- **適合：** 每月收大量加密電子帳單、想批次歸檔或合併，且能執行簡單 Python 腳本的人。
- **不適合：** 完全不想碰指令的人，以及想打開不知道密碼之檔案的人（它沒有破解功能）。

| 實測項目 | 結果 |
|---|---|
| 安裝（pip install pikepdf） | 約 3 秒 |
| 60 份 AES-256 假帳單批次解密 | 0.51–0.53 秒，60/60 成功 |
| 600 份批次解密 | 5.08 秒，600/600 成功 |
| RC4-40／RC4-128／AES-128／AES-256 | 密碼正確時全部解開 |
| 密碼小寫、少一碼、多一個空白 | 都打不開 |
| 混進 1 個損壞 PDF | 腳本中斷，只解 36/60 份 |
| 解開後 60 份合併 | 60 頁、103KB，不到 0.1 秒 |
| 解開後內容 | 與加密前原檔逐位元組相同 |

月底想對帳，打開信箱：信用卡、電信、保險的電子帳單，每一份 PDF 都要輸入身分證字號才打得開。想翻一整年的紀錄，三張卡加電信加保險，就是 60 份、60 次輸入。

pikepdf 是 GitHub 上一個不到三千星（實測時 2,808 星）的 Python 套件，專門讀寫、修復、轉換 PDF，底層是老牌的 C++ 函式庫 qpdf。它沒有圖形介面，但只要十幾行程式，就能用「你自己知道的密碼」把整個資料夾的帳單一次解開。這篇記錄實際跑出來的結果，包括兩個會讓人卡住的地方。

**測試設備：** Mac mini M4 Pro（12 核、64GB），Python 3.12，pikepdf 10.16.0（內含 qpdf 12.4.2）。

**全部使用假資料：** 帳單是我自己用程式產生的，範例身分證字號 A123456789、範例生日 19900101，沒有用任何真實帳單或個資。

## 安裝：一行，約 3 秒

```bash
pip install pikepdf
```

有預編譯好的版本，不用另外裝系統套件，實測約 3 秒裝好。

## 準備 60 份加密的假帳單

我做了一年份的假帳單：三張信用卡各 12 份、電信 12 份、保險 12 份，共 60 份。信用卡與電信用身分證當密碼，保險用生日當密碼，加密方式是 AES-256。不給密碼直接開，pikepdf 會丟出 `PasswordError: invalid password`。

## 核心腳本：輪流試自己的密碼

```python
import pikepdf, glob, os, time
mine = ['A123456789', '19900101']  # 自己的密碼
os.makedirs('unlocked', exist_ok=True)
t, ok = time.time(), 0
for f in sorted(glob.glob('bills/*.pdf')):
  for pw in mine:
    try:
      with pikepdf.open(f, password=pw) as p:
        p.save(f.replace('bills', 'unlocked'))
        ok += 1; break
    except pikepdf.PasswordError:
      pass
print(f'解開 {ok} 份，{time.time()-t:.2f} 秒')
```

邏輯很單純：每份帳單把你的密碼清單從頭試一遍，打得開就另存一份到 `unlocked` 資料夾。原始檔不會被改動。

![60 份帳單 0.53 秒全部解開](https://blog.valenvan.uk/img/pikepdf-pikepdf-article_1.png)

```bash
python3 unlock.py
# 解開 60 份，0.53 秒
```

我另外比對過：解開後的檔案不再需要密碼，頁面內容和加密前的原檔逐位元組相同。

## 實測數字

| 項目 | 結果 |
|---|---|
| 安裝（pip） | 約 3 秒 |
| 60 份 AES-256 帳單解開 | 0.51–0.53 秒，60/60 成功 |
| 放大到 600 份 | 5.08 秒，600/600 成功 |
| 加密方式 | RC4-40、RC4-128、AES-128、AES-256 四種都解得開 |
| 密碼打成小寫 a123456789 | 打不開 |
| 密碼少一碼、多一個空白 | 都打不開 |
| 混進 1 個壞檔 | 程式中斷，60 份只解了 36 份 |
| 60 份合併成一份 | 60 頁、103KB，不到 0.1 秒 |

## 踩雷一：大小寫要完全一樣

台灣的帳單密碼常是身分證字號，開頭是英文字母。我分別試了 `a123456789` 和 `A123456789`：小寫直接回報密碼錯誤，大寫才打得開。多打一個空白也一樣打不開。寫密碼清單時要照帳單通知上寫的格式，一個字都不能差。

## 踩雷二：一個壞檔，整批停下來

這是實測裡最痛的一點。我在資料夾裡放了一個「下載失敗」的假 PDF（其實是一段 HTML），再跑一次同樣的腳本：

![混進壞檔後程式直接中斷](https://blog.valenvan.uk/img/pikepdf-pikepdf-article_2.png)

```bash
python3 unlock.py 2>&1 | tail -1
# pikepdf._core.PdfError: bills/broken.pdf: unable to find trailer dictionary while recovering damaged file
```

程式照檔名順序處理，碰到壞檔就整個中斷：60 份只解了排在它前面的 36 份，後面 24 份完全沒處理，而且沒有任何「完成幾份」的訊息。從信箱批次下載帳單時，偶爾有一份沒下載完整很常見，所以實際使用建議在 `except pikepdf.PasswordError` 後面再接住 `pikepdf.PdfError`，印出壞檔名稱後跳過（這個改法我沒有在本次實測中重跑，請自行確認）。

## 解開之後：合併成一份全年帳單

```python
import pikepdf, glob
year = pikepdf.new()
for f in sorted(glob.glob('unlocked/*.pdf')):
  year.pages.extend(pikepdf.open(f).pages)
year.save('2026全年帳單.pdf')
```

60 份合併成一份 60 頁、103KB 的 PDF，不到 0.1 秒。要找某個月的扣款，開一個檔就能翻完。

## 怎麼判讀結果

- 印出「解開 N 份」時，N 應該等於資料夾裡的檔案數；少了就代表有檔案每組密碼都試過仍打不開，或程式中途停了。
- 出現 `PasswordError` 是密碼不對；出現 `PdfError` 通常是檔案本身壞了，不是密碼問題。
- 另一個觀察：只「限制列印」、沒有設開啟密碼的 PDF，pikepdf 不用密碼就能開，並會回報該檔不允許列印。這類權限設定和開啟密碼是兩回事。

## 我的結論

我會留著它。一年整理一次帳單、或要把一疊加密 PDF 交給會計，十幾行程式就省下幾十次輸入，速度也完全不是問題。但它是 Python 套件，不是點兩下就能用的 App：至少要會執行一個 .py 檔，壞檔也要自己處理，否則它會安靜地只做一半。

它沒有破解功能，只能用你已經知道的密碼打開你自己的檔案；忘記密碼的檔案，它一樣打不開。

**適合：** 每個月收一堆加密電子帳單、想批次整理或歸檔的人；會一點 Python，或願意照著範例改密碼清單的人。

**不適合：** 完全不想碰指令的人；想打開不知道密碼的檔案的人（它做不到，也不該這樣用）。

## 專案資訊

- 專案：[pikepdf/pikepdf](https://github.com/pikepdf/pikepdf)
- 文件：https://pikepdf.readthedocs.io/
- 授權：MPL-2.0
- 受測版本：pikepdf 10.16.0（qpdf 12.4.2）
