Привет, Хабр! Представляю вашему вниманию свой скромный проект для парсинга аудио Вконтакте.
Так как слушать музыку напрямую вконтакте не круто и не всегда возможно/удобно, я подумал, что неплохо бы иметь возможность стягивать свой плэйлист и плэйлисты друзей. Недостатком тех программ, что я юзал, было то, что приходилось качать по одной песне. Если плэйлист большой, то это не удобно и жутко долго. От своего приложения я хотел, чтобы оно могло качать всё и сразу, и чтобы музыка сортировалась по исполнителям.

В начале я думал, что обойдусь cURL, но как оказалось, контакт хитёр и выгружает по умолчанию ~50 песен. Но об этом позже.

1. Авторизация ВК

Тут я особо не изворачивался. Для того, чтобы получить свой профиль ВК, просто прикручиваем свои кукисы. Кука, которая нам нужна, называется remixsid.



2. Получение контента страницы с помощью phantomJS

Скачать фантом можно отсюда. Для Linux версии достаточно его просто распаковать и добавить линк в /usr/bin (по желанию). О том, что это и с чем его едят, можете ознакомиться тут.
Как я уже говорил, контакт выгружает не весь список аудио по умолчанию. Он подгружает его динамически в процессе прокручивания окна. К тому времени как я решил подключить фантом, у меня уже была готовая структура для парсинга странички, написанная на php, поэтому я решил не переписывать это на js. Сам фантом здесь только проскролливает окно и отдает контент страницы.

        // return vk page content
        private function parseUrl($url) {
            // get remixsid value
            $sid = $this->RemixSid();
            // get scrolled page
            $command = 'phantomjs '.$_SERVER['DOCUMENT_ROOT'].'/js/phantom.vk.js' . " $sid $url";
            $response = shell_exec($command);
            return $response;
        }


Листинг
Листинг
/*
 * it must run from phantomjs
 * args[0] - remixsid cookie value
 * args[1] - target vk url
 */
var sys = require("system"),
    page = require("webpage").create();
    
phantom.addCookie({
    'name': 'remixsid',
    'value': phantom.args[0],
    'domain': 'vk.com'
});

page.settings = {
	loadImages: false,
	javascriptEnabled: true,
	userAgent: 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1',
};

page.open(phantom.args[1], function() {
    var tries = 0,
    t = setInterval(function() {
        page.scrollPosition = { top: page.scrollPosition.top + screen.height, left: 0 };
        ++tries;
            if (tries === 40) {            
                console.log(page.content);
                clearInterval(t);
                phantom.exit();           
            }
    },100);
});




Думаю, тут всё должно быть понятно. Объект page умеет работать с веб-страницами, передавать данные POST, GET, PUT, выполнять JS непосредственно в контексте открытой страницы и многое другое.

Внешние переменные доступны через массив args, глобального объекта phantom. На протяжении 4 секунд скрипт проскролливает страницу и отдает её контент page.content. Мне этого хватает, чтобы спарсить овер 2000+ песен.

Вывод в phantomJS осуществляется с помощью обычного console.log();

3. Парсим?

Если посмотреть исходный код странички, то мы найдем возле каждого трэка ссылку на поддомен vk.me, откуда собственно и загружаются аудиозаписи
image

Я не прикручивал библиотек для разбора html типа этой. А обошелся обычным регэкспом:
        public function actionDownload() 
        {   
            $data = '';
        // TODO: include html simple dom
$audioRegExp = '/<input[\s\S]+?id=["\']audio_info[_0-9]+["\'][\s\S]+?value=[\'"](.+?)[\'"][\s\S]+?<b>\s*<a.*?>(.+?)<\/a>[\s\S]+?<span\sclass=["\']title[\'"]>\s*?(?:<a.*?>)?(.+?)</i';
            $songs = array();
            
            if (isset($_POST['type'])) {
                switch ($_POST['type']) {
                    case 'link': {
                        $data = $this->parseUrl($_POST['target']);
                        break;
                    }
                    case 'file': {
                        if ($_FILES && $_FILES['target']) {
                            $file = $_FILES['target']['tmp_name'];
                            if (!empty($file))
                                $data = file_get_contents($file);
                        }                           
                        break;
                    }
                    default: {
                        throw new CHttpException(400,'неверный тип загружаемых данных');
                        break;
                    }
                }
                preg_match_all($audioRegExp, $data, $parse);
                $songs = self::serializeVkSongs($parse);
                
            }

           $this->render('download', array('songs' => $songs, 'count' => self::$songsCount)); 
            
        }


Кому интересно узреть всё и сразу: github.com/zjiodeu/vkMusicParser
Всем спасибо. Если есть вопросы — пишите, отвечу в комментах.

Спасибо за внимание.