В споре рождается истина

- А я так не думаю!

Избитая тема про музыку вконтакте

с 2 комментариями

Вообще уже настало время, когда хранить музыку на винчестере смысла особого нет.
Но, блин, с инетом проблемы бывают. Поэтому пусть пока лучше будет :)

Итак, как сохранить свою музыку с сайта вконтакте на свой компьютер?

Если посмотреть код страницы “Аудиозаписи”, то можно заметить, что код разбит на div’ы, span’ы и тд, что позволяет легко вычленить ссылки на mp3-шки и получить сведения о песне (название, группа). И сохранить с нормальными названиями, а не просто комбинациями цифр и букв.

Кажется, что все просто, но это не так.
У меня 173 аудиозаписи, это все разбито на 2 страницы (100 штук на первой, остальное на второй).
Когда я перехожу на вторую страницу – в исходнике ничего не меняется. Ничего страшного, конечно, но просто остаются те же 100 песен, что были на 1-й странице.

Хаха, не трудно догадаться, что здесь замешан так горячо любимый мною JavaScript.
В Opera можно легко отключить javascript, после чего переход на вторую страницу позволяет просмотреть ее исходный код и увидеть оставшиеся 73 песни.

Структура кода примерно такая:

...
<div class="audioRow" id="audio84309192">
<a name='84309192'></a>
 <table width="100%"><tbody>
 <tr><td style="width: 20px; vertical-align:top">
 <img class="playimg" onclick="return operate(84309192,'http://cs4707.vkontakte.ru/u12848345/audio/e7bffd8f5682.mp3',281);" id="imgbutton84309192" nosorthandle="true" src="images/play.gif"/>
 </td>
 <td style="width: 360px;"><div class="audioTitle">
  <b id="performer84309192"><a href='gsearch.php?section=audio&c[q]=Queen'>Queen</a></b><span>&nbsp;-&nbsp;</span><span id="title84309192"><a href='javascript: showLyrics(84309192,4589379);'>I Want It All</a></span> </div>
  <div class="duration">4:41</div>
 </td>
 </tr>
 </tbody></table>

<div style="height:14px;margin-left:28px;">
<div id="line84309192" class="playline"></div>
<div id="toddler84309192" class="toddler">
</div>
<div id="player84309192" style="display: none;" class="playerClass">
</div>
</div>

<div id="lyrics84309192"></div>
</div>

<div class="audioRow" id="audio81408147">
<a name='81408147'></a>
 <table width="100%"><tbody>
 <tr><td style="width: 20px; vertical-align:top">
 <img class="playimg" onclick="return operate(81408147,'http://cs4718.vkontakte.ru/u59481183/audio/c3ff1145f6ec.mp3',146);" id="imgbutton81408147" nosorthandle="true" src="images/play.gif"/>
 </td>
 <td style="width: 360px;"><div class="audioTitle">
  <b id="performer81408147"><a href='gsearch.php?section=audio&c[q]=Валерий Горбачев'>Валерий Горбачев</a></b><span>&nbsp;-&nbsp;</span><span id="title81408147">До первого убитого</span> </div>
  <div class="duration">2:26</div>
 </td>
 </tr>
 </tbody></table>

<div style="height:14px;margin-left:28px;">
<div id="line81408147" class="playline"></div>
<div id="toddler81408147" class="toddler">
</div>
<div id="player81408147" style="display: none;" class="playerClass">
</div>
</div>

<div id="lyrics81408147"></div>
</div>
...

Отличие между этими двумя песнями в том, что для первой есть слова, а для второй – нет.
Таким образом, в Song! может внутрь вставляться гиперссылка и тогда получается

Song!

.

Уже зная это можно написать программу на любом тьюринг-полном.
Я написал скрипт на Ruby. Работать с ним надо так:

  1. Поместить его в папку, куда должны скачиваться файлы
  2. Сохранить в эту папку файлы страниц вконтакте с ссылками на аудиофайлы
  3. Запустить скрипт, ввести имена данных файлов
  4. Подождать

Вот, что было у меня:

Можно заметить, что есть несколько песен, скачанных с ошибкой:

