実践スクレイピング
       不条理な現実との折り合いの付け方




ふるかわだいすけ/mogya.com
ふるかわだいすけ

フリーランスWEBエンジニア


            http://mogya.com/
            http://mogya com/
            Twitter: @mogya



     自己紹介
ふるかわだいすけ

フリーランスWEBエンジニア



            http://maidmail.jp/


     自己紹介
http://oasis.mogya.com/


モバイラーズオアシス
http://oasis.mogya.com/


携帯/スマートフォン対応
実践スクレイピング



  今日のテーマ
はじめてのスクレイピング
・Hpricot
 X th
・Xpath
・店名を取ってみる
実践スクレイピング
・1ページに全部入っている場合
 id
・id=xxx方式
        方式
・一覧ページ→詳細ページ
・「次へ」対応
 「次へ 対応
現実のスクレイピング
まとめ


          目次
photo by Big Ben(Gaijin Bikers)




はじめてのスクレイピング
例えばこんなページ
    http://www.mcdonalds.co.jp/shop/map/map.php?strcode=01570
こうなっているところから
      http://www.mcdonalds.co.jp/shop/map/map.php?strcode=01570
require 'rubygems'
require 'hpricot'
require 'open-uri'
url =
'http://www mcdonalds map php?strcode=01570'
 http://www.mcdonalds…map.php?strcode=01570
page = Hpricot( open(url).read );nil
page sea c ( S op a e 3 )
page.search(".Shopname h3").inner_te t
                                 e text




            店名を取ってみる
Hpricot
  • HTMLパーサー
  • xpathでHTMLから切り出しができる




               Hpricot
page = Hpricot( open(url).read )
page.search(".Shopname h3").inner_text



                           “body div h1”




                           “div#Content1
                           “di #C t t1 h1”




                                 “.content title”
                                  .content_title
                                  (page/".content_title")[0].inner_text



        XPath(XML Path Language)
/html/body/table/tbody/tr[3]/td/
              勝手にtbodyとか補足する点に注意


Firebugを使うとラクチン
        http://www.mcdonalds.co.jp/shop/map/map.php?strcode=01570
require 'rubygems'
require 'hpricot'
require 'open-uri'
         open-uri
url =
'http://www.mcdonalds・・・map.php?strcode=01570'
page = Hp icot( open( l) ead ) nil
        Hpricot( open(url).read );nil
page.search(".Shopname h3").inner_text




               店名を取ってみる
page = Hpricot( open(url).read )
↓
page = Hpricot( open(url).read );nil

                     irbの場合
userAgent =
  "mogya scraper under construction. ”+
  ”you can contact me at mogya+scraper@mogya.com"
page = Hpricot( open(_url, "User-Agent" => userAgent ).read )




          連絡がつくようにしておく
Require “kconv”
userAgent =
  "mogya scraper under construction. ”+
  ”you can contact me at mogya+scraper@mogya.com"
page = Hpricot( open(url, "User-Agent" => userAgent ).read.toutf8 )



                      ※読みにくいので、以降のサンプルでは省略しました




                  文字コードの問題
• Aj でデ タを動的ロ ド
  Ajaxでデータを動的ロード
   →スクレイピングだとデータが取れない
• JSの読み出し元を見ればいいんじゃね?
•ブラウザでアクセスしてみる
   →403 forbidden
•以降、サイトまるごと403 forbiddenになった
   →「本気だ!」




                         ※このデザインになる前の話


         余談:防衛反応
                  http://www.mcdonalds.co.jp/shop/map/map.php?strcode=01570
photo by Big Ben(Gaijin Bikers)




実践スクレイピング
http://www.burgerkingjapan.co.jp/locations/




1ページに全部入っている場合
                                                 http://www.first-kitchen.co.jp/shop/
バーガーキング
      http://www.burgerkingjapan.co.jp/locations/
require 'rubygems'
     i ' b         '
require 'hpricot'
require 'open-uri'
url = 'htt //
  l 'http://www.burgerkingjapan.co.jp/locations/'
                    b   ki j         j /l ti   /'
page = Hpricot( open(url).read );nil

