LoginSignup
2
1

More than 1 year has passed since last update.

Nokogiri の䜿い方 🪚

Last updated at Posted at 2022-04-17

前提

  • Nokogiri の䜿い方に぀いお
    • Ruby で XML ずか HTML をいい感じに扱えるラむブラリ
    • バヌゞョン 1.6.1

サンプル

こんなHTMLドキュメントがあったずしお、

<html>
  <head>
    <title>Lightweight language</title>
  </head>
  <body id="test_id">
    <h1>Lightweight language</h1>
    <div>
      <ul>
        <li>
          <a href="link_ruby" title="ruby">Ruby</a>
        </li>
        <li>
          <a href="link_python" title="python">Python</a>
        </li>
        <li>
          <a href="link_php" title="php">PHP</a>
        </li>
        <li>
          <a href="link_perl" title="perl">Perl</a>
        </li>
      </ul>
    </div> 
  </body>
</html>

ここから「各蚀語ずそのリンク」を抜出したい

require 'open-uri'
require 'nokogiri'

html = Nokogiri::HTML.parse(URI.open('/tmp/test.html'))

# 芪タグずしお `body(id="test_id") 盎䞋に div 盎䞋に ul 盎䞋に li` を持぀ `aタグ` を取埗
# ちなみに、`目例` 限定せずに `配例` にあれば良い堎合は、`>` の代わりに ` ` を指定する
#   html.css('body[id="test_id"] a').each do |a|
html.css('body[id="test_id"] > div > ul > li > a').each do |a|
  p a.text.strip
  p a[:href]
end

=>

"Ruby"
"link_ruby"
"Python"
"link_python"
"PHP"
"link_php"
"Perl"
"link_perl"

他にも色々できお、䟋えば、察象ドキュメントのテキスト内容を党おくっ付けお返しおくれる

html = Nokogiri::HTML.parse(open('/tmp/test.html'))

p html.text

=>

"LightweightlanguageLightweightlanguageRubyPythonPHPPerl"

利甚方法の倧枠

ツリヌを構築/怜玢しお、ノヌドを参照する

  • ツリヌの構築
    • HTMLやXMLのドキュメントを解析しお Nokogiri::HTML::Document に倉換
  • ツリヌの怜玢
    • 怜玢系メ゜ッドを利甚しお探玢を行い、目圓おのノヌドを特定する
  • ノヌドの参照
    • 取埗できた Nokogiri::XML::NodeSet たたは Nokogiri::XML::Element から目圓おのデヌタを抜出する

ツリヌの構築

解析の皮類

DOMの他にもSAXやReaderやPullがあるらしいけど、有名どころのDOMに぀いお説明する

DOM 解析

Nokogiri では HTML/XML ドキュメントのどちらも解析できる

doc = Nokogiri::HTML.parse(html_document) # HTML ドキュメントの解析
doc = Nokogiri::XML.parse(xml_document) # XML ドキュメントの解析
  • 第1匕数には、IO オブゞェクトたたは文字列オブゞェクトを指定する
    • open_uri を盎接 Nokogiri に枡すこずも可胜文字列を䜿甚するより若干効率は䞊がるらしい
  • 第3匕数には、察象ペヌゞの文字コヌドを指定する
    • 解析察象の文字コヌドが UTF8 以倖の堎合、倧抵解析が倱敗するので指定する
  • 第2匕数はURL、第4匕数はオプション、どちらも倧抵は指定せず事足りる

オブゞェクトの可芖化

html = Nokogiri::HTML(open('/tmp/test.html'))

メモリ䞊ではどのようなオブゞェクトが䜜られおいるのか
簡略化するずこんなツリヌ状のオブゞェクトが䜜られおいる

noko1.png

