LoginSignup
scivola
@scivola

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

Ruby で HTML の特定ノードのソース上の位置が知りたい

解決したいこと

HTML が与えられ,その中の特定のノードを CSS パスや XPath で探し出し,当該箇所の位置を知るにはどうすればいいでしょうか。

例えば以下のような HTML があるとします。

<!-- 前略 -->
<ul>
  <li>foo</li>
  <li class="hoge">bar</li>
</ul><!-- 後略 -->

このような HTML において,クラス hoge を持つ要素,つまり,

<li class="hoge">bar</li>

が,HTML テキスト上で何文字目から何文字目まで占めているか,といったことが知りたいのです。

なお,「ノード」と書きましたが,想定しているユースケースではほぼ確実に要素なので「要素」と読み替えていただいても大丈夫です。

目的

HTML テキストに対し,文字列ベースでの加工を施したいのですが,加工箇所を HTML パーサーで絞りたいのです。正規表現で絞るのは極めて困難(現実には不可能)なので。

検討したこと

HTML のパーサーとして Nokogiri を用い,Nokogiri::HTML::Document の css メソッドや xpath メソッドで要素を得るのは簡単にできます。

しかし,得たノードは,「HTML テキスト上でどの位置を占めていたか」という情報を持っていないようです。
ドキュメントを見たのですが,そういう情報が取り出せそうなメソッドが見当たりませんでした。

Nokogiri の他には Oga というパーサーを検討しましたが,やはりそのようなメソッドは見つけられませんでした。

次に考えたのが,特定したノードの HTML 表現を使って検索をかけることでした。
しかし,Nokogiri のノードは,元の HTML を得る機能も持っていないようでした。
to_html メソッドはありますが,これは HTML を再構築する機能であり,元と同じものが再現できるとは限りません。
たとえば,

<img    src="foo.png"
 class='bar'>

<img src="foo.png" class="bar">

にされてしまいます。

0

2Answer

「ドキュメントを見た」とのことでご存じかと思われますが、Nokogiriは取得したノードの行のみですが位置は取得できます。ただしオリジナルではありません。

畑が違いますがPythonのlxmlライブラリだとオリジナルからノードの行数に限り得ることができますので、後は列数検索を独自実装することで目的に近いことは可能になるかもしれません。ご参考までに。

1

Comments

  1. @scivola

    Questioner

    Nokogiri::XML::Node#line は存在に気づいてませんでした。CRuby 版だとオリジナルの行番号が取れるようですね。
    ありがとうございます。

まさしく正規表現で置換なんてことを不要にするためのDOMパーサーを用いてレスポンステキストレベルの加工をしようとすること自体根本的な矛盾を感じますがいかがでしょうか.

そもそもスクレイピングか何かが目的とお見受けしますが,DOMを使って特定ノードの情報が取り出せるならそれ以上溯ること自体が無駄でしょう.
ノードの除去という点でもDOMが使えるならわけないです.
元文書の保全を目的としている場合は,むしろ下手に手をつけてしまうと処理的ないし情報規約的にまずい場合が多いです.

0

Comments

  1. @scivola

    Questioner

    ご心配には及びません,ありがとうございます。

  2. 心配というか普通は必要ないことをしているように見えるのです.できれば背景を補足願えませんか.

  3. @scivola

    Questioner

    HTML ファイルの加工をするのですが,その際,修正箇所以外は文字列(HTML テキスト)として温存するという要件があります。
    「DOM 的に同じ HTML は同じものを表しているんだから変わったっていいだろう」とか「その要件自体おかしい」というツッコミにはお答えしかねます。

Your answer might help someone💌