LoginSignup
1
0

More than 5 years have passed since last update.

ある要素の後に特定のテキストノードがある場合の xpath の書き方

Posted at

gem の html-pipeline を使って、独自のマークダウンフィルターを追加しているとき、ビジネスロジック上、かなり変化球なフィルターを作りたくなるときがあると思います。もしくは、コード上は入り組んでるけど諸事情があってそういった仕様にしなきゃいけないときとか。

今回、結局使わなかったけど、なかなかイレギュラーな xpath の書き方だなと思ったので、備忘録的にここにメモしておきます。
(こういう備忘録的なこと書くのってやっぱ個人のブログの方が良いんだよね…?ブログ作ろ…)

特定のテキストノードが a タグの後にある場合とは

html-pipeline の HTML::Pipeline::MarkdownFilter を使うときの依存 gem である Commonmarker のそもそもの設計思想として、「マークダウンはHTMLを書くためのものじゃないよ」ってのがあります。そのため、変換できない要素とか属性とかが結構あったりします。

たとえば、 <div> とか、 target="_blank" とか。

なので、これらをもしマークダウンで書けるようにしたいのであれば、独自のフィルターを作るしかない。うん作ろう。

リンクに target="_blank" をつけるための記法を決める

[]():blank のように、リンクの末尾に :blank がある場合は、 target="_blank" をつけようみたいな仕様を考えたとします。

[これは target="_blank" のリンクです。](https://example.com)       → target="_blank" つかない
[これは target="_blank" のリンクです。](https://example.com):blank → target="_blank" つく

このとき、HTML::Pipeline::MarkdownFilter の内部で動いてる nokogiri は以下のようにパースしてくれています。

(Element:0x3fc248dead9c {
  name = "p",
  children = [
    #(Element:0x3fc248df7330 {
      name = "a",
      attributes = [
        #(Attr:0x3fc248f308dc { name = "href", value = "https://example.com" })],
      children = [ #(Text "これは target=\"_blank\" のリンクです")]
      }),
    #(Text ":blank")]
  })

このように、 Element の後に Text が続いてるのが分かると思います。つまり、 []():blank のように書く場合はおおよそ上記の構造になると考えて差し支えなさそう。

nokogiri で target="_blank" を付与する

詳細は html-pipeline の仕様とも絡むので詳細は割愛しますが、 HTML::Pipeline を初期化するときに引数に独自のフィルターを渡すと、内部の call メソッド内で doc メソッド経由で Nokogiri::HTML::DocumentFragment にアクセスすることができます。このインスタンスにパースされる HTML 要素が全部詰まっています。

class HogeFilter < HTML::Pipeline::Filter
  def call
    # your custom filter goes here
    # ex) doc.css('a').each { |a| a[:rel] = "nofollow" }
    #     doc.search('.youtube-movie').wrap("<div class='wrapper'>")
  end
end

ここで、 直後に :blank というテキストを持つ a タグのみを抽出する方法を xpath で書きたいと思います。

doc.xpath('.//a[contains(following-sibling::text(), ":blank")]')

ところで xpath の記法については クローラ作成に必須!XPATHの記法まとめ がおすすめです!

ということで、これバカみてーに一日試行錯誤してこういう記法に落ち着きましたが、他にもっと短い(もしくは xpath を使わない)記法がありましたら教えていただけると幸いです。 RuboCop に怒られる確率も減ることでしょう…。

参考文献

https://qiita.com/rllllho/items/cb1187cec0fb17fc650a
https://github.com/jch/html-pipeline
https://github.com/sparklemotion/nokogiri
https://gogodiet.net/z/xml/7.htm
https://www.techscore.com/tech/XML/XPath/XPath3/xpath03.html/

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0