Limit
Auf LinkedIn gibt es eine solche Einschränkung — . Es ist sehr wahrscheinlich, dass Sie, wie ich bis vor kurzem, nie mit ihm konfrontiert wurden und nichts davon gehört haben.

Das Wesen des Limits ist, dass, wenn Sie die Suche nach Personen außerhalb Ihrer Kontakte zu oft verwenden (es gibt keine genauen Metriken, das Algorithmus entscheidet basierend auf Ihrem Verhalten — wie oft und viel Sie gesucht und Personen hinzugefügt haben), das Suchergebnis auf drei Profile beschränkt wird, statt der 1000 (standardmäßig 100 Seiten, 10 Profile pro Seite). Das Limit wird zu Beginn jedes Monats zurückgesetzt. Natürlich, .
Aber vor nicht allzu langer Zeit begann ich für ein kleines Projekt viel mit der Suche auf LinkedIn zu experimentieren und erhielt plötzlich diese Einschränkung. Natürlich gefiel mir das nicht besonders, da ich es nicht für kommerzielle Zwecke verwendete, also war der erste Gedanke, die Einschränkung zu untersuchen und zu versuchen, sie zu umgehen.
[Ein wichtiger Hinweis — die Materialien in diesem Artikel sind ausschließlich zu Informations- und Schulungszwecken gedacht. Der Autor ermutigt nicht zur Nutzung für kommerzielle Zwecke.]
Das Problem untersuchen
Wir haben: Statt zehn Profile mit Pagination zeigt die Suche nur drei an, nach denen ein Block mit einer "Empfehlung" für ein Premium-Konto eingefügt wird und darunter erscheinen unscharfe und nicht klickbare Profile.
Sofort greift die Hand zum Entwicklertools-Konsole, um diese versteckten Profile zu betrachten — vielleicht können wir einige Stile entfernen, die den Blur verursachen, oder Informationen aus dem Block im Markup extrahieren. Aber, wie zu erwarten war, sind diese Profile nur und enthalten keine Informationen.

Gut, jetzt schauen wir in den Bereich Netzwerk und prüfen, ob tatsächlich eine alternative Ausgabe der Suchergebnisse aktiviert ist, die nur drei Profile zurückgibt. Wir finden die uns interessierende Anfrage an "\/api\/search\/blended" und sehen uns die Antwort an.

Profile kommen im Array `included`, aber es gibt insgesamt 15 Entitäten. In diesem Fall sind die ersten drei davon Objekte mit zusätzlichen Informationen, jedes Objekt enthält Informationen zu einem bestimmten Profil (zum Beispiel, ob es sich um ein Premium-Profil handelt).

Die folgenden 12 sind echte Profile – Suchergebnisse, von denen uns nur drei angezeigt werden. Wie man bereits erraten kann, werden nur die angezeigt, für die zusätzliche Informationen eintreffen (die ersten drei Objekte). Wenn man zum Beispiel die Antwort von einem Profil ohne Limit nimmt, erhält man 28 Entitäten – 10 Objekte mit zusätzlichen Informationen und 18 Profile.
Antwort für Profil ohne Limit

Warum kommen mehr als 10 Profile, obwohl genau 10 angefordert werden, und sie spielen überhaupt keine Rolle in der Anzeige, selbst auf der nächsten Seite werden sie nicht angezeigt – darüber weiß ich noch nicht. Wenn man die URL-Abfrage analysiert, sieht man, dass count=10 (wie viele Profile in der Antwort zurückgegeben werden, maximal 49).
Ich freue mich über alle Kommentare dazu.
Experimentieren wir
Gut, das Wichtigste, was wir jetzt sicher wissen — es kommen mehr Profile in der Antwort, als uns angezeigt werden. Das bedeutet, wir können mehr Daten abrufen, trotz des Limits. Lassen Sie uns versuchen, die API selbst direkt aus der Konsole mit Hilfe von fetch zu ziehen.

Wie erwartet erhalten wir einen Fehler, 403. Das liegt an der Sicherheit; hier senden wir keinen CSRF-Token (). Kurz gesagt, zu jeder Anfrage wird ein einzigartiger Token hinzugefügt, der auf dem Server auf Echtheit überprüft wird.

Er kann aus jeder anderen erfolgreichen Anfrage oder aus Cookies kopiert werden, wo er im Feld 'JSESSIONID' gespeichert ist.
Wo man den Token findetHeader einer anderen Anfrage:

Oder aus Cookies, direkt über die Konsole:

Versuchen wir es noch einmal, diesmal übergeben wir in fetch die Einstellungen, in denen wir als Parameter im Header unseren csrf-Token angeben.

