Ignorando o limite de pesquisa do LinkedIn brincando com a API
Limite
Existe uma grande limitação no LinkedIn - Limite de uso comercial. É extremamente provável que você, como eu até recentemente, nunca tenha encontrado ou ouvido falar dele.
A essência do limite é que se você usar a busca por pessoas fora de seus contatos com muita frequência (não há métricas exatas, o algoritmo decide com base em suas ações - com que frequência e quanto você pesquisou, adicionou pessoas), então o resultado da pesquisa será limitado a três perfis, em vez de 1000 (padrão 100 páginas, 10 perfis por página). O limite é zerado no início de cada mês. Naturalmente, contas premium não têm essa limitação.
Mas não faz muito tempo, para um projeto favorito, comecei a brincar muito com a pesquisa do LinkedIn e de repente tive essa limitação. Naturalmente não gostei muito disso, pois não o utilizava para nenhum fim comercial, então meu primeiro pensamento foi estudar a limitação e tentar contorná-la.
[Um esclarecimento importante: os materiais do artigo são apresentados apenas para fins informativos e educacionais. O autor não incentiva seu uso para fins comerciais.]
Estamos estudando o problema
Temos: em vez de dez perfis com paginação, a busca retorna apenas três, após os quais é inserido um bloco com uma “recomendação” de conta premium e abaixo estão perfis borrados e não clicáveis.
Imediatamente, a mão se estende ao console do desenvolvedor para observar esses perfis ocultos - talvez possamos remover alguns estilos de desfoque ou extrair informações de um bloco na marcação. Mas, como era de se esperar, esses perfis são apenas imagens de espaço reservado e nenhuma informação é armazenada.
Ok, agora vamos dar uma olhada na aba Rede e verificar se os resultados da pesquisa alternativa que retornam apenas três perfis realmente funcionam. Encontramos a solicitação que nos interessa para “/api/search/blended” e analisamos a resposta.
Os perfis vêm em um array `incluído`, mas já contém 15 entidades. Neste caso, os três primeiros são objetos com informações adicionais, cada objeto contém informações sobre um perfil específico (por exemplo, se o perfil é premium ).
Os próximos 12 são perfis reais - resultados de pesquisa, dos quais apenas três nos serão mostrados. Como você já pode imaginar, mostra apenas quem recebe informações adicionais (os três primeiros objetos). Por exemplo, se você tirar a resposta de um perfil sem limite, receberá 28 entidades – 10 objetos com adicional. informações e 18 perfis.
Responder por perfil sem limite
Por que chegam mais de 10 perfis, embora exatamente 10 sejam solicitados, e eles não participem da exibição de forma alguma, mesmo na próxima página não estarão - ainda não sei. Se você analisar a URL da solicitação, poderá ver que count=10 (quantos perfis retornar na resposta, máximo 49).
Eu ficaria feliz em receber quaisquer comentários sobre este assunto.
Vamos experimentar
Ok, a coisa mais importante que sabemos agora com certeza é que há mais perfis na resposta do que eles nos mostram. Isso significa que podemos obter mais dados, apesar do limite. Vamos tentar extrair a API nós mesmos, diretamente do console, usando fetch.
Como esperado, obtemos um erro 403. Isso é por questão de segurança, aqui não estamos enviando um token CSRF (CSRF na Wikipédia. Resumindo, um token exclusivo é adicionado a cada solicitação, que é verificada no servidor quanto à autenticidade).
Pode ser copiado de qualquer outra solicitação bem-sucedida ou de cookies, onde é armazenado no campo 'JSESSIONID'.
Onde encontrar o tokenCabeçalho de outra solicitação:
Ou a partir de cookies, diretamente pelo console:
Vamos tentar novamente, desta vez passamos as configurações para fetch, nas quais especificamos nosso token csrf como parâmetro no cabeçalho.
Sucesso, recebemos todos os 10 perfis. :tada:
Devido à diferença nos cabeçalhos, a estrutura da resposta é um pouco diferente daquela recebida na solicitação original. Você pode obter a mesma estrutura se adicionar 'Accept: 'application/vnd.linkedin.normalized+json+2.1' ao nosso objeto, próximo ao token csrf. Exemplo de resposta com cabeçalho adicionado
Depois você pode editar (manualmente ou automatizar) o parâmetro `start`, apontando para o índice, a partir do qual teremos 10 perfis (padrão = 0) de todo o resultado da pesquisa. Em outras palavras, aumentando-o em 10 após cada solicitação, obtemos a saída normal página por página, 10 perfis por vez.
Nesta fase eu tinha dados e liberdade suficientes para continuar trabalhando no projeto favorito. Mas seria um pecado não tentar exibir esses dados na hora, pois eles já estavam em mãos. Não entraremos no Ember, que é usado na frente. O jQuery foi conectado ao site e, depois de adquirir o conhecimento da sintaxe básica na memória, você pode criar o seguinte em alguns minutos.
código jQuery
/* рендер блока, принимаем данные профиля и вставляем блок в список профилей используя эти данные */
const createProfileBlock = ({ headline, publicIdentifier, subline, title }) => {
$('.search-results__list').append(
`<li class="search-result search-result__occluded-item ember-view">
<div class="search-entity search-result search-result--person search-result--occlusion-enabled ember-view">
<div class="search-result__wrapper">
<div class="search-result__image-wrapper">
<a class="search-result__result-link ember-view" href="/pt/in/${publicIdentifier}/">
<figure class="search-result__image">
<div class="ivm-image-view-model ember-view">
<img class="lazy-image ivm-view-attr__img--centered EntityPhoto-circle-4 presence-entity__image EntityPhoto-circle-4 loaded" src="http://www.userlogos.org/files/logos/give/Habrahabr3.png" />
</div>
</figure>
</a>
</div>
<div class="search-result__info pt3 pb4 ph0">
<a class="search-result__result-link ember-view" href="/pt/in/${publicIdentifier}/">
<h3 class="actor-name-with-distance search-result__title single-line-truncate ember-view">
${title.text}
</h3>
</a>
<p class="subline-level-1 t-14 t-black t-normal search-result__truncate">${headline.text}</p>
<p class="subline-level-2 t-12 t-black--light t-normal search-result__truncate">${subline.text}</p>
</div>
</div>
</div>
<li>`
);
};
// дергаем апи, получаем данные и рендерим профили
const fetchProfiles = () => {
// токен
const csrf = 'ajax:9082932176494192209';
// объект с настройками запроса, передаем токен
const settings = { headers: { 'csrf-token': csrf } }
// урл запроса, с динамическим индексом старта в конце
const url = `https://www.linkedin.com/voyager/api/search/blended?count=10&filters=List(geoRegion-%3Ejp%3A0,network-%3ES,resultType-%3EPEOPLE)&origin=FACETED_SEARCH&q=all&queryContext=List(spellCorrectionEnabled-%3Etrue,relatedSearchesEnabled-%3Etrue)&start=${nextItemIndex}`;
/* делаем запрос, для каждого профиля в ответе вызываем рендер блока, и после инкрементируем стартовый индекс на 10 */
fetch(url, settings).then(response => response.json()).then(data => {
data.elements[0].elements.forEach(createProfileBlock);
nextItemIndex += 10;
});
};
// удаляем все профили из списка
$('.search-results__list').find('li').remove();
// вставляем кнопку загрузки профилей
$('.search-results__list').after('<button id="load-more">Load More</button>');
// добавляем функционал на кнопку
$('#load-more').addClass('artdeco-button').on('click', fetchProfiles);
// ставим по умолчания индекс профиля для запроса
window.nextItemIndex = 0;
Se você fizer isso diretamente no console da página de pesquisa, será adicionado um botão que carrega 10 novos perfis a cada clique e os renderiza em uma lista. Claro, altere o token e o URL para os necessários antes de fazer isso. O bloco de perfil conterá o nome, cargo, localização, link para o perfil e uma imagem de espaço reservado.
Conclusão
Assim, com um mínimo de esforço, conseguimos encontrar o ponto fraco e retomar a busca sem restrições. Bastou analisar os dados e seu caminho, olhar a própria solicitação.
Não posso dizer que este seja um problema sério para o LinkedIn, porque não representa nenhuma ameaça. O máximo é o lucro perdido devido a essas “soluções alternativas”, o que permite evitar o pagamento do prêmio. Talvez tal resposta do servidor seja necessária para o correto funcionamento de outras partes do site, ou seja simplesmente preguiça dos desenvolvedores e falta de recursos que não permite que seja bem feito. (A limitação surgiu em janeiro de 2015; antes não havia limite).
PS
Naturalmente, o código jQuery é um exemplo bastante primitivo dos recursos. No momento criei uma extensão de navegador para atender às minhas necessidades. Adiciona botões de controle e renderiza perfis completos com fotos, botão de convite e conexões gerais. Além disso, ele coleta filtros dinamicamente para locais, empresas e outras coisas, e recupera um token dos cookies. Portanto, não há mais necessidade de codificar nada. Bem, ele adiciona campos de configurações adicionais, como “quantos perfis solicitar por vez, até 49”.
Ainda estou trabalhando nessa adição e pretendo divulgá-la ao público. Escreva se você estiver interessado.