我想从我的Google Play音乐帐户中打印歌曲列表(包括歌手,专辑,评分以及播放次数和时长)。
没有简单的方法可以通过应用程序执行此操作。当我翻阅一长串歌曲时做打印屏幕是站不住脚的。
我很乐意将数据导出为可以自己操作的标准格式(纯文本,CSV,XML等)。
有什么建议么?
我想从我的Google Play音乐帐户中打印歌曲列表(包括歌手,专辑,评分以及播放次数和时长)。
没有简单的方法可以通过应用程序执行此操作。当我翻阅一长串歌曲时做打印屏幕是站不住脚的。
我很乐意将数据导出为可以自己操作的标准格式(纯文本,CSV,XML等)。
有什么建议么?
Answers:
修改darkliquid的答案后,我想到了以下内容,该内容可以一次保存多个播放列表。
JSON.stringify(tracklistObj, null, '\t')
(如果希望最小缩进'\t'
,' '
则将更改为),或者tracklistObj
仅希望JavaScript对象以自己的方式对其进行操作。如果要对其排序,请Object.values(tracklistObj).forEach(a => a.sort())
在调用命令之前运行该JSON.stringify
命令。在完成所有想做的操作之前,请不要刷新页面,否则必须从步骤1重新启动。
// Setup
var tracklistObj = {},
currentPlaylist,
checkIntervalTime = 100,
lastTime;
// Process the visible tracks
function getVisibleTracks() {
var playlist = document.querySelectorAll('.song-table tr.song-row');
for(var i = 0; i < playlist.length ; i++) {
var l = playlist[i];
var title = l.querySelector('td[data-col="title"] .column-content');
if(title !== null)
title = title.textContent;
var artist = l.querySelector('td[data-col="artist"] .column-content');
if(artist !== null)
artist = artist.textContent;
var duration = l.querySelector('td[data-col="duration"] span');
if(duration !== null)
duration = duration.textContent;
var album = l.querySelector('td[data-col="album"] .column-content');
if(album !== null)
album = album.textContent;
var playCount = l.querySelector('td[data-col="play-count"] span');
if(playCount !== null)
playCount = playCount.textContent;
var rating = l.querySelector('td[data-col="rating"]');
if(rating !== null)
rating = rating.textContent;
// Add it if it doesn't exist already
if(tracklistObj[currentPlaylist] && !tracklistObj[currentPlaylist].includes(artist + " - " + title)) {
tracklistObj[currentPlaylist].push(artist + " - " + title);
if(printTracksToConsole) {
console.log(artist + ' - ' + title);
}
}
}
}
// Listen for page changes
window.onhashchange = function(e) {
currentPlaylist = null;
var doneLoading = setInterval(function() {
var playListName = document.querySelector('.gpm-detail-page-header h2[slot="title"]');
if(playListName != null) {
currentPlaylist = playListName.innerText;
if(tracklistObj[currentPlaylist] === undefined) {
tracklistObj[currentPlaylist] = [];
}
console.log("===================================");
console.log("Adding to playlist " + currentPlaylist);
getVisibleTracks();
clearInterval(doneLoading);
}
}, 100);
}
// Check for new tracks every so often
setInterval(function() {
getVisibleTracks();
}, checkIntervalTime);
// Whether or not to print the tracks obtained to the console
var printTracksToConsole = false;
您还可以打印出来曲目名称到控制台,你去改变printTracksToConsole
,以true
(您第3步之前应该这样做)。
请注意,您可能会忽略控制台中的所有GET和POST错误(这些错误是由Play音乐本身生成的,而不是此脚本生成的)。
还要注意的是目前它的设置只给Artist - Track name
,但您可以轻松编辑已行tracklistObj[currentPlaylist].push(artist + " - " + title);
用album
,playCount
,duration
,或者rating
,和/或任何格式化你想要的(包括CSV格式,如果你那么请)。
带有默认设置的示例输出(我目前拥有的所有Google Play播放列表)。导航到32个播放列表中的每一个,向下滚动它们,然后将结果转换为文本,总共花费了大约5分钟。
PS:您可能会喜欢使用我发现的一个名为Tune My Music的网站从输出中制作YouTube播放列表(但YouTube将播放列表的创建限制为每天10个),以便您的朋友可以收听您的Google播放列表。如果这样做,可能要使用TextMechanic之类的东西来删除引号和.mp3
从输出列表中删除。
(已更新2016-05-09,比当前的最佳答案更可靠)
如果您只需要保存一些播放列表,则可以使用下面的我的Javascript代码段。此代码段可以保存网页上显示的每个列表,因此它也适用于所有歌曲/专辑/艺术家库视图。在此答案的结尾,我列出了其他两个选择。
转到:https : //play.google.com/music/listen#/all(或您的播放列表)
打开开发者控制台(适用于Chrome的F12)。将以下代码粘贴到控制台中。
所有抓取的歌曲都存储在allsongs
对象中,列表的文本版本被复制到剪贴板。我建议songsToText("all",true)
稍后再运行
以获取完整的CSV信息。copy(outText)
如果剪贴板复制在第一次尝试中不起作用,请手动运行。
代码(最新版本,2016年5月10日,修订版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)
将所有歌曲导出为文本。然后,如果要将歌曲或专辑添加到Spotify帐户中,则可以将数据粘贴到任意位置,例如http://www.ivyishere.org/。为了使常春藤识别完整的专辑,请使用“ artistalbum”风格。对于歌曲,请使用“ artistong”风格。
关于摘要: 这是基于Michael Smith的原始答案,但功能更强大。我做了以下改进:
适用于播放列表和库。所有遗漏的列都会被忽略,并且顺序会确定,因此它应该可以在Google音乐中的几乎所有歌曲列表上使用。
它到达底部(检测滚动位置)时或在指定的超时后停止。如果滚动检测代码偏离了几个像素,则存在超时以防止无限循环。
它的速度要快得多(每1毫秒间隔一次),但是会在数据未准备好时等待(直到指定的超时时间,目前为3秒)。
在操作期间和在输出上进行重复数据删除。
收集评级:“未定义”从未评级,“ 0”未被评级(即曾经评级但随后被删除),“ 1”表示不赞成,“ 5”表示不赞成(喜欢)。
除了基本的改进之外,它还可以很好地格式化文本并将其复制到剪贴板。如果需要,还可以通过songsToText
第二次运行该功能以CSV格式获取数据。
备择方案:
如果您需要Python API,请查看非正式的Google Music API项目。
如果您有大量的播放列表,并且想一次性导出所有播放列表,请尝试使用gmusic-scripts播放列表导出器(Python,使用非官方的API项目)。
songsToText("all", true)
稍后运行。
如果您不介意在浏览器开发者控制台中运行一些javascript代码,则可以像这样从页面中提取信息(仅在Chrome中测试):
var playlist = document.querySelectorAll('.song-table tr.song-row');
for(var i =0; i<playlist.length ; i++) {
var l = playlist[i];
var title = l.querySelector('td[data-col="title"] .column-content').textContent;
var artist = l.querySelector('td[data-col="artist"] .column-content').textContent;
var album = l.querySelector('td[data-col="album"] .column-content').textContent;
console.log(artist + ' --- ' + title + ' --- ' + album);
}
这将在控制台中打印出窗口中大多数当前可见歌曲的列表。您需要向下滚动并重新运行以获得更多内容。目前,我还没有找到一种完整的方式来获取信息,但是这种5分钟的快速破解总比没有好。
querySelector(...)
代替querySelectorAll(...)[0]
使用当时的最高答案并想要一个完整的解决方案,我创建了以下代码,该代码向下滚动音乐列表,并向数组中添加JSON对象。
由于不确切知道哪些歌曲可见,因此代码将所有歌曲添加在一起,然后在最后进行重复数据删除。(仅在Chrome中测试。)
用法:转到您的音乐库,在其中查看完整的歌曲列表,然后运行
var total = [];
var interval = setInterval(function(){
var songs = document.querySelectorAll("table.song-table tbody tr.song-row");
for (var i = 0; i < songs.length; i++) {
total.push({name: songs[i].childNodes[0].textContent,
length: songs[i].childNodes[1].textContent,
artist: songs[i].childNodes[2].textContent,
album: songs[i].childNodes[3].textContent,
plays: songs[i].childNodes[4].textContent
});
songs[i].scrollIntoView(true);
}
}, 800);
当该内容到达页面底部时,请运行此命令以停止滚动,删除重复数组并将JSON复制到剪贴板。
clearInterval(interval);
for (var i = 0; i < total.length; i++) {
for (var j = i + 1; j < total.length; j++) {
if (total.hasOwnProperty(i) && total.hasOwnProperty(j) && total[i].name == total[j].name && total[j].artist == total[i].artist) {
total.splice(j,1);
}
}
}
copy(total);
我有一些更短的JavaScript,可以粘贴到控制台中。无需重新运行代码,您只需向下滚动并添加所有可见的相册。然后,您可以将播放列表下载为电子表格。
前往这里:https://play.google.com/music/listen#/ap/auto-playlist-thumbs-up
打开开发人员工具(F12),然后将以下代码粘贴到“ 控制台”选项卡中
滚动查看,以便播放列表中的每个专辑至少可见一次
双击页面上的某处进行下载 export-google-play.csv
export-google-play.csv
在Excel中打开。
alert("Please scroll through the playlist so that each album is visible once.\n" +
"Then double-click the page to export a spreadsheet.");
var albums = ["Artist,Album,Purchased"];
var addVisibleAlbums = function(){
[].forEach.call(document.querySelectorAll(".song-row"), function(e){
var albumNodes = [e.querySelector("td[data-col='artist']"),
e.querySelector("td[data-col='album']"),
e.querySelector("td[data-col='title'] .title-right-items")];
var albumString = albumNodes.map(function(s){
return s.innerText.trim().replace(/,/g,"");
}).join(",");
if(albums.indexOf(albumString) === -1){
albums.push(albumString); console.log("Added: " + albumString)
}
});
}
var createCsv = function(){
var csv = "data:text/csv;charset=utf-8,";
albums.forEach(function(row){ csv += row + "\n"; });
var uri = encodeURI(csv);
var link = document.createElement("a");
link.setAttribute("href", uri);
link.setAttribute("download", "export-google-play.csv");
document.body.appendChild(link);
link.click();
alert("Download beginning!")
}
document.body.addEventListener("DOMNodeInserted", addVisibleAlbums, false);
document.body.addEventListener("dblclick", createCsv, false);
我对最佳答案的方法做了一些修改。使用Ivy的复制/粘贴方法(http://www.ivyishere.org/ivy),这对我来说效果更好:
步骤1在Chrome中从Google音乐打开所需的播放列表,然后将其粘贴到控制台中:
document.querySelector('body.material').style.height = (document.querySelector('table.song-table tbody').getAttribute('data-count') * 100) + 'px';
这将导致整个播放列表而不是一部分呈现。
步骤2将此脚本粘贴到控制台中:
var i, j, playlistString = '', playlist = document.querySelectorAll('.song-table tr.song-row');
for (i = 0, j = playlist.length; i < j; i++) {
var track = playlist[i];
var artist = track.querySelector('[href][aria-label]').textContent;
var title = track.querySelector('td[data-col="title"]').textContent;
playlistString += ('"' + artist + '", "' + title + '"\n');
}
console.log(playlistString);
步骤3转到常春藤,当您到达那里的步骤2时,选择“复制/粘贴”选项卡并将控制台输出粘贴到那里。
编辑
更新了Alex Pedersen建议的脚本
反复进行samurauturetsky的优化(我的声誉尚不足以对他的帖子发表评论)。我认为Googleplay样式已更新,因此下面的脚本再次提供了漂亮的输出。
var i, j, playlistString = '', playlist = document.querySelectorAll('.song-table tr.song-row');
for (i = 0, j = playlist.length; i < j; i++) {
var track = playlist[i];
var artist = track.querySelector('[href][aria-label]').textContent;
var title = track.querySelector('span[class="column-content fade-out tooltip"]').textContent;
playlistString += ('"' + artist + '", "' + title + '"\n');
}
console.log(playlistString);
我刚遇到这个问题,正在寻找类似的东西。
我想,您最好的选择是: