Altrettanto frustrato dalla mancanza di opzioni di filtro / ordinamento decenti in Google Play e ispirato dal tuo suggerimento che uno script Greasemonkey potesse risolvere il problema, ho deciso di scriverne uno, che ho caricato su https://greasyfork.org/en/ script / 24667-google-play-review-review-rating-filter . Aggiunge cinque caselle di controllo alle pagine delle app su play.google.com che consentono di filtrare le recensioni con specifiche stelle. L'ho testato con Greasemonkey e Unified Script Injector in Firefox e Tampermonkey in Chrome.
Invece di riprodurre qui l'intero script, descriverò l'approccio adottato per coloro che potrebbero essere interessati. TL; DR: se desideri solo la soluzione, installa il componente aggiuntivo del browser appropriato e scarica lo script utente dal link sopra. Tieni presente che se desideri utilizzarlo sul tuo dispositivo Android stesso, probabilmente dovrai utilizzare Firefox con il componente aggiuntivo USI (e anche selezionare Richiedi sito desktop dal menu), poiché la maggior parte degli altri browser Android non supporta il componente aggiuntivo- ons o script utente e Greasemonkey attualmente non funziona in Firefox per Android - non funzionerà nell'app Google Play.
Mentre sfogli le recensioni, GP (Google Play) carica i dati per ulteriori recensioni tramite richieste AJAX all'URL /store/getreviews
utilizzando il POST
metodo HTTP . Quindi agganciando queste chiamate AJAX, è possibile modificare i dati restituiti al GP.
XMLHttpRequest.prototype.open
può essere sostituito con una funzione che chiamerà l'originale, ma innanzitutto, se la richiesta è per i dati di revisione, modificare l' XMLHttpRequest
oggetto XHR ( ) in modo che il POST
corpo della richiesta possa essere catturato e la risposta modificata. Una send
proprietà può essere assegnata all'oggetto XHR come una funzione che memorizzerà i POST
dati prima di chiamare l'originale. La onreadystatechange
proprietà può essere assegnata come una funzione che modificherà la risposta prima di chiamare la funzione assegnata dal GP a questa proprietà. Come GP assegnerà onreadystatechange
dopo questo, Object.defineProperty
dovrebbe essere usato per ridefinire la proprietà in modo che il valore GP set sia memorizzato piuttosto che essere effettivamente assegnato alla proprietà interna. E poiché la responseText
proprietà è di sola lettura, Object.defineProperty
sarebbe necessario modificarne il valore.
I dati restituiti da GP sono in formato JSON, sebbene all'inizio abbiano alcuni caratteri di spazzatura che dovrebbero essere riprodotti fedelmente in tutti i dati modificati.
Il seguente codice lo dimostra e genererà nella finestra della console per gli sviluppatori del browser il corpo della richiesta e i dati di risposta (anche se in realtà non lo modifica):
XMLHttpRequest.prototype.open = (function(open) {
return function(method, url) {
if (
method === 'POST' &&
url &&
url.replace(/^https?:\/\/play\.google\.com/, '').split('?', 1)[0] ===
'/store/getreviews'
) {
var requestBody;
var orgSend = this.send;
var orgOnReadyStateChange = this.onreadystatechange;
this.send = function(data) {
requestBody = data;
return orgSend.apply(this, arguments);
};
this.onreadystatechange = function() {
if (this.readyState === XMLHttpRequest.DONE && this.status === 200) {
var responseText = this.responseText;
var nJunkChars = responseText.indexOf('[');
try {
var jsonData = JSON.parse(
nJunkChars ? responseText.substr(nJunkChars) : responseText
);
// TODO: modify jsonData here
console.log('Request: %o\nResponse: %o', requestBody, jsonData);
Object.defineProperty(this, 'responseText', {
value: responseText.substr(0, nJunkChars) +
JSON.stringify(jsonData),
configurable: true,
enumerable: true
});
} catch (e) {
console && console.log && console.log(e);
}
}
if (orgOnReadyStateChange) {
return orgOnReadyStateChange.apply(this, arguments);
}
};
Object.defineProperty(this, 'onreadystatechange', {
get: function() { return orgOnReadyStateChange; },
set: function(v) { orgOnReadyStateChange = v; },
configurable: true,
enumerable: true
});
}
return open.apply(this, arguments);
};
})(XMLHttpRequest.prototype.open);
I dati restituiti da GP comprendono una matrice di un elemento che è una matrice di quattro elementi come segue:
- La stringa
"ecr"
;
1
se ci sono più recensioni, 2
se questa è l'ultima "pagina" di recensioni, 3
se si è verificato un errore;
- L'HTML contenente la 'pagina' delle recensioni (e le eventuali risposte degli sviluppatori) - attualmente vengono restituite 40 recensioni per pagina;
- Il numero di pagina, corrispondente al
pageNum
parametro nel corpo della richiesta POST.
L'HTML può essere modificato per rimuovere le recensioni (e qualsiasi risposta dello sviluppatore associata) con voti in stelle diversi da quelli di interesse. Le recensioni corrispondono al selettore div.single-review
e hanno una corrispondenza discendente div.current-rating
con uno stile inline in cui la proprietà di larghezza CSS è una percentuale corrispondente alla valutazione ( 20%
per 1 stella, 40%
per 2 stelle, ecc.). Le risposte degli sviluppatori corrispondono al selettore div.developer-reply
e sono fratelli immediatamente dopo la recensione.
L'aggiunta di caselle di controllo all'interfaccia utente per consentire la scelta delle classificazioni a stelle delle recensioni da mostrare è abbastanza semplice. Tuttavia, quando le loro selezioni vengono modificate, è necessario recuperare nuovamente le recensioni. La modifica dell'ordinamento fa sì che ciò avvenga, così come la selezione dello stesso ordinamento di prima. Quindi, per raggiungere questo obiettivo automaticamente, ogni volta che una casella di controllo viene modificata, click
è possibile attivare un evento sull'elemento di ordinamento attualmente selezionato, che può essere trovato con un selettore di .id-review-sort-filter .dropdown-child.selected
. Quando una pagina dell'app su GP viene inizialmente caricata, la prima pagina delle recensioni è già inclusa e non viene caricata tramite AJAX, ma fino a quando le caselle di controllo vengono inizialmente spuntate, non importa.
A volte una pagina di (40) recensioni non ne contiene nessuna con la valutazione desiderata. Se non esiste alcun elemento nell'HTML restituito, GP non richiederà più pagine. Quindi, per far fronte a questo, sarebbe necessario recuperare altre pagine di recensioni (tramite la stessa API AJAX, ma modificando il pageNum
parametro) fino a quando non ci sono alcune recensioni da restituire. E per le pagine successive, il pageNum
parametro dovrebbe essere tradotto per tenere conto di ciò.
Quando l'ordinamento selezionato è 'Valutazione', potrebbero esserci molte pagine di recensioni a 5 stelle prima di qualsiasi con una valutazione desiderata. Recuperare e scartare ripetutamente pagine e pagine di recensioni sarebbe inefficiente (e potrebbe innescare un blocco IP temporaneo da parte di Google). In questo caso, quando il reviewSortOrder
parametro è 1
, è possibile utilizzare una ricerca binaria per trovare molto più rapidamente la pagina successiva con le recensioni da restituire. Un elemento di pagina corrispondente al selettore span.reviews-num
può essere ispezionato per trovare il numero totale di recensioni e quindi determinare un limite di numero di pagina superiore. Tuttavia, come risulta attualmente, le richieste di pagine oltre la pagina 111 ricevono una risposta HTTP 400.