1
1

More than 3 years have passed since last update.

Amazon Linux から Amazon Linux 2 への移行中 xpath ではまった話

Posted at

Amazon Linux AMI (2018.03) のサポート期間が2020年12月31日まで延びましたが、目下、過去の遺物を Amazon Linux 2 へ移行しています。
これはそんな中起きた問題で、結論は単純にxpathの演算子の優先順位を勘違いしていたという話です。
自分だけの勘違いではなく、まわりにも何人かいたので、もしかしたら有用かなと思って書きました。

皆さんは、このhtmlの2番目のh3のテキスト "Fuga" をxpathで取りたいときにどう書くでしょうか。

test.html
<!DOCTYPE html>
<html>
    <div>
        <h3>Hoge</h3>
        <h3>Fuga</h3>
        <h3>Gero</h3>
    </div>
    <div>
        <h3>Foo</h3>
        <h3>Bar</h3>
        <h3>Baz</h3>
    </div>
</html>

私とそのまわりの何人かは何の違和感もなくこう '//h3[2]/text()' 書いていました。

scraping.rb
require 'nokogiri'

doc = Nokogiri::HTML(open('test.html'))
p doc.xpath('//h3[2]/text()')

これは Amazon Linuxで yum パッケージの ruby2.0 と nokogiri だとこういう結果になります(これが古すぎるというのは置いといて)。

$ ruby scraping.rb
[#<Nokogiri::XML::Text:0x1160c78 "Fuga">]

そして、Amazon Linux 2 に移行すると、rubyは amazon-linux-extras の ruby2.4、nokogiri は gem で入れることになり、結果はこうなります。

$ ruby scraping.rb
[#<Nokogiri::XML::Text:0x2aea55368304 "Fuga">, #<Nokogiri::XML::Text:0x2aea55368160 "Bar">]

! !
異なる結果になってしまうのでした。これになかなか気づけずはまっていました。

理由

libxml2 の 2.9.2 でこういった修正が入っています。

Fix XPath '//' optimization with predicates (Nick Wellnhofer),

xpath の仕様では、 [] 演算子 は // よりも優先度が高いので、 //h3[2]/text() と書くと h3[2] の方に先にバインドされてしまうんですね。上で期待する操作をするときは (//h3)[2]/text() こう書くのが正でした。

libxml2 のバージョン 2.9.1 までここが間違っていて、Amazon Linux の yum リポジトリの libxml2 が 2.9.1 だったので、これまでうまく動いてしまっていたということです。

ちなみに

  • Amazon Linux でも gem で nokogiri をインストールしていれば回避できていました
  • Amazon Linux 2 でも yum の libxml2 は 2.9.1 なので、 xmllint なんかは間違ったままです
1
1
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
1