(Actualizado 2016-05-09, más robusto que la respuesta principal actual)
Si solo necesita guardar algunas listas de reproducción, puede usar mi fragmento de Javascript a continuación. Este fragmento puede guardar cada lista tal como se muestra en la página web, por lo que también funciona para todas las vistas de la biblioteca de canciones / álbumes / artistas. He enumerado otras dos alternativas al final de esta respuesta.
Vaya a: https://play.google.com/music/listen#/all (o su lista de reproducción)
Abra una consola de desarrollador (F12 para Chrome). Pegue el código a continuación en la consola.
Todas las canciones raspadas se almacenan en el allsongs
objeto y una versión de texto de la lista se copia en el portapapeles. Recomiendo correr
songsToText("all",true)
después para obtener la información CSV completa. Ejecútelo copy(outText)
manualmente si la copia del portapapeles no funcionó en el primer intento.
Código (última versión 10 de mayo de 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);
Último código en Github (Gist) aquí: https://gist.github.com/jmiserez/c9a9a0f41e867e5ebb75
Si desea la salida en formato de texto, puede llamar a la función songsToText (). Puede seleccionar un estilo, elegir el formato y, si solo se deben exportar las canciones con me gusta / pulgar hacia arriba. La lista resultante se pegará en el portapapeles. Los estilos son all
, artist
, artistalbum
, artistsong
,
artistalbumsong
. CSV generará un archivo CSV y puede omitirse (el valor predeterminado es falso). Likedonly puede omitirse (por defecto es falso) o establecerse en verdadero, y filtrará todas las canciones con calificaciones mayores o iguales a 5. Por ejemplo:
songsToText("all",true,false)
exportará todas las canciones en formato csv.
songsToText("all",true,true)
exportará solo las canciones que le gustaron en formato csv.
songsToText("artistsong",false,false)
exportará todas las canciones como texto.
Luego puede pegar los datos en cualquier lugar que desee, por ejemplo http://www.ivyishere.org/ si desea agregar las canciones o álbumes a su cuenta de Spotify. Para que Ivy reconozca álbumes completos, use el estilo "artistalbum". Para las canciones, use el estilo "artistas".
Acerca del fragmento:
se basa en la respuesta original de Michael Smith, pero es un poco más robusto. He realizado las siguientes mejoras:
Funciona en listas de reproducción y en la biblioteca. Se ignoran las columnas que faltan y se determina el orden, por lo que debería funcionar en casi cualquier lista de canciones dentro de Google Music.
Se detiene cuando llega al final (detecta la posición de desplazamiento) o después del tiempo de espera especificado. El tiempo de espera está ahí para evitar un bucle sin fin en caso de que el código de detección de desplazamiento esté desactivado por unos pocos píxeles.
Es mucho más rápido (intervalo cada 1 ms), pero espera si los datos no están listos (hasta el tiempo de espera especificado, actualmente 3 segundos).
Hace deduplicación durante la operación y en la salida.
Reúne las calificaciones: "indefinido" nunca se califica, "0" no se califica (es decir, una vez calificado pero luego eliminado), "1" se rechaza y "5" se aprueba (me gusta).
Además de las mejoras básicas, también formatea bien el texto y lo copia al portapapeles. También puede obtener los datos como CSV si lo desea, ejecutando la songsToText
función por segunda vez.
Alternativas:
Si necesita una API de Python, consulte el proyecto no oficial de la API de Google Music .
Si tiene toneladas de listas de reproducción y desea exportarlas todas de una vez, pruebe el exportador de listas de reproducción gmusic-scripts que puede hacerlo (Python, utiliza el proyecto API no oficial).