各オブゞェクトに぀いお

  • Nokogiri によっお、耇数のオブゞェクトが䜜成されおいる
    • Nokogiri::XML::Document ずか Nokogiri::XML::Element ずか
  • 党おのオブゞェクトがノヌドである
    • Nokogiri::XML::Node を継承しおいる
      • Nogogiri::XML::NodeSet < Enumerable
      • Nokogiri::HTML::Document < Nokogiri::XML::Document < Nokogiri::XML::Node
      • Nokogiri::XML::Element < Nokogiri::XML::Node
      • Nokogiri::XML::Text < Nokogiri::XML::CharacterData < Nokogiri::XML::Node
      • etc...
    • 怜玢系のメ゜ッドは Nokogiri::XML::Node にたずたっおいるため、同じ怜玢メ゜ッドがこれらオブゞェクトに察しお䜿える

他にも色々あるけど、䜜られおいるオブゞェクトの説明を簡単に

  • Nokogiri::XML::Node
    • ノヌドに察する操䜜や怜玢凊理を芏定する
    • 具䜓的には、 Nokogiri::XML::Searchable をincludeしお各皮メ゜ッドを実装
      • SearchableはDOM怜玢のむンタヌフェヌス
  • Nokogiri::XML::NodeSet
    • Nokogiri::XML::Node オブゞェクトのリストを持぀
    • Nokogiri::XML::Searchablecss/xpathの実行結果
  • Nokogiri::HTML::Document
    • Nokogiriによっお解析されたHTMLドキュメント
      • Nokogiri::HTML.parseの戻り倀
  • Nokogiri::XML::DTD
    • 察象ドキュメントが DTD による文曞構造に埓っおいるかどうかを怜蚌しおいる
  • Nokogiri::XML::Element
    • Nokogiriで HTML 芁玠を扱うためのオブゞェクト
      • C 拡匵なのできっず早い
  • Nokogiri::XML::Text
    • Nokogiri で HTML テキストを扱うためのオブゞェクト

ツリヌの怜玢

怜玢方法

倧たかに 3 皮類

  • XPath
    • XML圢匏の文曞から特定の郚分を指定しお抜出するための簡朔な構文
  • CSS
    • りェブペヌゞのスタむルを指定するための蚀語
  • その他
    • child ずか parentずか盞察的に䜍眮を特定する

よくある流れ

  • Nokogiri::HTML::Document オブゞェクトに察しお、CSSセレクタやXPathで怜玢を行い、怜玢結果ずしお Nokogiri::XML::NodeSet オブゞェクトを取埗
  • NodeSet は Node のリストは配列のように扱えるため、each や [] で該圓ノヌドである Element を特定する
    • CSS や Xpath は NodeSet を返すが、at や child など Element を返すものもあるので芁確認

怜玢メ゜ッド

よく䜿うであろう Nokogiri::XML::Node のメ゜ッドに぀いお
Nodeset ず Element 䞡方に察しお䜿える

XPath

芪タグずしお body(id="test_id"), div, ul を持぀ li 党おを怜玢

html.xpath('//body[@id="test_id"]/div/ul/li').each do |li|
  p li.text
  p li.at('a')[:href]
end
  • 匕数に぀いお
    • 冒頭の / が階局構造のルヌトを衚す
    • その他の / で各タグの階局情報を指定
    • / の間に䜕もない堎合は、配䞋にあるこずを瀺す
    • div や ul はタグ名の指定
      • * は、任意のタグに䞀臎するワむルドカヌドずしお䜿える
    • 属性䟋えばidの倀ずかを指定するずきは タグ名[@属性名=xxx] を䜿う
    • 匕数は耇数指定できる
  • 戻り倀に぀いお
    • NodeSet を返す
    • 結果が無ければ空の NodeSet
  • その他

CSS

䞊蚘を CSS で眮き換える

html.css('body[id="test_id"] > div > ul > li').each do |li|
  p li.text
  p li.at('a')[:href]
end
  • 匕数に぀いお
    • / の代わりに > たたは空癜を䜿う
    • > は芪タグ盎䞋のタグを指定したいずきに䜿う
      • xpathで蚀う //h3/a
        • h3 盎䞋に a が来る
    • 空癜はタグ間に任意のタグを蚱容したいずきに䜿う
      • xpathで蚀う //h3//a
        • h3 ず a 間に任意のタグを蚱容する
    • 属性䟋えばidの倀ずかを指定するずきは タグ名[属性名=xxx] を䜿う
    • class 属性の包含を指定するずきは タグ名.class名 を䜿う
      • タグ名[属性名=xxx] は完党䞀臎する必芁がある
    • テキストから怜玢したいずきは タグ名:contains("テキスト") を䜿う
      • html.at('a:contains("Ruby")')
    • 匕数は耇数指定できる
  • 戻り倀に぀いお
    • NodeSet を返す
    • 結果が無ければ空の NodeSet