Erfolg, wir erhalten alle 10 Profile. :tada:
Wegen der Unterschiede in den Headern sieht die Struktur der Antwort ein wenig anders aus als bei der ursprünglichen Anfrage. Man kann die gleiche Struktur erhalten, wenn man 'Accept: 'application/vnd.linkedin.normalized+json+2.1' in unser Objekt hinzufügt, neben dem csrf-Token.
Beispielantwort mit hinzugefügtem Header
Was folgt jetzt?
Danach kann der Parameter `start`, der den Index angibt, ab dem uns 10 Profile (Standard = 0) aus den gesamten Suchergebnissen zurückgegeben werden, manuell oder automatisiert bearbeitet werden. Mit anderen Worten, indem wir ihn nach jeder Anfrage um 10 inkrementieren, erhalten wir eine gewöhnliche paginierte Ausgabe, 10 Profile auf einmal.
In diesem Stadium hatte ich genügend Daten und Freiheit, um mit der Arbeit an meinem Pet-Projekt fortzufahren. Es wäre jedoch ein Fehler gewesen, diese Daten nicht direkt vor Ort zu visualisieren, da sie zur Verfügung standen. Wir werden uns nicht in den Ember einarbeiten, der im Frontend verwendet wird. Auf der Website war jQuery eingebunden, und mit dem Wissen über die grundlegende Syntax, das ich mir ins Gedächtnis gerufen habe, kann man in ein paar Minuten Folgendes erstellen.
Code 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="/de/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="/de/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>`
);
};
// API abrufen, Daten erhalten und Profile rendern
const fetchProfiles = () => {
// Token
const csrf = 'ajax:9082932176494192209';
// Objekt mit den Anfrageeinstellungen, Token übergeben
const settings = { headers: { 'csrf-token': csrf } }
// Anfrage-URL, mit dynamischem Startindex am Ende
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}`;
/* Anfrage senden, für jedes Profil in der Antwort rendern wir den Block und inkrementieren danach den Startindex um 10 */
fetch(url, settings).then(response => response.json()).then(data => {
data.elements[0].elements.forEach(createProfileBlock);
nextItemIndex += 10;
});
};
// Alle Profile aus der Liste entfernen
$('.search-results__list').find('li').remove();
// Knopf zum Laden von Profilen einfügen
$('.search-results__list').after('<button id="load-more">Mehr laden</button>');
// wir fügen die Funktionalität zum Button hinzu
$('#load-more').addClass('artdeco-button').on('click', fetchProfiles);
// wir setzen den Standardindex des Profils für die Anfrage
window.nextItemIndex = 0;
Wenn Sie dies direkt in der Konsole auf der Suchseite ausführen, wird dies eine Schaltfläche hinzufügen, mit der bei jedem Klick 10 neue Profile geladen werden, die in einer Liste angezeigt werden. Natürlich muss das Token und die URL vorher entsprechend angepasst werden. Der Profilblock enthält Name, Position, Ort, einen Link zum Profil und ein Platzhalterbild.

Fazit
Auf diese Weise konnten wir mit minimalem Aufwand eine Schwachstelle finden und die Suche ohne Einschränkungen zurückbekommen. Es genügte, die Daten und ihren Verlauf zu analysieren und den ursprünglichen Request zu betrachten.
Ich kann nicht sagen, dass dies ein ernsthaftes Problem für LinkedIn darstellt, da es keine Bedrohung darstellt. Höchstens ist es ein entgangener Gewinn durch solche "Umgehungen", die es ermöglichen, nicht für Premium zu bezahlen. Möglicherweise ist diese Serverantwort für das ordnungsgemäße Funktionieren anderer Teile der Website erforderlich, oder es ist einfach die Faulheit der Entwickler oder ein Ressourcenmangel, der es ihnen nicht erlaubt hat, es gut zu machen. (Die Einschränkung trat im Januar 2015 in Kraft, zuvor gab es kein Limit).
P.S.
Natürlich ist der jQuery-Code ein recht primitiver Beispiel für Potenziale. Im Moment habe ich eine Erweiterung für den Browser für meine Bedürfnisse erstellt. Diese fügt Steuerungsschaltflächen hinzu und rendert vollständige Profile mit Bildern, einer Einladungs-Schaltfläche und gemeinsamen Verbindungen. Außerdem sammelt sie dynamisch Filter für Standorte, Unternehmen und mehr, holt das Token aus den Cookies. So muss man nichts mehr hardcoden. Zudem fügt sie zusätzliche Einstellungsmöglichkeiten hinzu, wie z.B. „wie viele Profile gleichzeitig angefragt werden sollen, bis zu 49“.

An diesem Add-on arbeite ich noch und plane, es öffentlich zur Verfügung zu stellen. Schreiben Sie, wenn Sie interessiert sind.
Quelle: habr.com


