bhr を作った
bhr という CLI を作った。
Chrome の履歴を読んで、その日に読んだページを日ごとの一覧にしてくれるやつ。名前は browser history reading の頭文字。
ブラウザの履歴をObsidianに記録しているんだけどそこに活用したり、単純に「前にあれみたよなぁー・・・なんだっけ?」ってときに地味に便利。
Rustで実装していてるのでインストールも以下の通り簡単にできる。
$ cargo install bhr
読んだページだけにする
Chrome の履歴をそのまま出すと、ほとんどが読み物ではない。GitHub、Slack、Google の検索結果、X のタイムライン、localhost。なので除外は設定ファイル(~/.config/bhr/config.toml)に書くようにした。
allow = [] # 空なら deny 以外はすべて拾う。書けば、載っているホストだけ拾う
deny = ["github.com", "x.com", "google.com"] # サブドメインごと落とす
tracking_params = ["utm_*", "fbclid", "ref"] # URL から落とすパラメータ
localhost、IP アドレス、auth. や login. の認証画面、トップページ(/)、iframe の中で開かれたページは、設定に関係なく常に落とす。サイトごとの事情は設定、どこでも要らないものはコード、で分けている。
同じ日に同じページを何度開いても 1 行にまとめる。まとめる鍵は URL ではなくホストと題にした。検索欄に 1 文字打つたびに別の URL で履歴が残るサイトがあるので、URL で見ると同じページが何十行も並ぶ。残すのはいちばん短い URL。
それでも仕事のドメインは入ってくるので、読み物だけにしたいなら allow に読むサイトを並べるほうが早い。TUI でページを選んで x を押すと、そのホストを deny に足して設定ファイルに書き込む(toml_edit で書くのでコメントは消えない)。
History は写してから開く
Chrome の履歴は ~/.config/google-chrome/Default/History にある SQLite。Chrome が動いている間はロックされているので、一時フォルダにコピーしてから開いている。
visits.visit_time は 1601-01-01 からのマイクロ秒。日付への変換は SQL でやってしまう。
date(v.visit_time / 1000000 - 11644473600, 'unixepoch', 'localtime')
transition の下位 8bit が 3 か 4 のものは iframe で開かれたページなので、ここで落とす。
TUI と、意味で探す
引数なしで起動すると TUI になる。左に月と日、右にその月・日に開いたページ。最初は日付を全部並べていたけど、長すぎるので月を選んでから日を開く形にした。
/ は文字の絞り込み。? は jev に「検索語 で探している人が求めているページか」をはい/いいえで聞いて、その確率の順に並べる。1 ページ 1 リクエストで、min_score(既定 0.7)未満は出さない。CLI でも bhr list --ask "..." で同じことができる。
bhr list | jev のようにパイプで渡す案もあったけど、jev は標準入力を丸ごと 1 つの状態として受け取って答えを 1 つ返すので、ページごとの点数にはならない。なので bhr の中から 1 ページずつ投げている。
何を検索したかも残っていた
作っている途中で、Chrome が SQLite に何を持っているのか気になって、プロファイルの中を全部見てみた。
| ファイル | 中身 |
|---|---|
History |
履歴、ダウンロード、検索した語、訪問の間のつながり |
Shortcuts |
アドレスバーに打った文字と、開いた URL |
Favicons |
ページのアイコン |
Cookies |
cookie(値は暗号化) |
DIPS |
bounce tracking の記録 |
History の中の keyword_search_terms に、検索窓に打った語がそのまま残っている。これを日ごとに出す bhr searches を足した(v0.3.0)。
さらに visits.from_visit に「どの訪問から来たか」が入っているので、検索結果の画面から開いたページもたどれる。
# 2026-09-29 に検索した語
- 東名 混雑
- [交通情報 | ドライバーズサイト | 高速道路・高速情報はNEXCO 中日本](https://www.c-nexco.co.jp/jam/)
- [東名高速道路の渋滞情報 - NAVITIME](https://www.navitime.co.jp/highwaycongestion/prediction/result?id=3938)
- 新東名 静岡
- 新東名 ガソリン スタンド 上り
- [新東名・新名神(上り)東京方面|高速GS](https://highwaygs.jp/route/shintomeishin-1.html)
検索語そのものは除外しない。検索エンジンは deny に入っているので、ページと同じ除外をかけると全部消える。開いたページのほうには同じ除外をかけていて、そのおかげで検索結果の 2 ページ目(google.com)は勝手に落ちる。
その代わり、deny に入れている Amazon へのクリックや、トップページ(omarchy plugin で検索して開いた plugins.omarchy.org/)も一緒に落ちる。ここは割り切った。
from_visit は 1 段しかたどっていないので、リダイレクトを挟んだページは取りこぼす。困ったら再帰でたどる。
TUI では、選んでいる月・日の検索語をページ一覧の下の枠に出している。
crates.io に置いた
v* のタグを push すると、GitHub Actions から crates.io に publish されるようにした。
cargo install bhr
Chrome はだいたい 90 日より古い履歴を消すので、残しておきたいなら定期的に bhr list --json で書き出しておくといい。
Firefox(places.sqlite)と Safari(History.db)はまだ読めない。