Limite
Su LinkedIn c'è questa restrizione — . È molto probabile che tu, come me fino a poco tempo fa, non ne avessi mai sentito parlare.

Il concetto di limite è che se utilizzi la ricerca di persone al di fuori dei tuoi contatti troppo frequentemente (non ci sono metriche precise, è l'algoritmo a decidere in base alle tue azioni — quanto e quanto spesso hai cercato e aggiunto persone), il risultato della ricerca sarà limitato a tre profili, invece di 1000 (di default 100 pagine, 10 profili per pagina). Il limite si resetta all'inizio di ogni mese. Naturalmente, .
Ma non molto tempo fa, per un progetto secondario, ho iniziato a sperimentare molto con la ricerca su LinkedIn e ho improvvisamente ricevuto questa restrizione. Naturalmente, ciò non mi è piaciuto molto, poiché non lo stavo utilizzando per scopi commerciali, quindi la prima idea è stata quella di studiare il limite e provare a eluderlo.
[Importante precisazione: i materiali contenuti nell'articolo sono forniti esclusivamente a scopo informativo e formativo. L'autore non incoraggia il loro utilizzo per scopi commerciali.]
Esaminiamo il problema
Abbiamo: invece di dieci profili con paginazione, la ricerca restituisce solo tre, dopo i quali appare un blocco con "raccomandazione" per l'account premium e sotto ci sono profili sfocati e non cliccabili.
Immediatamente la mano corre alla console degli sviluppatori per controllare questi profili nascosti — forse possiamo rimuovere alcuni stili che creano il blur, o estrarre informazioni dal blocco del markup. Ma, come ci si aspettava, questi profili sono solo e non contengono alcuna informazione.

Bene, ora diamo un'occhiata alla scheda Rete e verifichiamo se la restituzione alternativa dei risultati di ricerca funziona davvero, restituendo solo tre profili. Troviamo la richiesta che ci interessa a “/api/search/blended” e osserviamo la risposta.

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

I successivi 12 sono profili reali — risultati della ricerca, da cui ci verranno mostrati solo tre. Come si può già intuire, vengono mostrati solo quelli sui quali arriva ulteriore informazione (i primi tre oggetti). Ad esempio, se prendiamo la risposta da un profilo senza limiti, arriveranno 28 entità — 10 oggetti con informazioni aggiuntive e 18 profili.
Risposta per il profilo senza limiti

Perché arrivano più di 10 profili, mentre ne vengono richiesti esattamente 10, e non partecipano affatto alla visualizzazione, nemmeno nella pagina seguente non ci saranno — non lo so ancora. Se analizziamo l'URL della richiesta, possiamo vedere che count=10 (quanti profili restituire nella risposta, massimo 49).
Sarei felice di ricevere qualsiasi commento al riguardo.
Sperimentiamo
Bene, la cosa più importante è che ora sappiamo con certezza che arrivano più profili nella risposta di quanti ce ne mostrano. Quindi possiamo ottenere più dati, nonostante il limite. Proviamo a chiamare l'API noi stessi, direttamente dalla console, usando fetch.

Come prevedibile, otteniamo un errore, 403. Questo è legato alla sicurezza, qui non stiamo inviando il token CSRF (. In parole povere — a ciascuna richiesta viene aggiunto un token unico che viene verificato sul server per autenticità).

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

Oppure dai cookie, direttamente tramite la console:

Riprovando, questa volta passiamo in fetch le impostazioni, specificando nel header il nostro csrf-token.

Successo, riceviamo tutti e 10 i profili. :tada:
A causa della differenza nei header, la struttura della risposta è leggermente diversa da quella che arriva nella richiesta originale. Possiamo ottenere la stessa struttura se aggiungiamo ‘Accept: 'application/vnd.linkedin.normalized+json+2.1' al nostro oggetto, accanto al token csrf.
Esempio di risposta con header aggiunto
Cosa succede dopo?
Dopo possiamo modificare (manualmente o automatizzare) il parametro `start`, che indica l'indice da cui ci verranno restituiti 10 profili (di default = 0) da tutti i risultati della ricerca. In altre parole, incrementandolo di 10 dopo ogni richiesta otteniamo una normale visualizzazione per pagina, con 10 profili alla volta.
In questa fase avevo abbastanza dati e libertà per continuare a lavorare sul mio progetto. Ma sarebbe stato un peccato non provare a visualizzare questi dati sul posto, dato che li avevo a disposizione. Non ci addentreremo in Ember, che viene utilizzato sul front-end. Il sito aveva jQuery collegato e, rispolverando le conoscenze di base, si può creare in pochi minuti quanto segue.
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>`
);
};
// chiamiamo 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 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 dalla lista
$('.search-results__list').find('li').remove();
// inseriamo il pulsante per caricare 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 l'indice del profilo per la richiesta su 0
window.nextItemIndex = 0;
Se esegui questo direttamente nella console della pagina di ricerca, aggiungerà un pulsante per caricare 10 nuovi profili ad ogni clic, rendendoli in un elenco. Ovviamente, prima di tutto, bisogna cambiare il token e l'URL con quelli necessari. Il blocco del profilo conterrà il nome, il titolo, la posizione, il link al profilo e un'immagine segnaposto.

Conclusione
In questo modo, con il minimo sforzo, siamo riusciti a trovare un punto vulnerabile e riportare la ricerca a senza limitazioni. È bastato analizzare i dati e il loro percorso, dare un'occhiata alla richiesta stessa.
Non posso dire che questo rappresenti un problema serio per LinkedIn, poiché non costituisce alcuna minaccia. Al massimo, è un guadagno perso a causa di simili "workaround", che permette di non pagare per il premium. Forse, una tale risposta del server è necessaria per il corretto funzionamento di altre parti del sito, oppure è semplicemente pigrizia degli sviluppatori, un difetto di risorse che non permette di fare bene. (La limitazione è stata introdotta a gennaio 2015, prima 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 i miei bisogni. Essa aggiunge pulsanti di controllo e rende profili completi con immagini, pulsante di invito e connessioni comuni. Inoltre, raccoglie dinamicamente i filtri per località, aziende e altro, estrae il token dai cookie. Quindi non è più necessario hardcodare nulla. Aggiunge anche campi di impostazione aggiuntivi, come "quanti profili richiedere alla volta, fino a 49".

Su questo complemento sto ancora lavorando e ho in programma di renderlo disponibile al pubblico. Scrivete se siete interessati.
Fonte: habr.com


