(Оновлено 09.05.2016, надійніше, ніж поточна відповідь)
Якщо вам просто потрібно зберегти кілька списків відтворення, ви можете просто скористатися моїм фрагментом Javascript нижче. Цей фрагмент може зберегти кожен список, як його відображено на веб-сторінці, тому він також працює для всіх переглядів пісень / альбомів / виконавців у бібліотеці. Я перерахував ще два варіанти в кінці цієї відповіді.
Перейдіть на сторінку: https://play.google.com/music/listen#/all (або ваш список відтворення)
Відкрийте консоль розробника (F12 для Chrome). Вставте код нижче в консоль.
Усі скребковані пісні зберігаються в allsongs
об’єкті, а текстова версія списку копіюється у буфер обміну. Рекомендую songsToText("all",true)
після цього запустити
повну інформацію про CSV. Запустити copy(outText)
вручну, якщо копіювання буфера обміну не спрацювало з першої спроби.
Код (остання версія 10 травня 2016 р., Rev 30):
var allsongs = []
var outText = "";
var songsToText = function(style, csv, likedonly){
if (style === undefined){
console.log("style is undefined.");
return;
}
var csv = csv || false; // defaults to false
var likedonly = likedonly || false; // defaults to false
if (likedonly) {
console.log("Only selecting liked songs");
}
if (style == "all" && !csv){
console.log("Duration, ratings, and playcount will only be exported with the CSV flag");
}
outText = "";
if (csv) {
if (style == "all") {
//extra line
outText = "artist,album,title,duration,playcount,rating,rating_interpretation" + "\n";
} else if (style == "artist") {
} else if (style == "artistsong") {
} else if (style == "artistalbum") {
} else if (style == "artistalbumsong") {
} else {
console.log("style not defined");
}
}
var numEntries = 0;
var seen = {};
for (var i = 0; i < allsongs.length; i++) {
var curr = "";
var properTitle = allsongs[i].title.replace(/[\n\r!]/g, '').trim();
if (!likedonly || (likedonly && allsongs[i].rating >= 5)){
if (csv) {
if (style == "all") {
//extra line
curr += '"' + allsongs[i].artist.replace(/"/g, '""').trim() + '"' + ",";
curr += '"' + allsongs[i].album.replace(/"/g, '""').trim() + '"' + ",";
curr += '"' + properTitle.replace(/"/g, '""').trim() + '"' + ",";
curr += '"' + allsongs[i].duration.replace(/"/g, '""').trim() + '"' + ",";
curr += '"' + allsongs[i].playcount.replace(/"/g, '""').trim() + '"' + ",";
curr += '"' + allsongs[i].rating.replace(/"/g, '""').trim() + '"' + ",";
curr += '"' + allsongs[i].rating_interpretation.replace(/"/g, '""').trim() + '"';
} else if (style == "artist") {
curr += '"' + allsongs[i].artist.replace(/"/g, '""').trim() + '"';
} else if (style == "artistsong") {
curr += '"' + allsongs[i].artist.replace(/"/g, '""').trim() + '"' + ",";
curr += '"' + properTitle.replace(/"/g, '""').trim() + '"';
} else if (style == "artistalbum") {
curr += '"' + allsongs[i].artist.replace(/"/g, '""').trim() + '"' + ",";
curr += '"' + allsongs[i].album.replace(/"/g, '""').trim() + '"';
} else if (style == "artistalbumsong") {
curr += '"' + allsongs[i].artist.replace(/"/g, '""').trim() + '"' + ",";
curr += '"' + allsongs[i].album.replace(/"/g, '""').trim() + '"' + ",";
curr += '"' + properTitle.replace(/"/g, '""').trim() + '"';
} else {
console.log("style not defined");
}
} else {
if (style == "all"){
curr = allsongs[i].artist + " - " + allsongs[i].album + " - " + properTitle + " [[playcount: " + allsongs[i].playcount + ", rating: " + allsongs[i].rating_interpretation + "]]" ;
} else if (style == "artist"){
curr = allsongs[i].artist;
} else if (style == "artistalbum"){
curr = allsongs[i].artist + " - " + allsongs[i].album;
} else if (style == "artistsong"){
curr = allsongs[i].artist + " - " + properTitle;
} else if (style == "artistalbumsong"){
curr = allsongs[i].artist + " - " + allsongs[i].album + " - " + properTitle;
} else {
console.log("style not defined");
}
}
if (!seen.hasOwnProperty(curr)){ // hashset
outText = outText + curr + "\n";
numEntries++;
seen[curr] = true;
} else {
//console.log("Skipping (duplicate) " + curr);
}
}
}
console.log("=============================================================");
console.log(outText);
console.log("=============================================================");
try {
copy(outText);
console.log("copy(outText) to clipboard succeeded.");
} catch (e) {
console.log(e);
console.log("copy(outText) to clipboard failed, please type copy(outText) on the console or copy the log output above.");
}
console.log("Done! " + numEntries + " lines in output. Used " + numEntries + " unique entries out of " + allsongs.length + ".");
};
var scrapeSongs = function(){
var intervalms = 1; //in ms
var timeoutms = 3000; //in ms
var retries = timeoutms / intervalms;
var total = [];
var seen = {};
var topId = "";
document.querySelector("#mainContainer").scrollTop = 0; //scroll to top
var interval = setInterval(function(){
var songs = document.querySelectorAll("table.song-table tbody tr.song-row");
if (songs.length > 0) {
// detect order
var colNames = {
index: -1,
title: -1,
duration: -1,
artist: -1,
album: -1,
playcount: -1,
rating: -1
};
for (var i = 0; i < songs[0].childNodes.length; i++) {
colNames.index = songs[0].childNodes[i].getAttribute("data-col") == "index" ? i : colNames.index;
colNames.title = songs[0].childNodes[i].getAttribute("data-col") == "title" ? i : colNames.title;
colNames.duration = songs[0].childNodes[i].getAttribute("data-col") == "duration" ? i : colNames.duration;
colNames.artist = songs[0].childNodes[i].getAttribute("data-col") == "artist" ? i : colNames.artist;
colNames.album = songs[0].childNodes[i].getAttribute("data-col") == "album" ? i : colNames.album;
colNames.playcount = songs[0].childNodes[i].getAttribute("data-col") == "play-count" ? i : colNames.playcount;
colNames.rating = songs[0].childNodes[i].getAttribute("data-col") == "rating" ? i : colNames.rating;
}
// check if page has updated/scrolled
var currId = songs[0].getAttribute("data-id");
if (currId == topId){ // page has not yet changed
retries--;
scrollDiv = document.querySelector("#mainContainer");
isAtBottom = scrollDiv.scrollTop == (scrollDiv.scrollHeight - scrollDiv.offsetHeight)
if (isAtBottom || retries <= 0) {
clearInterval(interval); //done
allsongs = total;
console.log("Got " + total.length + " songs and stored them in the allsongs variable.");
console.log("Calling songsToText with style all, csv flag true, likedonly false: songsToText(\"all\", false).");
songsToText("artistalbumsong", false, false);
}
} else {
retries = timeoutms / intervalms;
topId = currId;
// read page
for (var i = 0; i < songs.length; i++) {
var curr = {
dataid: songs[i].getAttribute("data-id"),
index: (colNames.index != -1 ? songs[i].childNodes[colNames.index].textContent : ""),
title: (colNames.title != -1 ? songs[i].childNodes[colNames.title].textContent : ""),
duration: (colNames.duration != -1 ? songs[i].childNodes[colNames.duration].textContent : ""),
artist: (colNames.artist != -1 ? songs[i].childNodes[colNames.artist].textContent : ""),
album: (colNames.album != -1 ? songs[i].childNodes[colNames.album].textContent : ""),
playcount: (colNames.playcount != -1 ? songs[i].childNodes[colNames.playcount].textContent : ""),
rating: (colNames.rating != -1 ? songs[i].childNodes[colNames.rating].getAttribute("data-rating") : ""),
rating_interpretation: "",
}
if(curr.rating == "undefined") {
curr.rating_interpretation = "never-rated"
}
if(curr.rating == "0") {
curr.rating_interpretation = "not-rated"
}
if(curr.rating == "1") {
curr.rating_interpretation = "thumbs-down"
}
if(curr.rating == "5") {
curr.rating_interpretation = "thumbs-up"
}
if (!seen.hasOwnProperty(curr.dataid)){ // hashset
total.push(curr);
seen[curr.dataid] = true;
}
}
songs[songs.length-1].scrollIntoView(true); // go to next page
}
}
}, intervalms);
};
scrapeSongs();
// for the full CSV version you can now call songsToText("all", true);
Останній код на Github (Gist) тут: https://gist.github.com/jmiserez/c9a9a0f41e867e5ebb75
Якщо ви хочете, щоб вихід був у текстовому форматі, можете зателефонувати на функцію songsToText (). Ви можете вибрати стиль, вибрати формат, і якщо потрібно експортувати лише вподобані / обдумані пісні. Потім отриманий список буде вставлений у буфер обміну. Стилі all
, artist
, artistalbum
, artistsong
,
artistalbumsong
. CSV призведе до файлу CSV і його можна буде залишити (за замовчуванням до значення false). Likedonly може бути залишений (за замовчуванням до false) або встановлено значення true, і буде фільтрувати всі пісні з рейтингами, більшими або рівними 5. Наприклад:
songsToText("all",true,false)
експортує всі пісні у форматі CSV.
songsToText("all",true,true)
експортуватиме лише улюблені пісні у форматі csv.
songsToText("artistsong",false,false)
експортує всі пісні як текст.
Потім ви можете вставити дані куди завгодно, наприклад, http://www.ivyishere.org/, якщо ви хочете додати пісні або альбоми до свого облікового запису Spotify. Щоб Ivy розпізнав цілі альбоми, використовуйте стиль "artistalbum". Для пісень використовуйте стиль "виконавців".
Про фрагмент:
Це засновано на оригінальній відповіді Майкла Сміта, але є трохи більш надійною. Я вніс такі вдосконалення:
Працює в списках відтворення, а також у бібліотеці. Будь-які стовпці, що відсутні, ігноруються, і порядок з’ясовується, тому він повинен працювати майже на будь-якому списку пісень у Google Music.
Він зупиняється або після досягнення нижньої частини (виявляє положення прокрутки), або після вказаного часу. Час очікування є для запобігання нескінченного циклу у випадку, якщо код виявлення прокрутки вимкнений на кілька пікселів.
Це набагато швидше (інтервал кожні 1 мс), але чекає, якщо дані не будуть готові (до вказаного часу, в даний час 3 секунди).
Робить дедуплікацію під час роботи та на виході.
Збирає рейтинги: "невизначений" ніколи не оцінюється, "0" не оцінюється (тобто один раз оцінюється, а потім видаляється), "1" є великими пальцями вниз, а "5" - великими пальцями вгору (подобається).
Окрім основних вдосконалень, він також добре форматує текст і копіює його у буфер обміну. Ви також можете отримати дані як CSV, якщо бажаєте, запустивши songsToText
функцію вдруге.
Альтернативи:
Якщо вам потрібен API Python, перегляньте неофіційний проект Google Music API .
Якщо у вас є багато списків відтворення, і ви хочете експортувати їх за один раз, спробуйте експортер плейлистів gmusic-script, який може це зробити (Python, використовує неофіційний проект API).