причем первая песня была недоступна с сервера (не проигрывалась даже на самом контакте), а вторая судя по всему содержала запрещенный символ в названии.
Думаю, результат можно считать удовлетворительным.

Вот мой скрипт:

#script helps you to download music
#author jtim, 17 august, 2010
#email jtimchenko@gmail.com
require 'rubygems'
require 'hpricot'
require 'iconv'
require 'open-uri'

def download_file(link, filename)
  begin
    writeOut = open(filename, 'wb')
    writeOut.write(open(link).read)
    writeOut.close
  rescue
    return false
  end
  true
end

utf8 = Iconv.new("utf8", "windows-1251")
links = Hash.new

re_link = Regexp.compile("'(.*)'")
re_song = Regexp.compile("<.*?>")

loop{
  puts"Enter name of file to scan or empty line to start downloading..."
  input_file_name = gets.chomp
  break if input_file_name == ""

  begin
    (Hpricot(open(input_file_name))/'div.audioRow').each do |nd|
    begin
      fn = re_link.match((nd/'img')[0]['onclick'])[1]
      artist = utf8.iconv((nd/'a')[1].inner_html())
      song   = utf8.iconv((nd/'span')[1].inner_html()).gsub(re_song,'')
      links[fn] = artist + " - " + song+'.mp3'
    rescue
      puts "Error!"
      puts nd.to_html
      puts "-----"
    end
  end
  rescue
    puts "Error while opening file!"
  end

  links.each_key{|k|
    puts k+" : "+links[k]
  }

  puts "There are #{links.length} files in list!"  
}

all = links.length
num = 1
links.each_key{|k|
  print("downloading [#{links[k]}]: #{num} / #{all} : ")
  msg=''
  if download_file(k, links[k])
    msg='OK'
  else
    msg='ERROR!'
  end
  puts(msg)
  num+=1
}

Что надо отметить:

  1. Это работает для ruby 1.8. На ruby 1.9 я не проверял
  2. Используется gem hpricote для парсинга html файла – код библиотеки написан самим Why (автор книги why’s poignant ruby guide)
  3. Я заюзал регулярки, нифигасе!

Основные минусы:

  • Появляются символы типа “Vicky Leandros – Tango d'Amor.mp3″. Конечно же там должен стоять апостроф. Видимо, это возникает при переводе cp1251 в utf8.
  • Каждый файл скачивается за раз. Пока он не скачался полностью – ничего не сохранено. Как только файл скачается целиком – он сразу же будет записан на винт. Короче, если интернет часто обрывается, то скрипт становится почти бесполезным.

Основные плюсы:

  • Простота в использовании
  • Человеческие имена файлов
  • Теперь можно будет слушать музыку, не заходя на контакт!

p.s.: в посте есть парочка несмертельных ошибок, но исправлять сейчас уже нет сил. Надо бы поспать)

Написано jtimv

17.08.2010 в 08:18

Опубликовано в Программирование

Отмечено как

Комментариев: 2

Подписаться на комментарии по RSS.

  1. Ага, я ще колись тобі казав, що треба парсити html. Ти мені сказав, що регулярного виразу досить.

    І я тебе таки послухав: wget -c `cat list.htm | grep -o http://.*mp3`

    http://bunyk.wordpress.com/2010/08/06/download-howto/

    Мінус мого методу – нелюдські імена файлів, але плеєрам які в курсі що таке id3 це до лампочки. (Загалом треба було б дописати скрипт який перейменовує файли відповідно до id3, але лінь).

    Плюси – мало коду, і це wget, він вміє докачувати.

    bunyk

    17.08.2010 в 09:10

  2. А в wget можно делать финты типа
    wget link_to_file new_file_name ?
    Если да, то регулярками можно выдрать все, что хочешь!

    jtimv

    17.08.2010 в 15:55


Добавить комментарий

Fill in your details below or click an icon to log in:

Логотип WordPress.com

You are commenting using your WordPress.com account. Log Out / Изменить )

Фотография Twitter

You are commenting using your Twitter account. Log Out / Изменить )

Фотография Facebook

You are commenting using your Facebook account. Log Out / Изменить )

Connecting to %s

Follow

Get every new post delivered to your Inbox.