This commit is contained in:
+69
-6
@@ -304,15 +304,78 @@ BrowseWithSession({
|
||||
|
||||
ダウンロードを認証付きで行いたい場合は、Browser Sessions 機能で対象サイトのログインセッションを保存し、タスクで bind した状態で BrowseWeb を呼ぶこと。
|
||||
|
||||
## ソースライブラリへの蓄積(スペースタスク)
|
||||
## ページの魚拓(本文+画像)
|
||||
|
||||
スペースのタスクで BrowseWeb がページ本文(スナップショット / セレクタ抽出 / dumpHtml 等の抽出テキスト)を取得すると、その本文が Markdown として `source/` に保存され、出典メタ(URL・取得日時・サイズ・`tool: "BrowseWeb"`)が `source/index.jsonl` に追記される。WebFetch / DownloadFile と同じソースライブラリに合流するので、「そのスペースのソース(資料)」が自動で溜まり、後から追加調査やグラウンディングに使える。
|
||||
BrowseWeb が開いたページは、本文とそのページに載っていた画像が `source/` に自動で溜まる。出典メタ(URL・取得日時・サイズ・`tool: "BrowseWeb"`)は `source/index.jsonl` に 1 行ずつ追記される。WebFetch / DownloadFile と同じソースライブラリに合流するので、後から追加調査やグラウンディングに使える。
|
||||
|
||||
- 同一 URL は重複保存しない(既に index にあればスキップ)。同じ URL を 2 回閲覧しても 1 件のまま
|
||||
- スペース文脈でないタスク(ephemeral / gitea issue 等)では従来どおり蓄積しない(`ctx.spaceId` が無いとき)
|
||||
**属している場所で挙動は変わらない。**スペースのタスクでも、タスク一覧から直接作ったタスクでも、定期実行・サブタスクでも同じように溜まる(issue #1097 / 由来 #1093)。
|
||||
|
||||
### 何が・どこに
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| ページ本文 | `source/<ページ名>.md` |
|
||||
| ページに載っていた画像 | `source/images/<名前>.<拡張子>` |
|
||||
| 索引 | `source/index.jsonl`(1 行 1 件) |
|
||||
|
||||
索引の画像行は `kind: "image"` を持ち、`page_url` / `page_file` で**どのページに載っていた画像か**を指す。`sha256`(中身の指紋)・`width` / `height` も入る。ページ本文の行は従来どおり `kind` を持たない。
|
||||
|
||||
保存された画像は `ReadImage({ file_path: "source/images/xxx.png" })` でそのまま読める。**画像のための新しいツールは無い。**
|
||||
|
||||
### 取り方
|
||||
|
||||
画像は **開いているページの文脈**で取りに行く(`page.request`。Cookie・User-Agent はブラウザコンテキストのもの、Referer は開いている URL)。
|
||||
|
||||
**画像の URL を別のツール(DownloadFile 等)に渡して取り直してはいけない。**ログインが要るページ・リンク元を見て弾くサイト・期限つきの署名が付いた URL がいずれも取れなくなり、この機能の価値が消える。ここがスクリーンショット依存を外せる理由。
|
||||
|
||||
候補は 2 つの経路から並べる。
|
||||
|
||||
1. `document.images` の `currentSrc`(srcset / picture で実際に選ばれた 1 枚)。実寸(`naturalWidth` / `naturalHeight`)が取れるので寸法の下限が効く
|
||||
2. Resource Timing(`performance.getEntriesByType('resource')`)のうち `initiatorType` が `css` / `img` のもの。**スタイルシートの背景として敷かれた画像はここにしか出ない**——記事の主役の写真が背景で敷かれている作りのサイトでは、`<img>` だけ見ていると図が丸ごと落ちる。実寸は取れないので、下限の判定は見送って残す側に倒す
|
||||
|
||||
並び順は `<img>` が先。取りに行く回数には上限があるので、フォントや装飾で枠を食い潰させない。資源の一覧には画像でないもの(フォント等)も混じるが、**種類で選り分けない**——画像かどうかは取ってきた中身の先頭バイトで決める。
|
||||
|
||||
`data:` URL はその場で復号する。`blob:` は取りに行けないので対象外。インラインの `<svg>` 要素と `<canvas>` は、そもそも取りに行く URL が無いので入らない。
|
||||
|
||||
`page.request` はページの route 傍受を通らないので、**取得前に SSRF の関所(`ssrfCheck`)を通す**。ページに `<img src="http://169.254.169.254/...">` を仕込まれても内部へ取りに行かせない。許可ホスト(`tools.webfetch_allowed_hosts`)は BrowseWeb 本体と同じものを使う。
|
||||
|
||||
### 歯止め(意味は見ない)
|
||||
|
||||
「これは飾りだから捨てる」という判定は書かない。書けば必ず漏れるし、漏れて困るのは「あとから欲しかったのに無い」側。歯止めは意味を見ないものだけ。
|
||||
|
||||
| 歯止め | 既定 | config.yaml(`tools` 配下) |
|
||||
|---|---|---|
|
||||
| 中身の指紋で畳む | 常時 | (設定なし) |
|
||||
| 1 ページあたりの枚数 | 20 | `browse_image_max_per_page` |
|
||||
| 1 枚あたりの容量 | 5MB | `browse_image_max_size_mb` |
|
||||
| 容量の下限 | 1024 バイト | `browse_image_min_bytes` |
|
||||
| 寸法の下限 | 32 ピクセル | `browse_image_min_dimension` |
|
||||
| 取りに行く回数 | 枚数の上限 × 3(既定 60 回) | (設定なし。枚数の上限に連動) |
|
||||
|
||||
寸法の下限は **実寸が取れたときだけ**効く。取れていない画像は残す側に倒す(飾りは入る前提)。
|
||||
|
||||
**回数の上限は「残した枚数」とは別に数える。**残した枚数だけを見ていると、1 枚も残らないページ(配信元が落ちている・下限を下回る画像ばかり)で候補の全件に取りに行ってしまう。1 枚あたり 15 秒待つので、候補が数百あるページを開いただけでブラウザ操作が何分も返らなくなる。
|
||||
|
||||
溜まったものを自動で削除する仕組みは入れない。索引に参照が残るので、「どのページからも参照が無い」「1 ページでしか使われず小さい」といった条件で後から掃除する道は残る。
|
||||
|
||||
### その他
|
||||
|
||||
- 同一 URL のページ本文は重複保存しない(既に index にあればスキップ)。**ただし本文が既出でも画像は取りに行く**——WebFetch で本文を読んでから、図が要ると気づいてブラウザで開き直す流れを塞がないため。再訪の抑止は 3 段。①このプロセスでこのページの収集を試したか(`hasPageArchiveAttempt`。索引に痕跡が残らない「1 枚も溜まらなかったページ」がここに当たる。中断で切ったときは記憶しない)、②索引にこのページの画像行があるか、③すり抜けた分は指紋の判定が止める
|
||||
- 中身が同じ画像は実ファイルを 1 枚に畳み、索引の行だけ足す(ロゴ・アイコンが何ページぶんも積み上がらない)
|
||||
- 空・エラー結果、`http(s)` 以外(`file://` 等)の URL はソース化しない
|
||||
- スクリーンショットはソースではないので `output/` に残り、`source/` には入らない(ソースはあくまでテキスト)
|
||||
- これは純粋な追記サイドライトで、BrowseWeb の戻り値・スクショ・`logs/` 出力は一切変わらない
|
||||
- スクリーンショットはソースではないので `output/` に残り、`source/` には入らない
|
||||
- 魚拓は副作用。失敗してもブラウザ操作そのものは通る。戻り値には `[archive] source/images/ に 7 枚(索引: source/index.jsonl)` の形で 1 行だけ足る。落ちた枚数は理由ごとに分けて出す(`上限・下限で対象外` / `画像として読めない中身` / `取得できず`)
|
||||
- 本文を取らない操作列(`goto` → `screenshot` 等)でも `goto` 直後に魚拓する。`BrowseWithSession` も同じ経路を通る
|
||||
|
||||
### 形式の判別
|
||||
|
||||
拡張子と `content-type` は**見ない**。どちらも当てにならないため——オブジェクトストレージの署名つき URL は拡張子を持たず `application/octet-stream` で配られることが多く、逆に `content-type: image/png` でも中身が HTML のエラーページということがある。
|
||||
|
||||
判別は**中身の先頭バイト**で行う(`sniffImageExtension`)。PNG / JPEG / GIF / WebP / BMP / TIFF / ICO / AVIF / SVG を見分け、**どれにも当たらないものだけ捨てる**。形式の判別であって、意味の判定ではない。AVIF は主ブランドが `mif1` で `avif` が互換ブランド側に載る配り方があるので、`ftyp` ボックスの互換ブランドの並びまで見る。
|
||||
|
||||
保存した画像は `ReadImage` がそのまま読める。SVG / AVIF / TIFF / ICO はモデルが直接受け取れないので、`ReadImage` 側が PNG に起こしてから渡す。唯一の例外は **BMP を格納した古い形式の `.ico`**(sharp が読めない)で、この場合は理由付きのエラーが返る。溜まってはいるので、必要なら別途変換できる。
|
||||
|
||||
動画・音声は対象外(`<img>` にも Resource Timing の `img` / `css` にも出ない)。フォントは候補には入りうるが、先頭バイトで画像でないと分かるので保存されない。
|
||||
|
||||
## SSRF 保護
|
||||
|
||||
|
||||
Reference in New Issue
Block a user