おぴよの気まぐれ日記

おぴよの気まぐれ日記

岡山やプログラミング、ファッションのこと、子育てや人生、生き方についての備忘録。

【Ruby】Nokogiriを使って「はてなブログ」をスクレイピングする!

こんばんは。エンジニアになれるか不安なopiyoです。

今日はWebサイトの情報を取得することが出来る、Webスクレイピングという技術をRubyでやってみます!

Webスクレイピングとは

Webサイトの情報を取得する技術ってイメージですが、wikipediaでちゃんと調べてみました。

ウェブスクレイピング(英: Web scraping)とは、ウェブサイトから情報を抽出するコンピュータソフトウェア技術のこと。ウェブ・クローラー[1]あるいはウェブ・スパイダー[2]とも呼ばれる。 通常このようなソフトウェアプログラムは低レベルのHTTPを実装することで、もしくはウェブブラウザを埋め込むことによって、WWWのコンテンツを取得する。

nokogiriのインストール

Gemfilenokogiriを記述します。

# Gemfile
gem 'nokogiri'

ターミナルで、いつも通りbundle installします

$ bundle install

スクレイピング実行するプログラム

class Scrape
  url = 'http://b.hatena.ne.jp/ctop/it'

  charset = nil

  opt = {}
  opt['User-Agent'] = "I Love Ruby"

  html = open(url, opt) do |f|
    charset = f.charset
    f.read
  end

  doc = Nokogiri::HTML.parse(html, nil, charset)
  doc.css('h1').each do |node|
    p node.text
    p node["href"]
  end
end

はまりポイント

503エラーが発生する事があるのだが、原因はUser-Agentが定義されていない。

