Limit
Na LinkedIn istnieje takie ograniczenie — . Jest bardzo prawdopodobne, że, podobnie jak ja do niedawna, nigdy się z nim nie spotkałeś i nie słyszałeś o nim.

Istota limitu polega na tym, że jeśli zbyt często korzystasz z wyszukiwania ludzi poza swoimi kontaktami (nie ma dokładnych metryk, decyduje algorytm w oparciu o twoje działania — jak często i dużo wyszukiwałeś, dodawałeś ludzi), to wyniki wyszukiwania będą ograniczone do trzech profili, zamiast 1000 (domyślnie 100 stron, po 10 profili na stronę). Limit resetuje się na początku każdego miesiąca. Oczywiście, .
Jednak niedawno, w ramach jednego z projektów, zacząłem dużo eksperymentować z wyszukiwaniem na LinkedIn i nagle napotkałem to ograniczenie. Naturalnie, nie spodobało mi się to, ponieważ nie wykorzystywałem go w żadnych celach komercyjnych, więc pierwszą myślą było zbadanie tego ograniczenia i próba jego obejścia.
[Ważna uwaga — materiały w artykule są przedstawione wyłącznie w celach informacyjnych i edukacyjnych. Autor nie zachęca do ich wykorzystywania w celach komercyjnych.]
Badamy problem
Mamy: zamiast dziesięciu profili z paginacją, wyszukiwanie zwraca tylko trzy, po których pojawia się blok z 'zaleceniem' konta premium, a poniżej znajdują się rozmyte i nieklikalne profile.
Natychmiast ręka sięga po konsolę dewelopera, aby zobaczyć te ukryte profile — być może możemy usunąć jakieś style, które wprowadzają rozmycie, lub wydobyć informacje z bloku w znacznikach. Ale, jak można się było spodziewać, te profile to tylko i nie zawierają żadnych informacji.

Dobrze, teraz spojrzymy na zakładkę Network i sprawdzimy, czy rzeczywiście działa alternatywne przekazywanie wyników wyszukiwania, które zwraca tylko trzy profile. Znajdujemy interesujące nas zapytanie do '/api/search/blended' i przyglądamy się odpowiedzi.

Profile przychodzą w tablicy `included`, ale zawiera ona łącznie 15 jednostek. W tym przypadku pierwsze trzy z nich to obiekty z dodatkowymi informacjami, każdy obiekt zawiera informacje o konkretnym profilu (na przykład, czy profil jest premium).

Następne 12 to prawdziwe profile — wyniki wyszukiwania, z których pokażemy tylko trzy. Jak można się domyślić, pokazuje się tylko tych, dla których przychodzą dodatkowe informacje (pierwsze trzy obiekty). Na przykład, jeśli weźmiemy odpowiedź z profilu bez limitu, otrzymamy 28 encji — 10 obiektów z dodatkowymi informacjami i 18 profili.
Odpowiedź dla profilu bez limitu

Dlaczego przychodzi więcej niż 10 profili, chociaż żąda się dokładnie 10, i nie mają one żadnego udziału w wyświetlaniu, nawet na następnej stronie ich nie będzie — na razie nie wiem. Jeśli przeanalizować URL zapytania, można zauważyć, że count=10 (ile profili zwrócić w odpowiedzi, maksymalnie 49).
Będę wdzięczny za wszelkie komentarze w tej sprawie.
Eksperymentujemy
Dobrze, najważniejsze, że teraz dokładnie wiemy — profili przychodzi w odpowiedzi więcej niż nam pokazują. Oznacza to, że możemy uzyskać więcej danych, mimo limitu. Spróbujmy uruchomić API sami, prosto z konsoli, za pomocą fetch.

Oczekiwanie, otrzymujemy błąd, 403. To związane jest z bezpieczeństwem, tutaj nie wysyłamy tokenu CSRF (. Mówiąc krótko — do każdego zapytania dodawany jest unikalny token, który jest weryfikowany na serwerze pod kątem autentyczności).

Można go skopiować z dowolnego innego udanego zapytania lub z cookies, gdzie jest przechowywany w polu ‘JSESSIONID’.
Gdzie znaleźć tokenNagłówek innego zapytania:

Lub z cookies, bezpośrednio przez konsolę:

Spróbujmy jeszcze raz, tym razem przekazujemy w fetch ustawienia, w których podajemy parametr w nagłówku nasz csrf-token.

