Igualmente frustrado com a falta de opções decentes de filtragem / classificação no Google Play, e inspirado por sua sugestão de que um script Greasemonkey poderia resolver o problema, decidi escrever um, que enviei para https://greasyfork.org/en/ scripts / 24667-google-play-review-rating-filter . Ele adiciona cinco caixas de seleção às páginas do aplicativo em play.google.com, que permitem filtrar comentários com classificações por estrelas específicas. Eu testei com o Greasemonkey e o Unified Script Injector no Firefox e o Tampermonkey no Chrome.
Em vez de reproduzir o script inteiro aqui, descreverei a abordagem adotada para aqueles que possam estar interessados. TL; DR: Se você deseja apenas a solução, instale o complemento de navegador apropriado e faça o download do script do usuário no link acima. Observe que, se você quiser usá-lo no seu dispositivo Android, provavelmente precisará usar o Firefox com o complemento USI (e também selecionar Solicitar site da área de trabalho no menu), pois a maioria dos outros navegadores Android não suporta ons ou scripts de usuário e o Greasemonkey atualmente não funciona no Firefox para Android - ele não funciona no aplicativo Google Play.
À medida que você percorre as análises, o GP (Google Play) carrega dados para obter mais análises por meio de solicitações AJAX no URL /store/getreviews
usando o POST
método HTTP . Portanto, conectando essas chamadas AJAX, é possível modificar os dados retornados ao GP.
XMLHttpRequest.prototype.open
pode ser substituído por uma função que chamará o original, mas primeiro, se a solicitação for para revisar dados, modifique o XMLHttpRequest
objeto XHR ( ) para que o POST
corpo da solicitação possa ser capturado e a resposta modificada. Uma send
propriedade pode ser atribuída ao objeto XHR como uma função que armazenará os POST
dados antes de chamar o original. A onreadystatechange
propriedade pode ser atribuída como uma função que modificará a resposta antes de chamar a função atribuída pelo GP a esta propriedade. Como o GP atribuirá onreadystatechange
depois disso, Object.defineProperty
precisaria ser usado para redefinir a propriedade para que o valor que o GP define seja armazenado em vez de realmente ser atribuído à propriedade interna. E como a responseText
propriedade é somente leitura, Object.defineProperty
seria necessário alterar seu valor.
Os dados retornados pelo GP estão no formato JSON, embora tenham alguns caracteres indesejados no início, que devem ser reproduzidos fielmente em qualquer dado modificado.
O código a seguir demonstra isso e exibirá na janela do console do desenvolvedor do navegador o corpo da solicitação e os dados de resposta (embora na verdade não os modifique):
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);
Os dados retornados pelo GP compreendem uma matriz de um elemento, que é uma matriz de quatro elementos da seguinte maneira:
- A corda
"ecr"
;
1
se houver mais revisões, 2
se esta for a última 'página' de revisões, 3
se ocorrer um erro;
- O HTML que contém a 'página' de críticas (e qualquer resposta do desenvolvedor) - atualmente 40 críticas são retornadas por página;
- O número da página, correspondente ao
pageNum
parâmetro no corpo da solicitação POST.
O HTML pode ser modificado para remover críticas (e qualquer resposta associada do desenvolvedor) com classificações por estrelas diferentes das de interesse. As revisões correspondem ao seletor div.single-review
e têm um descendente correspondente div.current-rating
a um estilo embutido, onde a propriedade largura do CSS é uma porcentagem correspondente à classificação ( 20%
para 1 estrela, 40%
para 2 estrelas, etc.). As respostas do desenvolvedor correspondem ao seletor div.developer-reply
e são irmãos imediatamente após a revisão.
Adicionar caixas de seleção à interface do usuário para permitir a escolha de quais classificações por estrelas das resenhas devem ser exibidas é bastante simples. No entanto, quando suas seleções são alteradas, as revisões precisam ser buscadas novamente. A alteração da ordem de classificação faz com que isso ocorra, assim como a seleção da mesma ordem de antes. Portanto, para conseguir isso automaticamente, sempre que uma caixa de seleção é alterada, um click
evento pode ser acionado no elemento de ordem de classificação atualmente selecionado, que pode ser encontrado com um seletor de .id-review-sort-filter .dropdown-child.selected
. Quando uma página de aplicativo no GP é carregada inicialmente, a primeira página de revisões já está incluída e não é carregada via AJAX, mas desde que as caixas de seleção estejam todas marcadas inicialmente, isso não importa.
Às vezes, uma página de (40) críticas não contém nenhuma com a classificação desejada. Se não houver elementos no HTML retornado, o GP não solicitará mais nenhuma página. Portanto, para atender a isso, seria necessário buscar páginas adicionais de revisões (através da mesma API AJAX, mas modificando o pageNum
parâmetro) até que haja algumas revisões a serem retornadas. E para páginas subseqüentes, o pageNum
parâmetro precisaria ser traduzido para explicar isso.
Quando a ordem de classificação selecionada é 'Classificação', pode haver muitas páginas com críticas de 5 estrelas antes de qualquer uma com a classificação desejada. Buscar e descartar repetidamente páginas e páginas de resenhas seria ineficiente (e poderia desencadear um bloqueio temporário de IP pelo Google). Nesse caso, quando o reviewSortOrder
parâmetro é 1
, uma pesquisa binária pode ser empregada para encontrar muito mais rapidamente a próxima página com revisões a serem retornadas. Um elemento da página correspondente ao seletor span.reviews-num
pode ser inspecionado para encontrar o número total de revisões e, assim, determinar um número de página superior vinculado. Embora, atualmente, os pedidos de páginas além da página 111 recebam uma resposta HTTP 400.