Wir umgehen das Suchlimit von LinkedIn mit der API

Limit

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

Wir umgehen das Suchlimit von LinkedIn mit der API

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, haben Premium-Konten diese Einschränkung nicht..

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 Störbilder und enthalten keine Informationen.

Wir umgehen das Suchlimit von LinkedIn mit der API

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.

Wir umgehen das Suchlimit von LinkedIn mit der API

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).

Wir umgehen das Suchlimit von LinkedIn mit der API

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 LimitWir umgehen das Suchlimit von LinkedIn mit der API
Wir umgehen das Suchlimit von LinkedIn mit der API

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).

Wir umgehen das Suchlimit von LinkedIn mit der API

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.

Wir umgehen das Suchlimit von LinkedIn mit der API

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

Wir umgehen das Suchlimit von LinkedIn mit der API

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:

Wir umgehen das Suchlimit von LinkedIn mit der API

Oder aus Cookies, direkt über die Konsole:

Wir umgehen das Suchlimit von LinkedIn mit der API

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

Wir umgehen das Suchlimit von LinkedIn mit der API

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 HeaderWir umgehen das Suchlimit von LinkedIn mit der API

Mehr über den Accept-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 = () =&gt; {
 // 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&amp;filters=List(geoRegion-jp0,network-S,resultType-PEOPLE)&amp;origin=FACETED_SEARCH&amp;q=all&amp;queryContext=List(spellCorrectionEnabled-true,relatedSearchesEnabled-true)&amp;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 =&gt; response.json()).then(data =&gt; {
 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.

Wir umgehen das Suchlimit von LinkedIn mit der API

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“.

Wir umgehen das Suchlimit von LinkedIn mit der API

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

60GB SSD 8Gb DDR4