page.search( li dl ).each{|part|
page search(".li dl") each{|part|
   p part.search("dd h4 a").inner_text
}




                     バーガーキング
                                              http://www.burgerkingjapan.co.jp/locations/
id=xxxx方式
     http://www.mcdonalds.co.jp/shop/map/map.php?strcode=01578
require 'rubygems'
require 'hp icot'
 eq i e 'hpricot'
require 'open-uriʻ
url_template
url template =
     'http://www.mcdonalds ・・・ strcode=%05i'
0.upto(10000){|i|
       url = sprintf(url_template,i)
       page = Hpricot( open(url).read );nil
       p page search(" Shopname h3") inner text
         page.search(".Shopname h3").inner_text
       sleep 3
}



                 id=xxxx方式
                          http://www.mcdonalds.co.jp/shop/map/map.php?strcode=01578
新店舗情報を見ると idのmax値がわかることが多い
新店舗情報を見ると、idのmax値がわかることが多い



  id=xxxx方式
         http://www.mcdonalds.co.jp/shop/map/map.php?strcode=01578
飛び飛びで見て最低値を推測

0.step(10000,100){|i|
        url = sprintf(url template i)
              sprintf(url_template,i)
        page = Hpricot( open(url).read );nil
        p "#{url}:"+page.search(".Shopname h3").inner_text
        sleep 3
}




                        id=xxxx方式
飛び飛びで見て最低値を推測

0.step(10000,100){|i|
        url = sprintf(url template i)
              sprintf(url_template,i)
        page = Hpricot( open(url).read );nil
        p "#{url}:"+page.search(".Shopname h3").inner_text
        sleep 3
}




                        id=xxxx方式
一覧ページ→詳細ページ
        http://www.bagus-99.com/company/shop.html
一覧ページ→詳細ページ
        http://www.bagus-99.com/company/shop.html
require 'rubygems'
  q         yg
require 'hpricot'
require 'open-uri'
archive_url = 'http://www.bagus-99.com/netcafe/store/'
archive_page = Hpricot( open(archive_url).read );nil
archive_page.search("td.darker2/a").each{|url_part|
      url = archive_url + url_part['href']
      page = Hpricot( open(url).read ) il
                   i (      ( l)     d );nil
         以下略
      sleep 1
}




             一覧ページ→詳細ページ
                                                http://www.bagus-99.com/company/shop.html
「次へ」対応
         http://www.mapion.co.jp・・・search=mapAreabtn
「次ページへ」がある時




「次ページへ」がない時
 次ペ ジへ」がない時




XPATHではムリ!




              「次へ」対応
                       http://www.mapion.co.jp・・・search=mapAreabtn
def parse(url)
    p     ( )
        page = Hpricot( open(url).read )
        page.search(".address").each{|part|
           p part.search("h2").inner_text
        }
        next_url =
           (page.inner_html.scan(
             /HREF="(.*)"><strong>次ページへ/
             /        "( *)"            次    ジ /
           )[0][0]) rescue nil
        if (next_url)
           parse("http://www.mapion.co.jp"+next_url)
                 ("htt //            i     j "+  t l)
        end
end

url = “http://www.mapion.co.jp/c/f?・・・admi3=13101・・・"
parse(url)




                      「次へ」対応
                                              http://www.mapion.co.jp・・・search=mapAreabtn
photo by Big Ben(Gaijin Bikers)




現実のスクレイピング
現実1:データ大杉
       http://www.doutor.co.jp/shopsearch/index.html#
→ 47回回せばOK?

現実1:データ大杉
            http://www.mapion.co.jp・・・BT=all
現実1:データ大杉
            http://www.mapion.co.jp・・・BT=all
URLをじーっと眺めると。
1.東京都の各エリア
http://www.mapion.co.jp/c/f?・・・&admi2code=13&BT=all&admi3=13101&search.x=15&search.y=8
http://www mapion co jp/c/f?   &admi2code 13&BT all&admi3 13101&search x 15&search y 8
http://www.mapion.co.jp/c/f?・・・&admi2code=13&BT=all&admi3=13102&search.x=25&search.y=5
:
2.それ以外の県
  . れ以外 県
http://www.mapion.co.jp/c/f?・・・&admi2code=40&mx=100&vp=10&BT=all
http://www.mapion.co.jp/c/f?・・・&admi2code=41&mx=100&vp=10&BT=all



→2パターンに分けてスクレイピング
      に分        グ




                       対処1:地道に対応
Flashの検索フォーム
                                    検索




現実2:強敵Flash
      http://sp.chizumaru.com/dbh/lotteria/top.aspx?account=lotteria
Flashの検索フォーム
                                    検索




現実2:強敵Flash
      http://sp.chizumaru.com/dbh/lotteria/top.aspx?account=lotteria
現実2:強敵Flash
              http://sp.chizumaru.com/・・・&arg=
http://sp.chizumaru.com/dbh/lotteria/detailmap.aspx?accou
nt=lotteria&accmd=0&arg=&c1=&c2=&c3=&c4=&c5=&c6=
&c7 &c8 &c9 &c10 &c11 &c12 &c13 &c14 &c15 &c16
&c7=&c8=&c9=&c10=&c11=&c12=&c13=&c14=&c15=&c16
=&c17=&c18=&c19=&c20=&c21=&c22=&c23=&c24=&c25
=&c26=&c27=&c28=&c29=&c30=&mode=11&key=&pg=1&
                    ad &b d
                    adr=&bid=113106
                                3 06

                                                 6桁とかw


      現実2:強敵Flash
                      http://sp.chizumaru.com/dbh/lotteria/detailmap.aspx・・・bid=113106
http://sp.chizumaru.com/dbh/lotteria/detailmap.aspx?accoun
                  t=lotteria&bid=113106




      現実2:強敵Flash
鉄壁!
・ページリスト:取れない(Flashだから)
・一覧ページのURL:取れない
      (座標指定なのでパターンを読めない)
・個別ページ:id多すぎ




         現実2:強敵Flash
対応:偶然発見!
http://www.pronto.co.jp/solare/
          注)BGMが鳴ります