補足

  • 䟿利メ゜ッド
    • search("怜玢")
      • 匕数に XPath たたは CSS を指定できる
      • 戻り倀ずしお、NodeSetを返す、無ければ空の NodeSet
    • at("怜玢")
      • 匕数に XPath たたは CSS を指定できる
      • 戻り倀ずしお、最初のノヌドの Element を返す、無ければ nil
  • ChromeやFirefoxFirebugで指定タグのパスが抜出できる
    • Chrome の堎合
      • バヌゞョン: 106.0.5249.103Official Build x86_64
      • 開発者コン゜ヌルを開いお、Elements タブで該圓芁玠を遞択する
      • 右クリックしお、Copy > Copy Selector / Copy XPath でコピペ

その他

ノヌドを指定しお任意の盞察ノヌドにアクセスできる

  • child
    • 最初の子ノヌドが Element で返る
  • children
    • 子ノヌドElementの配列が返る
  • previous_sibling、previous
    • 兄ノヌドが Element で返る
      • 手前にあるノヌド
  • next_sibling、next
    • 匟ノヌドが Element で返る
      • 埌にあるノヌド
  • parent
    • 芪ノヌドが Element で返る
  • ancestors
    • 祖先ノヌドElementの配列が返る

ノヌド情報の参照

ノヌドに関する情報を取埗するためのメ゜ッド
基本的に Nodeset ず Element 䞡方に察しお䜿えるが䞀郚䟋倖あり

ドキュメントの参照

Nokogiri::XML::Node ず NodeSet で基本的には同じ API が䜿えるが、䞀郚のメ゜ッドcontent、to_strがない
NodeSet の堎合、リスト内の党おのノヌドに察しおメ゜ッドを適甚した結果を返しおくれる

  • to_s
    • ノヌド党䜓のテキストを぀なぎ合わせた文字列が返る
  • content、text、inner_text、to_str
    • 子孫ノヌドのテキスト内容を぀なぎ合わせた文字列が返る
    • ゚むリアス倚
  • to_html、 to_s
    • ノヌド党䜓HTMLを぀なぎ合わせた文字列が返る
      • 本人ノヌドも含めた inner_html
  • to_xhtml、 to_xml
    • ノヌド党䜓を XHTML を぀なぎ合わせた文字列が返る
  • inner_html
    • 子孫ノヌドの HTML を぀なぎ合わせた文字列が返る

属性情報の参照

通垞の Ruby ハッシュのように扱える
Element に察しお䜿う

  • ["属性名"]、get_attribute("属性名")
    • 属性倀を文字列で返す、無ければ nil
  • key?("属性名")、has_attribute?("属性名")
    • 属性の有無をtrue/falseで返す
  • keys
    • 属性名を文字列の配列で返す
  • values
    • 属性倀を文字列の配列で返す
  • attributes
    • 属性名ず属性オブゞェクトのハッシュを返す
  • attribute("属性名")
    • 属性オブゞェクトを返す
  • each { |k,v| }
    • 属性名ず属性倀を返しブロック呌び出し

たずめ

  • ツリヌを探玢しおいるむメヌゞを持぀ずわかりやすい
    • 今自分が操䜜しおいるのが NodeSet なのか Element なのか知っおいた方が混乱なさそう
      • CSS や Xpath は NodeSet を返すが、at や child など Element を返す
  • 倧抵のケヌスでは最初のサンプルで事足りそう
    • 取埗郚分の现かい指定が必芁な堎合には children 等を䜿う
      • CSS で怜玢しお NodeSet を取っおきお、それを each で回しお各芁玠の情報を取埗する
    • テキストから怜玢するのは結構䜿いそう
      • CSSで曞くず html.at('a:contains("Ruby")') など
2
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
2
1