rails 独学して軽くにちゃんまとめつくる

読者です 読者をやめる 読者になる 読者になる

rails独学して軽く2ちゃんまとめビルダーをつくる。

rails tutorial読破→2chまとめビルダー作成→アメリカでインターンシップ→恋活SNSを作成・・・・・→音で感触をつくり感触をあつめたライブラリをつくる

nokogiri を使って Youtube の動画とサムネイル画像を拾ってきて。

Nokogiri というrails の gem を使うとインターネット上からテキストやデータや画像を拾ってくれるようになります。今回は youtube の動画を拾ってきて、説明欄や

まずはnogogiri という gem をインストールしましょう。 gem install nokogiri

その後以下のコードを入力して起動してください。

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

urls = []
search_term = URI.encode("アカギ")
url = "https://www.youtube.com/results?search_query=#{search_term}"


doc = Nokogiri::HTML(open(url))
elements = doc.xpath("//h3['yt-lockup-title']/a")
elements.each do |a|
  code = a.attributes['href'].value
  urls << "https://www.youtube.com" + code if code.include?("watch")
end



urls.each do |url|
  puts url
  doc = Nokogiri::HTML(open(url), nil, "UTF-8")
  title = doc.xpath("//h1[@class = 'watch-title-container']/span").text.gsub(/\n/, '')

  desc = doc.xpath("//p[@id = 'eow-description']").text
  puts title
  puts desc
end

すると

 $ ruby test_youtube.rb
https://www.youtube.com/watch?v=mpCKle9hhbk&list=PLTP-72gM0GB0uvu1zNut6kaunzPXfpExk
    Kaiji episode 1  

https://www.youtube.com/watch?v=y5pyPBTdNXQ
    カイジ Ultimate Survivor ①  
カイジ Ultimate Survivor ①https://goo.gl/KDMdJ6
https://www.youtube.com/watch?v=2jH2BNIeKEE
    Kaiji   #2 sub español  

https://www.youtube.com/watch?v=c0liPtuQbp0
    Kaiji 2 Life Reversal Game - Dublado  
Kaiji 2 Life Reversal Game - Dublado  em Português
https://www.youtube.com/watch?v=kZ53jVdhXE8
    CR弾球黙示録カイジ沼3 パチンコ新台実践『初打ち!』圧倒的怪物への挑戦!20173月新台【たぬパチ!】  

こんなかんじに URL とタイトルと説明欄をひろってくるようになります。

search_term = URI.encode("カイジ")

これについて、youtube では検索するとき日本語そのままで検索するのではなく、いったん文字をエンコードしてから検索しています。検索結果では日本語表記されてますが、再生したときはこうなってます。だから文字をエンコードするためにこれが必要です。

f:id:mooooooooooriiiiii:20170323010213p:plain

elements = doc.xpath("//h3['yt-lockup-title']/a")

これについて、h3['yt-lockup-title'] というのはクラス名が yt-lockup-title である h3 を指しています。本では h3[@class='yt-lockup-title'] と書かれているはずです。

たまになのですが、 @class= という書き方では認識されないときがあります。ちゃんと xpath を書いているのに nokogiri で表示されないというときは @class= や @id= の表記をうたがってください。

また、この xpath はユーチューブのここを指しています。

f:id:mooooooooooriiiiii:20170323011019p:plain

つぎにcode = a.attributes['href'].valueこのコードについて詳しく見ていきましょう。

これについて知るためには Nokogiri がどのようなデータになっているのか知る必要があります。最初のコードをちょこっと変えてみて、こんなコードを作ります。

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

urls = []
search_term = URI.encode("アカギ")
url = "https://www.youtube.com/results?search_query=#{search_term}"


doc = Nokogiri::HTML(open(url))
elements = doc.xpath("//h3['yt-lockup-title']/a")
elements.each do |a|
  
  p a
  code = a.attributes['href'].value
  puts "------------------------"
  p code
  puts "------------------------"
  urls << "https://www.youtube.com" + code if code.include?("watch")
end

