Limite
Su LinkedIn c'è una tale restrizione — . È molto probabile che, come me fino a tempi recenti, non ne fossi a conoscenza.

Il concetto del limite è che se utilizzi la ricerca di persone al di fuori dei tuoi contatti troppo spesso (non ci sono metriche esatte, l'algoritmo decide in base alle tue azioni — quanto spesso e quanto hai cercato, aggiunto persone), il risultato della ricerca sarà limitato a tre profili, invece di 1000 (di default 100 pagine, con 10 profili per pagina). Il limite viene resettato all'inizio di ogni mese. Ovviamente, .
Ma non molto tempo fa, per un progetto personale, ho iniziato a esplorare molto la ricerca su LinkedIn e improvvisamente ho ricevuto questa limitazione. Naturalmente, non mi è piaciuto molto, dato che non lo utilizzavo per scopi commerciali, quindi la mia prima reazione è stata studiare la limitazione e cercare di aggirarla.
[Importante precisazione — i materiali nell'articolo sono presentati esclusivamente a scopo informativo e educativo. L'autore non incoraggia il loro utilizzo per scopi commerciali.]
Studiamo il problema
Abbiamo: invece di dieci profili con paginazione, la ricerca restituisce solo tre, dopo i quali viene inserito un blocco con la "raccomandazione" dell'account premium e sotto ci sono profili sfocati e non cliccabili.
La mano si dirige immediatamente alla console degli sviluppatori per vedere questi profili nascosti — forse possiamo rimuovere alcuni stili che applicano il blur, o estrarre le informazioni dal blocco nel markup. Ma, come previsto, questi profili sono solo e non contengono alcuna informazione.

Bene, ora guardiamo nella scheda Network e verifichiamo se si attiva davvero un'uscita alternativa dei risultati di ricerca, che restituisce solo tre profili. Troviamo la richiesta a "/api/search/blended" e controlliamo la risposta.

I profili arrivano in un array `included`, ma ci sono 15 entità in esso. In questo caso, i primi tre di loro sono oggetti con informazioni aggiuntive, ogni oggetto contiene informazioni su un profilo specifico (ad esempio, se il profilo è premium).

I successivi 12 sono profili reali — risultati di ricerca, dei quali ci mostreranno solo tre. Come si può già intuire, mostra solo quelli per cui arriva ulteriore informazione (i primi tre oggetti). Ad esempio, se prendiamo una risposta da un profilo senza limiti, riceveremo 28 entità — 10 oggetti con informazioni aggiuntive e 18 profili.
Risposta per profilo senza limiti

Perché arrivano più di 10 profili, se ne vengono richiesti precisamente 10, e questi non partecipano in alcun modo alla visualizzazione, nemmeno nella pagina successiva non ci saranno — per ora non lo so. Analizzando l'URL della richiesta, si può vedere che count=10 (quanti profili restituire nella risposta, massimo 49).
Sarei felice di ricevere qualsiasi commento a riguardo.
Stiamo sperimentando
Bene, la cosa più importante è che ora sappiamo per certo — che nei risultati arrivano più profili di quelli che ci mostrano. Quindi possiamo estrarre più dati, nonostante il limite. Proviamo a chiamare l'API noi stessi, direttamente dalla console, utilizzando fetch.

Come previsto, otteniamo un errore, 403. Questo è legato alla sicurezza; qui non stiamo inviando il token CSRF (. In poche parole, a ogni richiesta viene aggiunto un token unico, che viene verificato sul server per autenticità.

Può essere copiato da qualsiasi altra richiesta riuscita o dai cookie, dove è memorizzato nel campo ‘JSESSIONID’.
Dove trovare il tokenIntestazione di un'altra richiesta:

Oppure dai cookie, direttamente tramite la console:

Proviamo ancora una volta, questa volta passando a fetch le impostazioni in cui specifichiamo come parametro nell'intestazione il nostro csrf-token.

Successo, riceviamo tutti e 10 i profili. :tada:
A causa della differenza nelle intestazioni, la struttura della risposta è leggermente diversa rispetto a quella che arriva nella richiesta originale. È possibile ottenere la stessa struttura aggiungendo ‘Accept: ‘application/vnd.linkedin.normalized+json+2.1’ al nostro oggetto, accanto al token csrf.
Esempio di risposta con intestazione aggiunta
E ora?
Dopo, possiamo modificare (a mano o automatizzando) il parametro `start`, che indica l'indice da cui ci vengono restituiti 10 profili (di default = 0) dai risultati di ricerca totali. In altre parole, incrementandolo di 10 dopo ogni richiesta, otterremo una classica visualizzazione a pagine, 10 profili alla volta.
In questa fase avevo abbastanza dati e libertà per continuare a lavorare sul mio progetto personale. Ma sarebbe stato un peccato non provare a visualizzare questi dati direttamente, ora che li avevo a disposizione. Non ci addentreremo in Ember, che viene utilizzato nel frontend. Il sito aveva jQuery integrato, e rispolverando dalla memoria le conoscenze di base della sintassi, è possibile creare la seguente soluzione in pochi minuti.
Codice in 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="/it/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="/it/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>`
);
};
// interroghiamo l'API, otteniamo i dati e rendiamo i profili
const fetchProfiles = () => {
// token
const csrf = 'ajax:9082932176494192209';
// oggetto con le impostazioni della richiesta, passiamo il token
const settings = { headers: { 'csrf-token': csrf } }
// URL della richiesta, con l'indice dinamico di partenza alla fine
const url = `https://www.linkedin.com/voyager/api/search/blended?count=10&filters=List(geoRegion-jp0,network-S,resultType-PEOPLE)&origin=FACETED_SEARCH&q=all&queryContext=List(spellCorrectionEnabled-true,relatedSearchesEnabled-true)&start=${nextItemIndex}`;
/* facciamo la richiesta, per ogni profilo nella risposta chiamiamo il rendering del blocco, e poi incrementiamo l'indice di partenza di 10 */
fetch(url, settings).then(response => response.json()).then(data => {
data.elements[0].elements.forEach(createProfileBlock);
nextItemIndex += 10;
});
};
// rimuoviamo tutti i profili dall'elenco
$('.search-results__list').find('li').remove();
// inseriamo il pulsante per caricare i profili
$('.search-results__list').after('<button id="load-more">Carica di più</button>');
// aggiungiamo funzionalità al pulsante
$('#load-more').addClass('artdeco-button').on('click', fetchProfiles);
// impostiamo per default l'indice del profilo per la richiesta
window.nextItemIndex = 0;
Se esegui questo direttamente nella console della pagina di ricerca, verrà aggiunto un pulsante che carica 10 nuovi profili ad ogni clic, visualizzandoli come un elenco. Ovviamente, sarà necessario modificare il token e l'URL adeguatamente. Il blocco del profilo conterrà nome, posizione, località, un link al profilo e un'immagine segnaposto.

Conclusione
In questo modo, con un minimo di sforzo, siamo riusciti a trovare un punto vulnerabile e ripristinare la ricerca senza restrizioni. È stato sufficiente analizzare i dati e il loro percorso, dando un'occhiata alla richiesta stessa.
Non posso dire che questo rappresenti un problema serio per LinkedIn, poiché non comporta alcuna minaccia. Al massimo, si tratta di un guadagno perso a causa di tali "scappatoie" che consentono di non pagare per il premium. Potrebbe essere che questa risposta del server sia necessaria per il corretto funzionamento di altre parti del sito, oppure si tratta semplicemente di pigrizia da parte degli sviluppatori a causa della mancanza di risorse per fare un buon lavoro. (La limitazione è stata introdotta a gennaio 2015, prima di allora non c'era alcun limite).
P.S.
Naturalmente, il codice in jQuery è un esempio piuttosto primitivo delle possibilità. Attualmente ho creato un'estensione per il browser per le mie necessità. Aggiunge pulsanti di controllo e rende profili completi con immagini, pulsante di invito e connessioni generali. Inoltre, raccoglie dinamicamente filtri per località, aziende e altro, estraendo il token dai cookie. Quindi non c'è bisogno di hardcodificare nulla. Aggiunge anche campi di configurazione aggiuntivi, tipo "quanti profili richiedere per volta, fino a 49".

Su questo complemento sto ancora lavorando e ho in programma di pubblicarlo in accesso aperto. Scrivetemi se siete interessati.
Fonte: habr.com


