先日Rubyのお勉強を始めまして、スクレイピングに挑戦しました。やったらできました!
うまく動いてとても高ぶってるのですが、プログラミングはほとんど初めてなので、いろいろとお作法から外れていそうです。
そこで、変なところや、もっと一般化して書けるところがあればお教えいただけますか?
■やったこと:
CSV形式の曲のリストを用意して、初音ミクWikiから該当する曲の歌詞をスクレイピングする。
■知りたいこと:
私の書いたコードの変なところ、一般化できるところを教えてほしい。
気づいてる範囲ではたとえば、
・今回URLは「?page=」のあとにタイトルつければ繋がることが偶然わかったのでそうしています。そういう仕組みではないサイトでも平気な方法はありますか。
・「Nokogiri」はわかんなくてあきらめてしまったけど、それを使ったほうがよかったのでしょうか。
・最後にばらばらのテキストファイルに書き出してるけど、ふつうはデータベースに入れますか??
など?
基本なにもわからないので、いろいろ教えてください! よろしくお願いします。
長くなるので、コードは別に書きます!
ruby使いじゃないのでrubyでのお決まりはちょっとわかりませんが、補足的+αで書いてみます。
・今回URLは「?page=」のあとにタイトルつければ繋がることが偶然わかったのでそうしています。そういう仕組みではないサイトでも平気な方法はありますか。
仕組みもですが、元にするデータも色々なので、その都度調べて法則を見つけたり、一覧をスクレイプしてみたりです。
スクレイピングってのが元々そういう感じです。
ただ、それだと大変なんで、ってことでプログラムで読み書きするために出来たのがWebAPIで、これがあれば楽が出来ますし、別途マニュアルが用意されていたりします。
ただWebAPIが用意されているかが、サイトによるわけで、それも含めて「このリストからあのデータを得るにはどうしよう?」と毎度調べるしか無いですね。
・「Nokogiri」はわかんなくてあきらめてしまったけど、それを使ったほうがよかったのでしょうか。
今回は無しでOKだと思います。
Nokogiriだとタグで取ってくる感じなので、たとえば作者名だとか、ニコニコのリンクだとか、リンクから作者情報も得るだとかになるとNokogiriが便利になるかもしれません。
今は正規表現を使っていますが、Nokogiriを使うとCSSのセレクタやXPathが使えます。これは、どっちが分かりやすいか、書きやすいかという話で、スクレイピングだとデータさえ取れれば使いやすい方で良いです。
・最後にばらばらのテキストファイルに書き出してるけど、ふつうはデータベースに入れますか??
用途によります。
この後、Rを使ったりということですが、そのRで使いやすい方を選べば良いです。
今後、作者名とか製作時期とか付加情報も得るならデータベースが扱いやすいかもしれませんが、テキストファイルがあるならその時にデータベース化するのも難しく無いですし。
・その他
URI.escapeはobsoleteとありますし、曲名に「+」や「?」があるとURI.escapeではダメなんじゃないかと思います。
CGI.escapeかURI.encode_www_formを使うとかで、下記のどちらかのようにするのが良いのではないでしょうか?
(ruby使いじゃないので外しているかも?)
url = "http://www5.atwiki.jp/hmiku/?page=" + CGI.escape(title) url = "http://www5.atwiki.jp/hmiku/?" + URI.encode_www_form({"page"=>title})
上記の関連で曲名に「:」「/」が有ると、ファイル名としてどうなるんでしょう?
曲名のような自由な文字列を元にするときはチェックや正規化をしたほうが安心できる気がします。
ライブラリにそういうのもあったりしませんかね?
(ruby使いじゃないのでよくわかりません)
データの取得に失敗してif文でnextした場合、sleepなしで連続して試みています。
何らかの原因で連続して失敗する場合、続けざまに行ってしまうので、最後にsleepじゃなく、openの前にsleepとかで、確実にsleepしたほうが良い気がします。
今どきはこの程度どうって事無い気もしますけど。
上記関連で、失敗した時はメッセージ出しておくと、なんかおかしいな?って時に早めに気が付きます。
(なんとなく例外使っても良い気がしますが、ruby的にはどうなのでしょう?)
あと、一時的な問題で失敗したとか、途中で例外が発生して、再度取り直すとかの場合でも、最初のリストを修正しないと全部取り直しをしてしまいます。
最初にファイルがあるかチェックして、無いときだけ取り直すようにすると、失敗した時とか、増やした時とかに対応し易いかもしれません。
さらに、別フォルダなどにhtmlそのものを保存したり、それを自動で行うようなライブラリを使う、つまりキャッシュしておくと、ちょっと取る範囲を変えてみたとか、そういうのもやりやすくなります。
(逆に失敗した時のキャッシュが残っていて……ということもありますが……)
とは言っても、自分だけが使うこういうツールは、ようはデータが取れれば良いので、使って行って気が付いたところを修正改良していけば十分なんじゃないですかね?
CSVファイルは
https://docs.google.com/spreadsheet/pub?key=0AidlTjRleM3mdHJBSjJ...
これをもとにして変換したものです。
0列目には順位が、2列目には曲名を初音ミクWikiの表記に合わせたものを正規表現で作って入れました。
CSVファイルは全部で200行ですが、初音ミクWikiにない曲もあるので、処理できたのは170曲ほどでした。
最終的にはRで読み込んで、MeCabで形態素解析して、ボカロ曲の特徴を考えたりしたいです。まだお勉強中です。
http://hacosato.hatenablog.com/
結果はこのブログに載せようと思っています。
Rubyで私が書いたのは以下のようなコードです。
require 'open-uri' require 'csv' require 'uri' CSV.foreach("(csvファイルのパス)", "r") do |row| title = row[2] url = "http://www5.atwiki.jp/hmiku/?page="+title url = URI.escape(url) charset = nil html = open(url) do |f| charset = f.charset f.read end if (/<title>初音ミク Wiki -.*は見つかりません<\/title>/ =~ html) next end /(?<=<h3 id="id_0a172479">歌詞<\/h3>)(.*?)(?=<h3)/m =~ html lyric = $1 if (lyric == nil) next end lyric.gsub!(/<div>|\n/m, '') #divタグと\nを取り除く lyric.gsub!(/<\/div>|\n/m, '<br />') #div閉じタグを<br \/>にする lyric.gsub!(/\A\s+?|\s+?\Z|(<br \/>)+\Z/m, '') #文字列の最初と最後の空白や<br />を取り除く lyric.gsub!("<br />", "\n") #brタグを改行にする lyric.gsub!(/<\/?[^>]*>/, "") #文中のhtmlタグは外す lyric.chomp! File.write("/lyric/"+row[0]+"_"+title+".txt", lyric) #それぞれテキストファイルに書き出す sleep 1 end
色をつけられるんですね!
・今回URLは「?page=」のあとにタイトルつければ繋がることが偶然わかったのでそうしています。そういう仕組みではないサイトでも平気な方法はありますか。
「そういう仕組みではない」にも、いろいろなタイプがあるので、ぶち当たってから、でも良さそうな気がします。
よくあるところだと、POST と、リダイレクトでしょうか。
POST は、パラメータが URL で見えないやつです。
ネットバンクや電車の予約のサイトでは、URL にパラメータがついてません。
リダイレクトは、ホームページの引っ越しとかで、アクセスしたURLから、別のサイトに飛ばされるやつです。
Net::HTTP のライブラリを使うと、「リダイレクトしろ、って言ってるよ」というのが分かるので、もう一回、ページの内容を取りに行きます。
・「Nokogiri」はわかんなくてあきらめてしまったけど、それを使ったほうがよかったのでしょうか。
Nokogiri を使わないと、どうにも面倒だ、ってなってからでも良いと思います。
例えば、先の「リダイレクト」。
これなんかを自動でやってくれます(確か)。
歌詞を切り取るところで、ピンポイントな目印があれば、こんな感じのソースで行けますけど、HTML の構造をたどらなきゃいけなかったりすると、Nokogiri を使うと楽になります。
# とか書きながら、ぼくも文字列ベースでやることがほとんどです (^^;
・最後にばらばらのテキストファイルに書き出してるけど、ふつうはデータベースに入れますか??
ページの内容をマルっと取っておきたいだけだったら、どっちでも。
件数が多くなってくると、データベースを使いたくなるような気がします。
検索したり、バックアップが楽だったり。
データベースを使わないとできない、というわけじゃありませんが。
スクレイピングは、取ってくる先のページの構造に大きく左右されるので、一般化はあまり気にしなくても良いと思います。
やるとしたら URL から中身を取ってくるところまでのエラー処理とかくらいでしょうか。
でも、その辺りは open-uri がやってくれているので。
動いているコードなので、このままでも良いと思うんですけど、以下の件は、
/(?<=<h3 id="id_0a172479">歌詞<\/h3>)(.*?)(?=<h3)/m =~ html lyric = $1 if (lyric == nil) next end lyric.gsub!(/<div>|\n/m, '') #divタグと\nを取り除く ... sleep 1
こういうふうに書くと思います。
if (/(?<=<h3 id="id_0a172479">歌詞<\/h3>)(.*?)(?=<h3)/m =~ html) lyric = $1 lyric.gsub!(/<div>|\n/m, '') #divタグと\nを取り除く ... sleep 1 end
それか、こう。
unless (/(?<=<h3 id="id_0a172479">歌詞<\/h3>)(.*?)(?=<h3)/m =~ html) next end lyric = $1 lyric.gsub!(/<div>|\n/m, '') #divタグと\nを取り除く ... sleep 1
この直前の「~見つかりません」と同じような形になってないと、気持ち悪いな、ってだけですが。
1,【IA】ロスタイムメモリー【オリジナルMV】,ロスタイムメモリー,http://www.nicovideo.jp/watch/sm20470051,じん,4:43,2013/3/30 2:27
2,【IA】夜咄ディセイブ【オリジナルMV】,夜咄ディセイブ,http://www.nicovideo.jp/watch/sm20116702,じん,3:22,2013/2/17 21:03
3,【鏡音リン】 ロストワンの号哭 【オリジナルPV付】,ロストワンの号哭,http://www.nicovideo.jp/watch/sm20244918,Neru,3:37,2013/3/4 19:59
4,初音ミクが声優のようにしゃべってラップして歌った!『ビバハピ』PV付,ビバハピ,http://www.nicovideo.jp/watch/sm21443197,Mitchie M,3:35,2013/7/26 16:23
5,【IA】アヤノの幸福理論【オリジナルMV】,アヤノの幸福理論,http://www.nicovideo.jp/watch/sm20671920,じん,5:33,2013/4/22 19:57
ほかになにか不足している情報があれば教えてください。
とても迷ったのですが、コメントでのやりとりに付き合ってくださった
TransFreeBSDさんをベストアンサーにしました。
人力検索はたいてい気持ちのいい回答をいただけるのでうれしいです。
今後ともよろしくお願いします!
私もお勉強がんばります。
http://q.hatena.ne.jp/1376398836#a1210917
こんなのとかは Ruby のスクリプトでデータを抜いてるので、あまり難しいことじゃなければ、一緒に考えることができると思います。
TransFreeBSD さんのいう「一覧をスクレイプ」をやってるわけですよね?
私もこんな境地を目指してがんばります。
これ、グラフはどうやって描いているのですか…?
あんまりやったことない私でも
Excelとかに放り込めばたぶん必要最低限のグラフは作れると思うのですが、
a-kuma3さんのグラフは見た目もキレイでわかりやすいです。
そうです。
「質問一覧」でみられるページの URL で質問番号の一覧をテキストで保存するスクリプトと、質問番号が書いてあるテキストファイルから個別の質問の情報を取り出すスクリプトを持ってます。
二つに分けてるのは、時間がかかることもあるのですが、個別の質問ページから情報を取るスクリプトを作るのに少々てこずったから、というのが大きいです。
テスト用に、いろいろなバリエーションの質問番号だけを書いたファイルをテスト用に作って、それを使ってテストを繰り返しました。
後で気が付いたのですが、こういう作りにしておくと、例えば、かきつばた杯の質問番号の一覧をはてなキーワードから作って、その情報を取り出す、というのが、わりと簡単にできます。
>これ、グラフはどうやって描いているのですか…?
お察しの通り、Excel くんにお任せです。とっても楽チン。
データの加工は、たまに別のスクリプトを書くことがありますが、Excel 2007 から扱えるデータの数が増えたので、データの加工も Excel でやることが多くなりました。
2つに分けている、ということですね。
今回私が初音ミクWikiでやったのは、a-kuma3さんのスクリプトでいうと
後者のほうと近い働きをしていることになるでしょうか。
私は今後、元ネタがオリコンランキングになったり
JOYSOUNDになったりすると思いますが、
基本的に元があって、そのための歌詞を集めてくることをしようと思うので、
後者型のスクリプトに重点的に取り組んでいくことになると思います!
グラフはExcelなのですか!
失礼ながらExcelってもっとあか抜けないグラフになると思っていました…。
道具は使う人次第ですね。
です、です。
ネタになってる CSV をスクレイピングで作るスクリプトもある、という感じです。
一覧がこんな感じで、
>>
1391065531 16時間前
1391064945 16時間前
1391063963 17時間前
1391062840 17時間前
...
<<
詳細が、こんな感じです。
>>
Q 1391065531 pida6 pida6 2014/01/30 16:05:31 -- - - - 0
Q 1391064945 koko24 koko24 2014/01/30 15:55:45 -- 100 - - 0
A 1391063963 bg5551 bg5551 2014/01/30 15:39:23 2014/01/30 18:14:20 300 - -
A 1391063963 rafting ラフティング 2014/01/30 15:39:23 2014/01/30 22:47:23 300 - -
Q 1391063963 kzmmtsd stdgejf 2014/01/30 15:39:23 -- 300 - - 2
A 1391062840 bg5551 bg5551 2014/01/30 15:20:40 2014/01/30 18:01:01 500 - -
Q 1391062840 kozo-san コーゾー 2014/01/30 15:20:40 -- 500 - - 1
...
<<
回答→質問の順番になってるのは、正規表現で拾ってる限界(というほどじゃないけど、面倒でギブアップ)です。
>失礼ながらExcelってもっとあか抜けないグラフになると思っていました…。
>道具は使う人次第ですね。
いやいや、Excel くんの力が大きいです。
やってみると分かりますが、色やサイズなんか、ほぼデフォルトですし :-)
ひとつめのリストの1列目(Ruby的には0列目)は質問のページのURLですよね。
ふたつめのリストの最後の列は質問についている回答の数ですね。
ここまで情報が揃えられるようになったらば、そこから先は
いろんな分析の切り口を見出せるようになることのほうが大事になりますね。
情報の山の中からおもしろい見解を発見するようなことは
あまりしたことがないので、そういう腕も今後磨いていきたいと思います!