p を使うことで、each で取り出した変数 a の内部構造をみることができます。puts "-------" で区切り線をつけて見やすくしてます。最終的にでてきたコードの一部をとりだしてみるとこういうコードになります。

#<Nokogiri::XML::Element:0xe9a3cc name="a" attributes=
  [  #<Nokogiri::XML::Attr:0xe9a304 name="href" value="/watch?v=gnx3W8Q3nuE">,
     #<Nokogiri::XML::Attr:0xe9a2f0 name="class" value="yt-uix-tile-link yt-ui-ellipsis yt-ui-ellipsis-2 yt-uix-sessionlink      spf-link ">,
     #<Nokogiri::XML::Attr:0xe9a2dc name="data-sessionlink" value="itct=CEIQ3DAYAiITCMX05rjC6tICFdQWWAod3d8I_yj0JFIJ44Ki44Kr44Ku">, 
     #<Nokogiri::XML::Attr:0xe9a2c8 name="title" value="ã\u0082¢ã\u0082«ã\u0082®ã\u0080\u0080第9話ã\u0080\u0080天æ\u0089\u008Dã\u0081®ç\u009C\u009Fè´\u008B">, 
     #<Nokogiri::XML::Attr:0xe9a2a0 name="aria-describedby" value="description-id-531595">,
     #<Nokogiri::XML::Attr:0xe9a278 name="rel" value="spf-prefetch">, 
     #<Nokogiri::XML::Attr:0xe9a23c name="dir" value="ltr">] children=[#<Nokogiri::XML::Text:0x10d30e4 "ã\u0082¢ã\u0082«ã\u0082®ã\u0080\u0080第9話ã\u0080\u0080天æ\u0089\u008Dã\u0081®ç\u009C\u009Fè´\u008B">
  ]>
----------------------
"/watch?v=gnx3W8Q3nuE"
------------------------

コードをみればなんとなくわかるように、code = a.attributes['href'].value の attributes[‘href’] とは Nokogiri の attributes = “” のなかの name=“href” を指しています。value はまさにその中に value=“ ” の中にある文字をさしています。 どうように a.attributes['class'].class もできます。

doc = Nokogiri::HTML(open(url), nil, "UTF-8")

これはurl を開くためのコードです。少しうえのコードで作成した URL を今度は開いてます。ということはこの時点で検索結果一覧ページではなく、個々の動画のページに移動していることになります。

title = doc.xpath("//h1[@class = 'watch-title-container']/span").text.gsub(/\n/, '')

これはタイトルを表してます。画像で言えばここ。

f:id:mooooooooooriiiiii:20170323014217p:plain

//h1[@class= 'watch-headline-title'] とはどこか奥深くの watch-headline-title というクラス名をもった h1 を意味します。 // を使うと位置を厳密に指定する必要がなくて便利です。その分重複してしまう可能性があり、このようにクラス名を指定しておくのは重要です。

.text はタグで囲まれた文字を返します。この場合 にかこまれた文字です。

.gsub(/(正規表現)/, "(置き換える文字)") について。これはまあまあ使うめそっどです。このばあい \n(改行)を``に置き換えてる、つまり削除してます。 便利なのでおぼえておきましょう。

こんなもんですね。

Ruby によるクローラー開発技法』のままやるとエラーがでます。 どうやらYoutube のURL を http ではなく https ではじめる必要があります。だからそのまま打ち込むと

 $ ruby test_youtube.rb
