LoginSignup
1

posted at

updated at

Nokogiri の䜿い方 🪚

前提

  • 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")') など

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
What you can do with signing up
1