LoginSignup
30
27

More than 5 years have passed since last update.

Nokogiriでぎこぎこスクレイピングしてつまずいたときにみるメモ

Last updated at Posted at 2018-03-10

しょしんしゃえんじにあです。
サイトを数件スクレイピングしたときにつまずいた点と、その対処方法の個人的メモです。

はじめに

Webスクレイピングの注意事項一覧

大切だなとおもったこと

「あなたの内面がどんなに汚くても、私は絶対にあなたを受け止める」
スクレイピングするサイトへの愛かなと思いました。

つかったもの

  1. ruby
  2. nokogiri
  3. Chromeデベロッパーツール

クローリングしたパターン

トップページのカテゴリ一覧 => カテゴリ一覧ページ => 詳細ページ

つまずいた点&対処方法

そもそも要素をどう取得するか

NokogiriではXPathやCSSセレクタで要素を取得できる。
WEBエンジニア的に馴染みがあるCSSセレクタを選んだ。

# CSSセレクタはこんなかんじ
doc.css('#main > article> h1')
# XPathはこんなかんじ
doc.xpath('//*[@id="main"]/article/h1')

HTML構造が複雑で要素を指定できない

実際にサイトの要素を取得しようとすると結構長くなる。

#main > article > div.p-items_wrapper > div > div.p-items_main > div.p-items_article > div.it-Header > h1

目視では到底追えないので、「Chromeデベロッパーツール」にCSSセレクタを自動生成してもらった。

こちらの記事がとてもわかりやすい!
Python Webスクレイピング テクニック集「取得できない値は無い」JavaScript対応@追記あり2/28

ChromeデペロッパーツールのCSSセレクタの注意点

tbodyには気をつける

例えばtable > tbody > tr > tdと自動生成された場合、そのまま素直に利用すると、サイトによって取得が失敗したりする。
原因としては単純で、実サイトではtable > tr > tdとコーディングをしているから取得できない、というケース。ページのソースを表示すればすぐわかる。
Chromeさんがなんか勝手にtbodyを自動補完してくれるみたい。きちょうめんだ。

とりあえずなぜかとれないときは

  • 「>」(直下セレクタ)を取っ払ったら、いいことあった。
  • 自動生成のCSSセレクタを無視してサイトへの愛によるCSSセレクタをひねりだす
    • :nth-child
    • + (隣接セレクタ)
    • [foo=bar](属性セレクタ)

このあたりを結構活用した。

同サイト内で詳細ページのHTML構造が微妙にばらばら

ページによってあったりなかったりする要素もあるのでぼっち演算子とかで、

doc.css('img')&.attribute("src")

などとやりそうになるが、undefined method `attribute' for nil:NilClassでコケる。
css、xpathメソッドは、該当する要素がない場合、nilではなく空の配列(NodeSet)が返ってきているから。

HTMLが文字化けする

kconvつかう。toutf8つける。

require 'open-uri'
require 'nokogiri'
require 'kconv'

url = "http://example.com"
html = URI.parse(url).read
doc = Nokogiri::HTML(html.toutf8, nil, 'utf-8')

URLに日本語が含まれている

URI.encodeつかう。

html = URI.parse(URI.encode(url)).read

なんかこういうケースもあった

url = doc.css('a#hoge').attribute('href')
next_html = URI.parse(URI.encode(url)).read

undefined method `gsub' for #<Nokogiri::XML::Attr:...が出た。
to_sしたらうまくいった。

url = doc.css('a#hoge').attribute('href').to_s
next_html = URI.parse(URI.encode(url)).read

ページネーションをどうくぐりぬけるか

こうした

カテゴリ一覧ページのリンク達.each do |カテゴリ|
  カテゴリAの一覧ページをノコギる
  begin
    カテゴリAの詳細ページのリンク達.each do |s|
      詳細ページをノコギる
    end
    # ↓↓次のページがあるかどうか判定↓↓
    next_link = document.css('#pager > ul > li > a.next')
    unless next_link.empty?
      カテゴリAの一覧の次のページをノコギる
    end
  end until next_link.empty?
end

"【タイトル】</br>内容…"みたいな構成のときに「タイトル」と「内容」を取りたい

正規表現でごにょごにょしてなんとかした。下記のさらにもっと汚い版

irb(main):081:0> "【あああ】<br>いいい".split(/【((?!【).*?)】/)
=> ["", "あああ", "<br>いいい"]

要素を取得したときに値にブランクやタブやbrタグがはいる

ひたすらえすけーぷえすけーぷえすけーぷえすけーぷ

どこまで進行したかわからない

ログなりなんなり出そう(結構大事)

さいごに

スクレイピングして、うまくいったとおもったら、つまずいて、サイトのソース読んでを繰り返すとサイト愛が芽生えてきてしまいました。何言ってるんだろう私。
かなり作業の泥臭さを感じたので、もう少しスマートにできる方法を模索したいです。次の機会があれば…。

30
27
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
30
27