Yahoo!ボックス からファイルを一括ダウンロードするスクリプト
Yahoo!ボックスとかいうオンラインストレージサービスがあるらしい。
info.box.yahoo.co.jp
アプリが終了してブラウザから1つ1つダウンロードするしかないらしい。助けてくれ!と友人に頼まれた。
しばらくChromeのデベロッパーツールで眺めていると、ファイル情報をJSONでやりとりしているだけなようなので、書いた。
はじめはRuby の Mechanize を使って Yahoo! JAPAN にログインする - kaosf’s diaryを参考にしてログイン処理をしていたが、途中からログインできなくなった。Mechanizeだと今どのような状態なのかがわかりにくいので、Selenium-webdriverを使って再現してみると、文字認証を求められていた。
探してみるとrubyでYahoo Japanにログインする。Cookie発行してもらう - それマグで!のような情報がでてきたので、使わせていただいた。
(微妙にCAPCHAのURLの種類が違ったのでそのへんだけ書き換えたりした)
2016/12/30 Windows環境で動作させると,txt以外がファイルが壊れるとの指摘をいただきました。訂正しました。
#参考:http://takuya-1st.hatenablog.jp/entry/20121018/1350587902 #!/usr/bin/env ruby #coding:utf-8 require 'rubygems' require 'mechanize' require 'open-uri' require 'net/http' require 'uri' OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE Id = 'XXXXXXXXXXXXXXXX' Password = '****************' cookie_jar_yaml_path = 'yahoo.yaml' #Yahoo!ログイン agent = Mechanize.new agent.user_agent_alias = 'Windows IE 7' agent.get('https://login.yahoo.co.jp/config/login?.src=www&.done=http://www.yahoo.co.jp') agent.page.form_with(name: 'login_form') do |form| form.field_with(name: 'login').value = Id form.field_with(name: 'passwd').value = Password # agent.page.body =~ /\("\.albatross"\)\[0\]\.value = "(.*)"/ # form.field_with(name: '.albatross').value = $1 form.click_button end #CAPTHCA str = agent.page.body.match( %r!"https://captcha.yahoo.co.jp:443/[^"]+!).to_s.gsub(/"/,"") puts str open(str) do |file| open("captcha#{Time.now.to_i}.jpg", "w+b") do |out| out.write(file.read) end end capthca = '' $stdout.print 'enter captcha:' captcha = $stdin.readline puts "i got captcha#{captcha}" agent.page.forms.first.fields_with(:type=>"text").first.value=captcha agent.page.forms.first.submit #CAPTHCA後の再ログイン f=agent.page.forms[0] f.fields_with( :name=>"login")[0].value=Id f.fields_with( :name=>"passwd")[0].value=Password f.submit puts agent.page.body.to_s.toutf8 agent.cookie_jar.save_as(cookie_jar_yaml_path) File.expand_path cookie_jar_yaml_path
これを実行すると、ログイン情報のクッキーがyahoo.yamlというファイルに出力される.
次に、Yahoo!ボックスからファイルを一括ダウンロードするスクリプトを実行する
#Yahoo! Box Downloader require 'mechanize' require 'nokogiri' require 'kconv' require 'scanf' require 'date' require 'uri' require 'json' require 'erb' require 'net/http' require 'open-uri' include ERB::Util cookie_jar_yaml_path = 'yahoo.yaml' #ログイン情報のクッキーを保存したファイル filenum_of_page = 100 #一度に読み込むファイル数 20,50,100のどれか #Yahoo!Boxへアクセス agent = Mechanize.new agent.user_agent_alias = 'Windows IE 7' agent.cookie_jar.load(cookie_jar_yaml_path) page = agent.get('https://box.yahoo.co.jp/user/viewer') #Javascriptの文字列からsid,uniqid,crumb,appidを取り出す tmp_rst = page.search('script')[0] user_parmsstr = tmp_rst.to_s.split("\n")[2].split(',') crumb_parameter = tmp_rst.to_s.split("\n")[3].split(',') appid_parameter = tmp_rst.to_s.split("\n")[4].split(',') sid = user_parmsstr[0].scanf(" User = {\'sid\':\"%s\"")[0].to_s topuniqid = user_parmsstr[1].scanf(" \'uniqid\':\"%s\"},")[0].to_s crumb = crumb_parameter[1].scanf("'bcrumb':\"%s")[0].to_s appid = appid_parameter[0].scanf("\t\t'appid':\'%s")[0].to_s puts appid #scanfうまくいかないのでうしろの"を消す 正規表現ちゃんとかくべき^^; sid = sid[0,topuniqid.index("\"",2)+1] topuniqid = topuniqid[0,topuniqid.index("\"",2)] crumb = crumb[0,crumb.index("\"",2)] appid = appid[0,appid.index("'",2)] puts "sid = #{sid}" puts "uniqid = #{topuniqid}" puts "crumb = #{crumb}" puts "appid = #{appid}" #ここから巡回してファイルをダウンロード folderList = Array.new folderList.push(topuniqid) #folderListが空になるまで巡回する while folderList.size != 0 do #folderListから一つ取り出す nowuniqid = folderList.pop #そのフォルダ内のファイルのリストが書かれたJSONを取得する urlstr = "https://box.yahoo.co.jp/api/v1/filelist/" + sid + "/" + nowuniqid + "?_=" + DateTime.now.strftime('%Q').to_s + "&" urlstr << "results=#{filenum_of_page}&start=1&output=json&sort=%2Bname&filetype=both&meta=1&thumbnail=1&tree=1&sharemembercount=1&ownerinfo=1&boxcrumb=" urlstr << url_encode(crumb) agent.get(urlstr) jsonstr = JSON.parse(agent.page.body.to_s) # 複数ページが存在する場合はまず全ページたどってファイル情報を入手 filenum = jsonstr['ObjectList']['TotalResultsAvailable'].to_s unless jsonstr['ObjectList']['Object'] == nil jsonstr['ObjectList']['Object'].each do |object| type = object['Type'].to_s name = object['Name'].to_s uniqid = object['UniqId'].to_s dlurl = object['Url'].to_s path = "." + object['Path'].to_s #パスの先頭にドットをつけないとうまく相対パスにならない #ファイルかフォルダかで処理を分岐 if(type == 'file') then dlurl << "?appid=#{appid}&error_redirect=1&done=https%3A%2F%2Fbox.yahoo.co.jp%2Ferror%2Fdownload_error&boxcrumb=" dlurl << url_encode(crumb) #dlurlからリダイレクトされたURLを取得 これがダウンロードリンク agent.get(dlurl) redirect_link = agent.page.uri.to_s #ファイルを保存 #File.write(path, Net::HTTP.get(URI.parse(redirect_link))) open(redirect_link) do |file| open(path, "w+b") do |out| out.write(file.read) end end puts "Download #{path}" elsif(type == 'dir') then #folderListに追加してあとで巡回 folderList.push(uniqid) Dir.mkdir(path) end end end end
詳しく解説してもたぶん需要なさそうなので、コードはりつけておしまい。
sleepをはさんで、負荷かけないようにしてつかいましょう。
一応gistこちらも修正しました[2016/12/30]
https://gist.github.com/lp6m/5913c1ef770f75825b00081a6ed7f671
https://gist.github.com/lp6m/a4e927963e218884e2a843e40e7a22b5