現実3:強敵Flash2
数が少ないので手作業で片付けましたw




     対策3:あきらめる
余談:渡る世間に鬼はなし
               http://www.manboo.co.jp/
余談:渡る世間に鬼はなし
               http://www.manboo.co.jp/
現実4:Tableレイアウト
現実4:Tableレイアウト
XPATHがこんなふうになる
 page.search("/html/body/table/tr[3]/td/table/tr/td/table/tr/td[2]/tab
 le/tr[3]/td/table/tr[3]/td/table/tr/td/table/tr/td/a )
 le/tr[3]/td/table/tr[3]/td/table/tr/td/table/tr/td/a”)

ひどい場合、そもそもXPATHで表現できないことも


対応
 ・1カ所くらいclassがついていることもある
 ・FireBugで何とかなる間はなんとかする
 ・どうにもならない時は、正規表現で取得




           現実4:Tableレイアウト
現実5:InvalidなHTML
現実5:InvalidなHTML
問題:xpathで読めなくなってしまう
対策:読み込んだタイミングで直してしまう

page =
  Hpricot(
  H i t(
    open(url).read.gsub(
        "</script>¥n<body>","</script>¥n“
         </script>¥n<body> </script>¥n
    )
  );nil




         対策:アドホックに直す
th便利
・xpath便利
・スクレイピングは個別対応の嵐
 スクレイピングは個別対応の嵐
・validなHTMLを書きましょう




         まとめ
おしまい
おしまいだってば!
もうないよ!
時間があまった?
じゃあしょうがない
About
モバイラーズオアシス
モバイラ ズオアシス


    おまけ
「情報はどこから?」
 情報はどこから?」




  よくいただく質問
こたえ
ブログ
・・・正直ムリ(><)
twitter
• t itt で「電源 OR コンセント -切」をwatch
  twitterで「電源    ンセント 切 を t h
• 99%はどうでもいい話だけど、
    高速で読み飛ばせるので対応可能
• 敵はbotと診断メーカー




               twitter
• t itt で「電源 OR コンセント -切」をwatch
  twitterで「電源    ンセント 切 を t h
• 99%はどうでもいい話だけど、
    高速で読み飛ばせるので対応可能
• 効能
   •電源情報収集
   •宣伝
   •SEO
    顧客の声を聞く
   •顧客の声を聞く




               twitter
on ExpressionEngine




   バックエンド
管理をCMSに投げる
システム構成
• Ce tOS5 3
  CentOS5.3
• Apache2.2.3
• E pressionEngine v.1.6.4
  ExpressionEngine 1 6 4
• MySQL 5.0
• jQuery 1.3.2
         132
• GoogleMapsAPI




               システム構成
システム構成
                        ee_Net_UserAgent_Mobile
                        ee Net UserAgent Mobile
通常のリクエスト
通常 リク  ト
           Expression   spot_search
結果ページの       Engine
リクエスト



                            DB
検索クエリ      Search.php




           システム構成

実践スクレイピング