http://www.youtube.com/watch?v=DFJh9vFixHs&list=PLyJjW5KES8Q0waiG5eF7RuFIqaA-SIxKN
/usr/local/rvm/rubies/ruby-2.3.0/lib/ruby/2.3.0/open-uri.rb:225:in `open_loop': redirection forbidden: http://www.youtube.com/watch?v=DFJh9vFixHs&list=PLyJjW5KES8Q0waiG5eF7RuFIqaA-SIxKN -> https://www.youtube.com/watch?v=DFJh9vFixHs&list=PLyJjW5KES8Q0waiG5eF7RuFIqaA-SIxKN (RuntimeError)
        from /usr/local/rvm/rubies/ruby-2.3.0/lib/ruby/2.3.0/open-uri.rb:151:in `open_uri'
        from /usr/local/rvm/rubies/ruby-2.3.0/lib/ruby/2.3.0/open-uri.rb:717:in `open'
        from /usr/local/rvm/rubies/ruby-2.3.0/lib/ruby/2.3.0/open-uri.rb:35:in `open'
        from test_youtube.rb:21:in `block in <main>'
        from test_youtube.rb:19:in `each'
        from test_youtube.rb:19:in `<main>'

こんなエラーがでて一件しか取得できません。注意。

ーーーーーーーーーーーーーーー 追記。

ここからはもう少しあそんでいきます。リンクを持ってくるだけではつまらないので、サムネイル画像を拾ってこうと思います。基本的にこのような流れでやっていきます。

①ターゲット(画像)のxpath を解析 ②取得したリンクを見て、都合のいいように変形 ③出力

まずは①の画像のxpath 解析から。

画像の xpath を観察してみると 1 ばんめから順に li の数字が変化していることに気付きます。

//li[2]/ol/li[1]/div/div/div[1]/a/div/span/img

//li[2]/ol/li[2]/div/div/div[1]/a/div/span/img

//li[2]/ol/li[3]/div/div/div[1]/a/div/span/img

//li[2]/ol/li[4]/div/div/div[1]/a/div/span/img

よって

//li[2]/ol/li/div/div/div[1]/a/div/span/img

この状態でのコードと結果を書いておきます。

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

urls = []
search_term = URI.encode("カイジ")
url = "https://www.youtube.com/results?search_query=#{search_term}"

cleaned_pics = []
doc = Nokogiri::HTML(open(url))
pics = doc.xpath("//li[2]/ol/li/div/div/div[1]/a/div/span/img").each{|pic| puts pic.attributes['src']}

end
:~/workspace $ ruby test_youtube.rb
https://i.ytimg.com/vi/mpCKle9hhbk/hqdefault.jpg?custom=true&w=246&h=138&stc=true&jpg444=true&jpgq=90&sp=68&sigh=04JeuJ4O29aIHreWcinFA2-wisI
https://i.ytimg.com/vi/y5pyPBTdNXQ/hqdefault.jpg?custom=true&w=246&h=138&stc=true&jpg444=true&jpgq=90&sp=68&sigh=_1YnIM0G4hCOb4NMEm5woZaeP-E
https://i.ytimg.com/vi/2jH2BNIeKEE/hqdefault.jpg?custom=true&w=246&h=138&stc=true&jpg444=true&jpgq=90&sp=68&sigh=xo_SW7brtUDDCF-Q6HJ2TCVJlpI
https://i.ytimg.com/vi/c0liPtuQbp0/hqdefault.jpg?custom=true&w=246&h=138&stc=true&jpg444=true&jpgq=90&sp=68&sigh=P-4H62loznfh9iwthKjkDCj7XYw
https://i.ytimg.com/vi/Kp9Ed2LCSio/hqdefault.jpg?custom=true&w=246&h=138&stc=true&jpg444=true&jpgq=90&sp=68&sigh=jNEVG6isk5Lmt2XLmEzalEHrdh8
https://i.ytimg.com/vi/kZ53jVdhXE8/hqdefault.jpg?custom=true&w=246&h=138&stc=true&jpg444=true&jpgq=90&sp=68&sigh=8BcivPyS899L2BmPQoPXfrPDbqU
/yts/img/pixel-vfl3z5WfW.gif
/yts/img/pixel-vfl3z5WfW.gif
/yts/img/pixel-vfl3z5WfW.gif
/yts/img/pixel-vfl3z5WfW.gif
/yts/img/pixel-vfl3z5WfW.gif
/yts/img/pixel-vfl3z5WfW.gif

ここで問題は gif ファイルが入っているのと、 jpg 画像のサイズ指定があり邪魔だということです。 ②ではgif を解き除き、jpg のリンクの後ろのサイズ指定などのオプションを取り除きましょう。

② 取得したリンクを見て、都合のいいように変形

まず gif から取り除きます。ほしい jpg といらない gif 画像の違いは jpg という文字があるか否かです。 ということで include?('jpg') を使って jpg という文字があるものだけを取り出しましょう。 さらに pic.attributes['src'] はこの時点では #<Nokogiri::XML::Attr….> という形なので文字列に直しておくために .to_s を使って文字に直しておきます。すると、

require 'nokogiri'
require 'open-uri'
require 'uri'
urls = []
search_term = URI.encode("カイジ")
url = "https://www.youtube.com/results?search_query=#{search_term}"

cleaned_pics = []
doc = Nokogiri::HTML(open(url))
pics = doc.xpath("//li[2]/ol/li/div/div/div[1]/a/div/span/img")
            .each{|pic| puts pic.attributes['src'] if pic.attributes['src'].to_s.include?('jpg')}

となり、出力結果は

$ ruby test_youtube.rb
https://i.ytimg.com/vi/mpCKle9hhbk/hqdefault.jpg?custom=true&w=246&h=138&stc=true&jpg444=true&jpgq=90&sp=68&sigh=04JeuJ4O29aIHreWcinFA2-wisI

https://i.ytimg.com/vi/y5pyPBTdNXQ/hqdefault.jpg?custom=true&w=246&h=138&stc=true&jpg444=true&jpgq=90&sp=68&sigh=_1YnIM0G4hCOb4NMEm5woZaeP-E

https://i.ytimg.com/vi/2jH2BNIeKEE/hqdefault.jpg?custom=true&w=246&h=138&stc=true&jpg444=true&jpgq=90&sp=68&sigh=xo_SW7brtUDDCF-Q6HJ2TCVJlpI

こんな感じになってます。cleaned_pics という変数にいれておきます。よってこうなります。

pics = doc.xpath("//li[2]/ol/li/div/div/div[1]/a/div/span/img")
        .each{|pic| cleaned_pics << pic.attributes['src'] if pic.attributes['src'].to_s.include?('jpg')}

でも後ろのオプションがじゃまですねえ。これを取り除いていきます。 どの部分がオプションなのかはじっくり見続けるしかないです。よくみると .jpg?を境にオプションであることがわかります。

https://i.ytimg.com/vi/mpCKle9hhbk/hqdefault.jpg?custom=true&w=246&h=138&stc=true&jpg444=true&jpgq=90&sp=68&
sigh=04JeuJ4O29aIHreWcinFA2-wisI
は
https://i.ytimg.com/vi/mpCKle9hhbk/hqdefault.jpg
これと
?custom=true&w=246&h=138&stc=true&jpg444=true&jpgq=90&sp=68&sigh=04JeuJ4O29aIHreWcinFA2-wisI
これにわかれてる

扱うのはリンクだからある程度形式に従って成立していてかつ、それを取り出したいから正規表現を使うのが吉。 よってこんな正規表現を書きます。

/(.+\.jpg)\?.+/

これで jpg を境に左右に分けられ、()に囲まれた左側のみキャプチャをすることができます。 キャプチャしたもののみを取り出す場合は $1 を打てばいいですから結果的には

cleaned_pics.each do |pic|
  pic.to_s =~ /(.+\.jpg)\?.+/
  puts $1
end

となります。これらを統合すると

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

urls = []
search_term = URI.encode("カイジ")
url = "https://www.youtube.com/results?search_query=#{search_term}"

cleaned_pics = []
doc = Nokogiri::HTML(open(url))
pics = doc.xpath("//li[2]/ol/li/div/div/div[1]/a/div/span/img")
        .each{|pic| cleaned_pics << pic.attributes['src'] if pic.attributes['src'].to_s.include?('jpg')}

cleaned_pics.each do |pic|
  pic.to_s =~ /(.+\.jpg)\?.+/
  puts $1
end
=end

こうなりました。 よし、お疲れさまです。これでこの記事は終わりです。頑張ってね。