http.get で取得してCSS selectorで分解できます。Chromiumも、モデルも、記録されたブラウザーセッションも不要です。std/html には、そのための3つのpure Toolが含まれています。
| Tool | 形 | 使う場面 |
|---|---|---|
html.extract | string[] | 見出し、価格、hrefなど、繰り返し現れる1種類の値が欲しい場合。 |
html.table | string[][] | セルの行を取得し、下流で列をindex指定して選ぶ場合。 |
html.records | object[] | 列ヘッダーをキーにした行が欲しく、名前でfilterしたい場合。 |
3つとも capabilityなし の
kind: "transform" として宣言されています。HTML文字列を受け取り、データを返すだけで、ネットワーク、ファイルシステム、時計には一切触れません。pureなので、エンジンはこれらをJournalに記録しません。同じ html と select が与えられれば毎回同じ結果を生成するため、リプレイでは記録済みの値を再生するのではなく、parseを再実行します(ToolとPackを参照)。記録されるのは、http.get 経由で取得したページそのものであり、そのparse結果ではありません。SDKは、この3つのTool専用のbuilderをまだexportしていません(
@browserflow/sdk に http / browser / transform helperがある http.get / http.post、browser.tool、transform とは異なります)。下に示すように、YAMLから use: html.extract / html.table / html.records で参照してください。fluent SDK builderでFlowを作成している場合は、生の { use: "html.extract", withRaw: {...} } as StepDef というescape hatchがサポートされていると考える前に、SDK ownerに確認してください。これはまだdocumented APIではありません。アンカーとなるrecipe: recipe substitutions
examples/01-recipe-substitutions は、このrepo内で最小の、完全なブラウザーなし・AIなしのFlowです。HTTPでrecipe pageを取得し、1つのCSS selectorから本文を取り出し、実際にsubstitutionの名前を挙げている文だけを残します。
flow.yaml
http.get がページ本文を一度Journalに記録し、html.extract が各実行(またはリプレイ)でそこから本文を決定論的に再導出し、transform がexpression languageでfilterします。同じ入力なら、バイト単位で同じ答えになります。これがruntimeの要点です(実行を参照)。
html.extract — 選択して投影する
html.extract はHTML文字列に対してCSS selectorを実行し、matchした各要素を文字列へ投影します。対象はそのtext、href、または任意の属性です。
| 入力 | 型 | 備考 |
|---|---|---|
html | string | parseするマークアップ。通常は ${{ steps.<fetch>.output.body }} です。 |
select | string | CSS selector。tag、class、id、attribute、またはdescendant combinatorを指定できます。 |
as | "text" | "href" | "attr" | 任意。defaultは "text" です。"href" は href 属性を読みます(存在しない場合は空文字列)。"attr" は attr で指定した任意の属性を読みます。 |
attr | string | as: "attr" の場合は必須です。指定しないと、このToolは as:"attr" requires a non-empty attr をthrowします。 |
limit | number | document orderで、最初のN件のmatchだけを残します。 |
{ values: string[] }。matchした要素ごとに1つの文字列を、document orderで返します。
html.table — セルの行
html.table は html.extract の2次元版にあたるToolです。行を選択し、次に各行内のセルを選択して、セル配列のリストを返します。列は下流でindex指定して選びます。
YAML
| 入力 | 型 | 備考 |
|---|---|---|
html | string | parseするマークアップ。 |
select | string | row selector。defaultは "tr" です。 |
cell | string | 行内のcell selector。defaultは "td, th" です。 |
as / attr | html.extract と同じ | 各行ではなく、各 cell を投影します。 |
limit | number | 最初のN行だけを残します。 |
{ rows: string[][] }。
html.records — headerをキーにした行
html.records は html.table のキー付き形式です。各行のセルを列ヘッダーと対応付け、objectの配列を返します。そのため、数値indexではなく、名前付きの列でfilterできます。
YAML (explicit header selector)
YAML (header inferred from the first row)
| 入力 | 型 | 備考 |
|---|---|---|
html | string | parseするマークアップ。 |
select | string | row selector。defaultは "tr" です。 |
cell | string | 行内のcell selector。defaultは "td, th" です。 |
header | string | header cell用の任意selector。指定した場合、matchした要素の text がcolumn keyになり、select にmatchしたすべての行がdataとして扱われます。省略した場合、少なくとも1つのセルを持つ、matchした 最初の 行がkeyを提供し、dataとしてはskipされます。 |
as / attr | html.extract と同じ | 各cellの value を投影します(header textではありません。header textは常にplain textです)。 |
limit | number | 最初のN data rowだけを残します。 |
{ records: Array<Record<string, string>> }。
Header key: whitespaceをcollapseし、trimし、重複を除く
Header key: whitespaceをcollapseし、trimし、重複を除く
Header textはkeyになる前に
replace(/\s+/g, " ").trim() でnormalizeされます。" Meta Score \n" は "Meta Score" になります。2つのheader cellが同じkeyにcollapseされる場合、後続のものにはdocument orderで数値suffixが付きます。col、col_2、col_3、… のようになります。そのため、空の <th> が2つあるtableでは "" と "_2" というkeyが生成されます。重要なunlabeled columnがあるマークアップでは、上流でrenameする価値があります。セルが0個の行はhtml.tableと同じくdropされる
セルが0個の行はhtml.tableと同じくdropされる
ある行が
select にmatchしても、その子要素が1つも cell にmatchしない場合、その行は何も生成しません。空のrecordすら生成しません。不ぞろいな行: 余分なcellはdropされ、短い行はkeyを省略する
不ぞろいな行: 余分なcellはdropされ、短い行はkeyを省略する
header keyより多くのcellを持つ行では、余分なcellは静かにdropされます(
cells.length > keys.length)。keyより少ないcellしかない行では、不足したkeyを "" で埋めるのではなく、そのrecordから単に省略します。したがって、すべてのrecordがすべてのkeyを持つとは想定しないでください。列が疎になる可能性がある場合は、下流で input.foo ?? "default" のようなexpressionを使ってguardしてください。下流でrecordをfilterする
html.records は各行に列名でkeyを付けるため、自然な次の手順は expression language を使った transform filterです。
YAML
html.table の rows は名前を持たないため、同等の処理では item.<key> ではなく nth(item, <index>) を使います。tableに実際のheader rowがある場合は、できるだけ html.records を使ってください。
関連
標準Pack
std/html が他のfirst-party Packの中でどこに位置するか、そしてなぜpure ToolはJournalに記録されないのか。カスタムTool
独自の
defineTool を作成します。これは html.extract が実装しているものと同じpure-tool contractです。式言語
filter、map、nth、to_number。抽出したlistを答えに変換する関数です。実行
pure Toolがreplayされるのではなく再実行される理由と、それによってFlowの再現性が保たれる仕組み。