您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Lists all of your reviews with vote and comment tallies, with updates highlighted
当前为
/*===========================================================================*\ | The Amazon Review Tabulator - TART | | (c) 2016 by Another Floyd | | From your "Public Reviews Written by You" page on Amazon, this script | | collects and tabulates vote tallies and related information, from all of | | your Amazon reviews. Click the "Tabulate" link in the "Your Profile" | | panel. See item on script host menu, on browser toolbar, to toggle | | Display Mode, between using the Amazon tab (default), or showing in a | | new tab, but with fewer features. | \*===========================================================================*/ // ==UserScript== // @name The Amazon Review Tabulator - TART // @namespace floyd.scripts // @version 1.2.0 // @author Another Floyd at Amazon.com // @description Lists all of your reviews with vote and comment tallies, with updates highlighted // @include https://www.amazon.com/gp/cdp/member-reviews/* // @include https://www.amazon.co.uk/gp/cdp/member-reviews/* // @include https://www.amazon.ca/gp/cdp/member-reviews/* // @include https://www.amazon.com.au/gp/cdp/member-reviews/* // @grant GM_getValue // @grant GM_setValue // @grant GM_xmlhttpRequest // @grant GM_log // @grant GM_openInTab // @grant GM_registerMenuCommand // @require https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js // @require https://gf.qytechs.cn/scripts/20744-sortable/code/sortable.js?version=132520 // ==/UserScript== // Start (function() { var enhancedDisplayMode = true; var showUpdatesOnly = false; var primaryDisplayBuffer = ""; var updateDisplayBuffer = ""; var userID = ""; var reviewCount = 0; var reviewerRanking = ""; var helpfulVotes = 0; var oldStoreItemIDs = []; var oldStoreUpvotes = []; var oldStoreDownvotes = []; var oldStoreComments = []; var newStoreItemIDs = ""; var newStoreUpvotes = ""; var newStoreDownvotes = ""; var newStoreComments = ""; var tallyUpvotes = 0; var tallyDownvotes = 0; var tallyStars = 0; var tallyComments = 0; // use this reference for progress indicator var profileDiv = ""; var profileDivOriginalHTML = ""; var profileDivTabulateHTML = "<br></br><a href='javascript:tabulate();'>Tabulate</a>"; function tabulate() { // reset global accumulators to ensure that repeated script runs remain clean newStoreItemIDs = ""; newStoreUpvotes = ""; newStoreDownvotes = ""; newStoreComments = ""; tallyUpvotes = 0; tallyDownvotes = 0; tallyStars = 0; tallyComments = 0; var today = new Date(); var formattedToday = today.toLocaleDateString('en-US',{month:'long',day:'numeric',year:'numeric'}); var toggleLink = (enhancedDisplayMode) ? "<p><a href='javascript:toggleView();'>Toggle View: All Reviews | Updates Only</a>" : ""; // set up top of display page primaryDisplayBuffer = "<!DOCTYPE html><html lang='en'>" + "<head><meta charset='utf-8'/><title>TART Amazon Review Details</title>" + "<style type='text/css'>" + "body {font-family:Arial, sans-serif; font-size:12px}" + ".tg {border-collapse:collapse;border-spacing:0;width:100%}" + ".tg td{padding:10px 5px;border-style:solid;border-width:1px;overflow:hidden;word-break:normal}" + ".tg th{padding:10px 5px;border-style:solid;border-width:1px;overflow:hidden;word-break:normal}" + ".tg .header-left{font-weight:bold;background-color:#010066;color:#ffffff;text-align:left}" + ".tg .header-right{font-weight:bold;background-color:#010066;color:#ffffff;text-align:right}" + ".tg .cell-left{text-align:left}" + ".tg .cell-right{text-align:right}" + ".tg .hilite-left{text-align:left;background-color:#FFFF12}" + ".tg .hilite-right{text-align:right;background-color:#FFFF12}" + ".txtLarge {font-size:18px;font-weight:bold}" + ".tableLink, .tableLink:link, .tableLink:visited {text-decoration:none;font-weight:bold;color:#000000}" + ".footerLink, .footerLink:link, .footerLink:visited {text-decoration:none;font-weight:bold;color:#ffffff}" + "table.sortable th.sorted {background-color:#000000}" + "</style></head><body>" + "<span class='txtLarge'>Amazon Review Details</span><br>" + "Prepared with <a href='https://gf.qytechs.cn/en/scripts/24434-the-amazon-review-tabulator-tart' target='_new'>TART</a> - " + formattedToday + "<p>Reviewer Ranking: " + reviewerRanking + "<br>" + "Review Count: " + reviewCount + "<br>" + "Helpful Votes: " + helpfulVotes + "<br>" + "Upvote/Review Ratio: " + (helpfulVotes/reviewCount).toFixed(2) + toggleLink + "</p><table class='tg sortable'>" + "<thead><tr>" + "<th class='header-left sort-number sort-default'>#</th>" + "<th class='header-left sort-text'>Item</th>" + "<th class='header-left sort-date'>Date</th>" + "<th class='header-right sort-number'>Stars<br></th>" + "<th class='header-right sort-number'>Upvotes</th>" + "<th class='header-right sort-number'>Downvotes</th>" + "<th class='header-right sort-number'>% Helpful<br></th>" + "<th class='header-right sort-number'>Comments</th></tr></thead><tbody>"; updateDisplayBuffer = primaryDisplayBuffer; // for alternate display mode // read in stored info from past run, for use in change detection oldStoreItemIDs = GM_getValue("recentItemIDs", "").split(" "); oldStoreUpvotes = GM_getValue("recentUpvotes", "").split(" "); oldStoreDownvotes = GM_getValue("recentDownvotes", "").split(" "); oldStoreComments = GM_getValue("recentComments", "").split(" "); // prepare url with country domain and user ID, ready for review page number var tld = "com"; var url = window.location.href; if(url.indexOf('amazon.co.uk/') > -1) tld = "co.uk"; if(url.indexOf('amazon.ca/') > -1) tld = "ca"; if(url.indexOf('amazon.com.au/') > -1) tld = "com.au"; var urlStart = "https://www.amazon." + tld + "/gp/cdp/member-reviews/" + userID + "?ie=UTF8&display=public&page="; var urlEnd = "&sort_by=MostRecentReview"; // space and counters for incoming data var perPageResponseDiv = []; var pageSetOfTableRows = []; var pageResponseCount = 0; var reviewsProcessed = 0; var pageCount = Math.floor(reviewCount / 10) + ((reviewCount % 10 > 0) ? 1 : 0); //var pageCount = 2; // for testing // initialize the progress indicator // sort of pre-redundant to do this here AND in the loop, but, // looks better, if there is a lag before the first response var progressHTML = "<br></br><b>" + pageCount + "</b>"; profileDiv.innerHTML = profileDivOriginalHTML + progressHTML; // download and process Amazon pages var x = 1; while (x <= pageCount) { (function(x){ var urlComplete = urlStart + x + urlEnd; perPageResponseDiv[x] = document.createElement('div'); GM_xmlhttpRequest({ method: "GET", url: urlComplete, onload: function(response) { // capture incoming data perPageResponseDiv[x].innerHTML = response.responseText; pageResponseCount++; // update the progress indicator var progressHTML = "<br></br><b>" + (pageCount - pageResponseCount) + "</b>"; profileDiv.innerHTML = profileDivOriginalHTML + progressHTML; // get parent of any reviewText DIV var findReviews = document.evaluate("//div[@class='reviewText']/..", perPageResponseDiv[x], null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); // evaluating the doc DIV made above // process each review found on current page pageSetOfTableRows[x] = ""; // initialize each member prior to concatenating for (var j = 0; j < findReviews.snapshotLength; j++) { var oneReview = findReviews.snapshotItem(j); var reviewChildren = oneReview.children; var childCount = reviewChildren.length; var commentCount = 0; var itemTitle = "No Title Available"; var itemLink = ""; var permaLink = ""; var starRating = 0; var reviewDate = ""; var upVotes = 0; var downVotes = 0; var itemID = ""; // get number of comments, and permalink var tempText = reviewChildren[childCount-2].textContent; if(tempText.indexOf('Comment (') > -1 || tempText.indexOf('Comments (') > -1) { var paren1 = tempText.indexOf('('); var paren2 = tempText.indexOf(')'); commentCount = tempText.substring(paren1+1,paren2); commentCount = parseInt(commentCount.replace(/,/g, '')); // remove commas } var lst = reviewChildren[childCount-2].getElementsByTagName('a'); permaLink = lst[2].getAttribute("href"); // the data items below do not have reliable positions, due to presence // or not, of vine voice tags, verified purchase, votes, etc. // so, are done in a loop with IF checks. Must start loop just above review // text, in case the reviewer has used any of the phrases I am searching for for (var i = childCount - 4; i > -1; i--) { var childHTML = reviewChildren[i].innerHTML; // used 2x, below // get item title and item link var titleClue = childHTML.indexOf('This review is from'); if(titleClue > -1) { var lst = reviewChildren[i].getElementsByTagName('a'); itemLink = lst[0].getAttribute("href"); itemTitle = lst[0].textContent; } // get star rating AND review date var ratingClue = childHTML.indexOf('out of 5 stars'); if(ratingClue > -1) { starRating = childHTML.substring(ratingClue-4,ratingClue-1); reviewDate = reviewChildren[i].lastElementChild.textContent; } // get vote counts var childText = reviewChildren[i].textContent; var voteClue = childText.indexOf('people found the following review helpful'); if(voteClue > -1) { var list = childText.trim().split(" "); // there were extra, invisible spaces! upVotes = parseInt(list[0].replace(/,/g, '')); // remove commas var totalVotes = parseInt(list[2].replace(/,/g, '')); downVotes = totalVotes - upVotes; } } // get item ID var lst = oneReview.parentNode.getElementsByTagName('a'); itemID = lst[0].getAttribute("name"); // get HTML formatted table row pageSetOfTableRows[x] += prepOneTableRow((j+1+(x-1)*10),itemID,itemTitle,permaLink,reviewDate,starRating,upVotes,downVotes,commentCount); // clear the response, to save memory -- // could be critical when there are many review pages perPageResponseDiv[x].innerHTML = ""; reviewsProcessed++; // more reliable than reviewCount, for calculating avg. rating } // see if all data from multiple page loads has arrived if(pageResponseCount==pageCount) { // assemble the sets of table rows for(var y=1; y <= pageCount; y++) { primaryDisplayBuffer += pageSetOfTableRows[y]; } // add footer and complete the results page var calcStars = (tallyStars/reviewsProcessed).toFixed(1); var calcHelpfulPct = helpfulPercent(tallyUpvotes,tallyDownvotes); var newStoreFooter = calcStars + " " + tallyUpvotes + " " + tallyDownvotes + " " + calcHelpfulPct + " " + tallyComments; var oldStoreFooter = GM_getValue("recentFooterValues", " ").split(" "); primaryDisplayBuffer += "</tbody><tfoot><tr><td class='header-left'></td><td class='header-left'></td><td class='header-left'></td>" + "<td class='header-right'><a href='javascript: void(0)' class='footerLink' title='Previous: " + oldStoreFooter[0] + "'>" + calcStars + "</a></td>" + "<td class='header-right'><a href='javascript: void(0)' class='footerLink' title='Previous: " + oldStoreFooter[1] + "'>" + tallyUpvotes + "</a></td>" + "<td class='header-right'><a href='javascript: void(0)' class='footerLink' title='Previous: " + oldStoreFooter[2] + "'>" + tallyDownvotes + "</a></td>" + "<td class='header-right'><a href='javascript: void(0)' class='footerLink' title='Previous: " + oldStoreFooter[3] + "'>" + calcHelpfulPct + "</a></td>" + "<td class='header-right'><a href='javascript: void(0)' class='footerLink' title='Previous: " + oldStoreFooter[4] + "'>" + tallyComments + "</a></td>" + "</tr></tfoot></table></body></html>"; // get rows containing updated reviews, only var tempDiv = document.createElement('div'); tempDiv.innerHTML = primaryDisplayBuffer; var findUpdateRows = document.evaluate("//td[@class='hilite-left']/..", tempDiv, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); for(var d = 0; d < findUpdateRows.snapshotLength; d++) { updateDisplayBuffer += findUpdateRows.snapshotItem(d).outerHTML; } updateDisplayBuffer += "</tbody></table></body></html>"; // store info to be used in subsequent run, for change detection GM_setValue("recentItemIDs", newStoreItemIDs.trim()); GM_setValue("recentUpvotes", newStoreUpvotes.trim()); GM_setValue("recentDownvotes", newStoreDownvotes.trim()); GM_setValue("recentComments", newStoreComments.trim()); GM_setValue("recentFooterValues", newStoreFooter.trim()); // replace progress indicator with Tabulate link profileDiv.innerHTML = profileDivOriginalHTML + profileDivTabulateHTML; // --- a display alternative - put results IN the Amazon page //document.body.innerHTML = primaryDisplayBuffer; // --- to open new window, user must allow popups for https://www.amazon.com //var resultsWindow = window.open("data:text/html," + encodeURIComponent(primaryDisplayBuffer), "_blank", "scrollbars=yes"); // --- using GM_openInTab does not require exception to be set by user if(!enhancedDisplayMode) GM_openInTab("data:text/html," + encodeURIComponent(primaryDisplayBuffer)); else document.body.innerHTML = primaryDisplayBuffer; } } }); })(x); x++; } } function displayModeToggle() { enhancedDisplayMode = !enhancedDisplayMode; GM_setValue("AltDisplayMode", enhancedDisplayMode); alert("Enhanced Display Mode = " + enhancedDisplayMode); } function toggleView() { showUpdatesOnly = !showUpdatesOnly; if(showUpdatesOnly) document.body.innerHTML = updateDisplayBuffer; else document.body.innerHTML = primaryDisplayBuffer; } function helpfulPercent(upVotes,downVotes) { var helpfulPercent = ""; upVotes = upVotes; downVotes = downVotes; if(upVotes + downVotes > 0) helpfulPercent = (upVotes/(upVotes+downVotes)*100).toFixed(1); return helpfulPercent; } function prepOneTableRow (row,itemID,itemTitle,permaLink,reviewDate,starRating,upVotes,downVotes,commentCount) { // do these before mangling the values with <b> tags </b> var helpfulPct = helpfulPercent(upVotes,downVotes); itemTitle = "<a href='" + permaLink + "' target='_new'>" + itemTitle.substring(0,65) + "</a>"; // keep tallies to use in table footer tallyUpvotes += upVotes; tallyDownvotes += downVotes; tallyStars += parseInt(starRating); tallyComments += commentCount; // assemble storage info, to use in subsequent run, for change detection newStoreItemIDs += itemID + " "; newStoreUpvotes += upVotes + " "; newStoreDownvotes += downVotes + " "; newStoreComments += commentCount + " "; // see if review for this item has previously been examined var matchIdx = -1; for(var i=0; i<oldStoreItemIDs.length; i++) { if(oldStoreItemIDs[i] == itemID) { // we have a match, an item that has previously been seen matchIdx = i; break; } } var hiliteRow = false; if(matchIdx > -1) { // entry exists; see if any of the numbers have changed if(oldStoreUpvotes[matchIdx] != upVotes) { // for changed number, make it bold, and hilite row // and store previous value for display as tooltip, for mouse hover upVotes = "<a href='javascript: void(0)' class='tableLink' title='Previous: " + oldStoreUpvotes[matchIdx] + "'>" + upVotes + "</a>"; hiliteRow = true; } if(oldStoreDownvotes[matchIdx] != downVotes) { downVotes = "<a href='javascript: void(0)' class='tableLink' title='Previous: " + oldStoreDownvotes[matchIdx] + "'>" + downVotes + "</a>"; hiliteRow = true; } if(oldStoreComments[matchIdx] != commentCount) { commentCount = "<a href='javascript: void(0)' class='tableLink' title='Previous: " + oldStoreComments[matchIdx] + "'>" + commentCount + "</a>"; hiliteRow = true; } } else { // no match, so, it's a new review; bold the title and hilite the row itemTitle = "<b>" + itemTitle + "</b>"; hiliteRow = true; } var tdLeft = "<td class='cell-left'>"; var tdRight = "<td class='cell-right'>"; if(hiliteRow===true && oldStoreItemIDs[0].length > 0) { tdLeft = "<td class='hilite-left'>"; tdRight = "<td class='hilite-right'>"; } var tableRow = "<tr>" + tdLeft + row + "</td>" + tdLeft + itemTitle + "</td>" + tdLeft + reviewDate + "</td>" + tdRight + starRating + "</td>" + tdRight + upVotes + "</td>" + tdRight + downVotes + "</td>" + tdRight + helpfulPct + "</td>" + tdRight + commentCount + "</td></tr>"; return tableRow; } document.addEventListener('click', function(event) { var tempstr = new String(event.target); if(tempstr.indexOf('tabulate') > -1) { tabulate(); event.stopPropagation(); event.preventDefault(); } if(tempstr.indexOf('toggleView') > -1) { toggleView(); event.stopPropagation(); event.preventDefault(); } }, true); function main() { // find reviewer ID var url = window.location.href; if(url.indexOf('amazon.com/') > -1) { // for Amazon US var findProfileLink = document.evaluate("//b[contains(.,'Your Profile')]/a", document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); } else { // Amazon UK, CA, AU var findProfileLink = document.evaluate("//a[contains(.,'Customer Reviews')]", document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); } // and, the following lines are ok for US and UK, but, maybe not for others var profileLink = findProfileLink.snapshotItem(0).getAttribute("href"); var lst = profileLink.split("/"); userID = lst[4]; // find profile info panel var findDiv = document.evaluate("//div[contains(.,'Helpful Votes')]", document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); profileDiv = findDiv.snapshotItem(0); // get reviewer ranking and helpful votes var lst = profileDiv.textContent.split(" "); reviewerRanking = lst[4].substring(9); helpfulVotes = lst[9].substring(7); // get review count var prevSibDiv = profileDiv.previousElementSibling; charIdx = prevSibDiv.textContent.lastIndexOf(':'); reviewCount = prevSibDiv.textContent.substring(charIdx+2); // add Tabulate link; also, save content for use with progress indicator profileDivOriginalHTML = profileDiv.innerHTML; profileDiv.innerHTML += profileDivTabulateHTML; // add item to the User Script Command menu, to toggle display mode GM_registerMenuCommand("TART Display Mode Toggle", displayModeToggle); enhancedDisplayMode = GM_getValue("AltDisplayMode", true); } main(); })(); // End
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址