$ rails runner lib/scrape.rb
Running via Spring preloader in process 14182
/Users/taku/.rbenv/versions/2.4.1/lib/ruby/2.4.0/open-uri.rb:363:in `open_http': 503 Service Temporarily Unavailable (OpenURI::HTTPError)

User-Agentってのは、そのアクセスした端末が何を使っているのかが定義されていてアプリ側では、この設定を見て「こいつはIEだ」とか「こいつはスマホだ」とか判断したりします。 例えばこんな感じ

  • Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36
    • Mac OS X
    • Chrome

で、サイトによってはこのUser-Agentが定義されていないとエラーになってしまうらしいので、一緒に渡してやる。

スクレイピングしてみる

ここからが本題ですね。

データを取得する際に、どの情報を取得するのか定義してやります。

上のサンプルを例に幾つかやってみます。取得先は、はてなブックマークのテクノロジー一覧ページです。

はてなブログから「h1」に定義されているテキストを取得

  doc.css('h1').each do |node|
    p node
    p node.text
  end
$ rails runner lib/scrape.rb
#<Nokogiri::XML::Element:0x3fc3eed74b04 name="h1" children=[#<Nokogiri::XML::Element:0x3fc3eed74820 name="a" attributes=[#<Nokogiri::XML::Attr:0x3fc3eed747bc name="href" value="/">] children=[#<Nokogiri::XML::Element:0x3fc3eed74244 name="span" children=[#<Nokogiri::XML::Text:0x3fc3eed74064 "はてなブックマーク">]>]>]>
"はてなブックマーク"

はてなブログの各記事「h3」に定義されているテキストを取得(記事の一覧)

  doc.css('h3.hb-entry-link-container').each do |node|
    p node.text
  end
$ rails runner lib/scrape.rb
"コミュ力もリーダーシップもいらない。Googleが考える、本当に“優秀な人材“とは"
"Googleの画像認識APIを基に、好きな画像を学習させて認識機能を簡単にカスタ..."
"質問箱が暴露「4万人以上が自作自演」 ⇒ 反発を受けて実装した新機能とは?"
"最新版!Webページを作成する時のベースになる、最小限の構成で記述されたHT..."
"必見? Mozillaが提供しているユーザー環境週報がなにげに興味深い件につい..."
"Google ウェブマスター向け公式ブログ: ページの読み込み速度をモバイル検索..."
"はてなのアイコン設定したい"
"2月末に株式会社groovesを退職します - アジャイルSEの憂鬱"
"ネットでの誹謗中傷!書き込んだ人物を特定するための手順"
"\n      \n        Androidアーキテクチャことはじめ ― 選定する意味と、MVP、Clean Architecture、MVVM、Fluxの特徴を理解する - エンジニアHub|若手Webエンジニアのキャリアを考える!\n      \n    "
"\n      \n        フロントエンド開発に Babel も Webpack も必要ない ※ - KAYAC engineers' blog\n      \n    "
"\n      \n        Google ウェブマスター向け公式ブログ: ページの読み込み速度をモバイル検索のランキング要素に使用します\n      \n    "
"\n      \n        確率密度比推定まわりの書籍・解説記事・論文・ソフトウェアの各種情報まとめ - 備忘録\n      \n    "
"\n      \n        コード整形をStandardJSからPrettierに乗り換えたら捗った - 丁寧に手を抜く\n      \n    "
"\n      \n        大学を辞めたけど何も起こらなかった - 素人がプログラミングを勉強していたブログ\n      \n    "
"\n      \n        「ITをITで支援する」MSP事業者が、変革する開発・運用で果たす役割──スキルの価値が10年で消える世界においてエンジニアはどうあるべきか - GeekOut\n      \n    "
"\n      \n        プログラミングとUIデザインの境界、およびデザインの環境設定について – timakin – Medium\n      \n    "
"\n      \n        Golangでtestingことはじめ(1) - DeNA Testing Blog\n      \n    "
"\n      \n        ECS コンテナインスタンスをモニタリングするときは mackerel-agent v0.49.0 以上にすると良さそう - kakakakakku blog\n      \n    "
"\n      \n        IntelliJ IDEAで特定行のGitHub PRを開く\"Find Pull Request\" pluginがまじイノベーティブ - Islands in the byte stream\n      \n    "
"\n      \n        Perl で Compiler::CodeGenerator::LLVM を用いて LLVM IR を出力する - アルパカ三銃士\n      \n    "
"\n      \n        CodeStar で AWS Lambda + Golang の雛形をサクッと作成してみた - ソモサン\n      \n    "
"\n      \n        Kubernetes を利用したコンテナベース機械学習基盤の構築 - LIVESENSE Data Analytics Blog\n      \n    "
"\n      \n        Serverless Frameworkで静的サイトをBASIC認証付きでホスティングするためのボイラープレートを作った - Copy/Cut/Paste/Hatena\n      \n    "
"はてな、500 Startups Japanを通じたスタートアップ支援。サーバー監視サー..."
"Google、プログラミングができなくてもAIツールを作れる「AutoML」のα版提供..."
"「ITパスポート試験」83歳が合格 最年長記録更新 - ITmedia NEWS"
"ドミノ・ピザ、「20分保証」の宅配サービス (ITmedia ビジネスオンライン)..."
"AWS最先進ユーザーNetflix - 「サル軍団」にシステム障害を起こさせる、Netflixの驚異的なトラブル撲滅..."
"CPUの脆弱性対策パッチでSSDのランダムアクセスが大幅減速?影響をチェックしてみた - AKIBA PC Hotline!"
"word2vec(Skip-Gram Model)の仕組みを恐らく日本一簡潔にまとめてみたつもり - これで無理なら諦めて..."
"Microsoft 、VRを用いて実在する建物内で起こる災害を実際のスケールで走り..."
"Nintendo Switchを使ってロボットや楽器コントローラーなど様々なものを作れ..."
"任天堂、Switchと合体する“段ボールコントローラー”「Nintendo Labo」発売..."
"首元にのせるだけで周囲360度のパノラマムービー撮影が可能なウェアラブルカ..."
"15.5型ノート「VAIO S15」に追加モデル 店頭向けエントリー構成、新色のピ..."
"第8世代Coreと13.3型IGZO液晶を備えたスリムノート「Razer Blade Stealth」..."
"日本参入か? 中国OPPOが日本語Twitterアカウントを開設 - ITmedia Mobile"
"iPhoneでも使えるOfficeの手書き プレゼンにも便利 - 日経トレンディネット"

はてなブログの各記事「h3」に定義されているリンクURL

  doc.css('h3.hb-entry-link-container > a').each do |node|
    p node["href"]
  end
$ rails runner lib/scrape.rb
"http://www.huffingtonpost.jp/2018/01/16/piotr_a_23334437/"
"http://www.publickey1.jp/blog/18/googleapicloud_automl_vision.html"
"http://www.huffingtonpost.jp/2018/01/17/peing_a_23335512/"
"http://coliss.com/articles/build-websites/operation/work/html5-template-for-2018.html"
"https://forest.watch.impress.co.jp/docs/serial/yajiuma/1101696.html"
"https://webmaster-ja.googleblog.com/2018/01/using-page-speed-in-mobile-search.html"
"https://anond.hatelabo.jp/20180118015611"
"http://sinsoku.hatenablog.com/entry/2018/01/18/144408"
"https://sakuya-shougainenkin.com/slander"
"https://employment.en-japan.com/engineerhub/entry/2018/01/17/110000"
"http://techblog.kayac.com/pure-js-app"
"https://webmaster-ja.googleblog.com/2018/01/using-page-speed-in-mobile-search.html"
"http://tam5917.hatenablog.com/entry/2018/01/16/233057"
"http://craftzdog.hateblo.jp/entry/prettier-is-good"
"http://javascripter.hatenablog.com/entry/2018/01/18/033738"
"https://geek-out.jp/column/entry/2018/01/18/110000"
"https://medium.com/@timakin/3ee839874d19"
"http://swet.dena.com/entry/2018/01/16/211035"
"http://kakakakakku.hatenablog.com/entry/2018/01/17/124717"
"http://gfx.hatenablog.com/entry/2018/01/17/133457"
"http://codehex.hateblo.jp/entry/2018/01/16/221634"
"http://rohki.hatenablog.com/entry/2018/01/16/211941"
"http://analytics.livesense.co.jp/entry/2018/01/18/090000"
"http://k1low.hatenablog.com/entry/2018/01/18/085843"
"http://hatenacorp.jp/press/release/entry/2018/01/18/153000"
"http://www.itmedia.co.jp/news/articles/1801/18/news096.html"
"http://www.itmedia.co.jp/news/articles/1801/18/news090.html"
"https://headlines.yahoo.co.jp/hl?a=20180118-00000066-zdn_mkt-bus_all"
"http://itpro.nikkeibp.co.jp/atcl/column/17/122800596/010500003/"
"https://akiba-pc.watch.impress.co.jp/docs/sp/1101498.html"
"http://www.randpy.tokyo/entry/word2vec_skip_gram_model"
"https://kadenkaigi.com/entry/354850025"
"https://kadenkaigi.com/entry/354832865"
"https://kadenkaigi.com/entry/354831021"
"https://kadenkaigi.com/entry/354829845"
"https://kadenkaigi.com/entry/354862221"
"https://kadenkaigi.com/entry/354859010"
"https://kadenkaigi.com/entry/354858999"
"https://kadenkaigi.com/entry/354822870"

まとめ

やばい。楽しくなってきた。

cssを定義するように書けるので非常に非常に分かりやすいし簡単ですね。

スクレイピングを使うと色々な情報を取得することが出来るので、非常に便利ですね。

が、便利な反面迷惑かけてしまうことも当然ありますので、しっかりルールを守って使っていきたいですね。

と記事を書き終えた所で、なんとスクレイピング用のgemがあるらしい。これは、また別の機会で…くそう。