Sukces, otrzymujemy wszystkie 10 profili. :tada:
Z powodu różnicy w nagłówkach struktura odpowiedzi nieco różni się od tej, która przychodzi w oryginalnym zapytaniu. Można uzyskać taką samą strukturę, jeśli dodamy ‘Accept: ‘application/vnd.linkedin.normalized+json+2.1’, do naszego obiektu, obok tokena csrf.
Przykład odpowiedzi z dodanym nagłówkiem
Co dalej?
Dalej można edytować (ręcznie lub automatycznie) parametr `start`, wskazujący na indeks, od którego oddadzą nam 10 profili (domyślnie = 0) z całego wyniku wyszukiwania. Innymi słowy, inkrementując go o 10 po każdym zapytaniu, uzyskujemy standardowe stronicowanie, po 10 profili na raz.
Na tym etapie miałem wystarczająco dużo danych i wolności, by kontynuować pracę nad projektem pobocznym. Ale byłoby grzechem nie spróbować wyświetlić tych danych na miejscu, skoro już są pod ręką. Nie będziemy zagłębiać się w Ember, który jest używany na froncie. Na stronie był podłączony jQuery, a przypominając sobie podstawową składnię, można w kilka minut stworzyć to, co poniżej.
Kod w 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="/pl/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="/pl/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>`
);
};
// pobieramy API, uzyskujemy dane i renderujemy profile
const fetchProfiles = () => {
// token
const csrf = 'ajax:9082932176494192209';
// obiekt z ustawieniami zapytania, przesyłamy token
const settings = { headers: { 'csrf-token': csrf } }
// URL zapytania, z dynamicznym indeksem startowym na końcu
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}`;
/* wykonujemy zapytanie, dla każdego profilu w odpowiedzi wywołujemy render bloku, a potem inkrementujemy indeks startowy o 10 */
fetch(url, settings).then(response => response.json()).then(data => {
data.elements[0].elements.forEach(createProfileBlock);
nextItemIndex += 10;
});
};
// usuwamy wszystkie profile z listy
$('.search-results__list').find('li').remove();
// wstawiamy przycisk ładowania profili
$('.search-results__list').after('<button id="load-more">Załaduj więcej</button>');
// dodajemy funkcjonalność do przycisku
$('#load-more').addClass('artdeco-button').on('click', fetchProfiles);
// ustawiamy domyślny indeks profilu dla zapytania
window.nextItemIndex = 0;
Jeśli to wykona się bezpośrednio w konsoli na stronie wyszukiwania, doda to przycisk, który za każdym razem ładować będzie 10 nowych profili, renderując je w liście. Oczywiście, przed tym należy zmienić token i URL na potrzebne. Blok profilu będzie zawierał imię, stanowisko, lokalizację, link do profilu oraz obrazek zastępczy.

Podsumowanie
W ten sposób, przy minimalnym wysiłku, udało nam się znaleźć lukę i odzyskać nielimitowane wyszukiwanie. Wystarczyło przeanalizować dane i ich ścieżkę, zajrzeć w samą prośbę.
Nie mogę powiedzieć, że jest to poważny problem dla LinkedIn, ponieważ nie niesie ze sobą żadnego zagrożenia. Maksymalnie, to utracony zysk z powodu takich "obejść", które pozwalają unikać płacenia za premium. Być może taka odpowiedź serwera jest potrzebna do prawidłowego działania innych części strony, lub to po prostu lenistwo programistów, brak zasobów, które nie pozwala na zrobienie tego dobrze. (Ograniczenie pojawiło się w styczniu 2015 roku, wcześniej nie było limitu).
P.S.
Oczywiście, kod w jQuery to dość prymitywny przykład możliwości. Obecnie stworzyłem rozszerzenie dla przeglądarki wg własnych potrzeb. Dodaje przyciski kontrolne i renderuje pełne profile z obrazkami, przyciskiem zaproszenia i kontaktami wspólnymi. Ponadto dynamicznie zbiera filtry lokalizacji, firm i innych, wydobywa token z ciasteczek. Tak więc, nie ma potrzeby twardego kodowania. Dodaje również dodatkowe pola ustawień, a la "ile profili żądać za jednym razem, do 49".

Nad tym dodatkiem nadal pracuję i planuję udostępnić go publicznie. Piszecie, jeśli jesteście zainteresowani.
Źródło: habr.com


