Obidemo omejitev iskanja na LinkedInu, igrajoč se z API-jem

Limit

There is such a limitation on LinkedIn — Commercial use limit. It is highly likely that, like me until recently, you have never encountered or heard of it.

Obidemo omejitev iskanja na LinkedInu, igrajoč se z API-jem

The essence of the limit is that if you use the people search outside your contacts too frequently (there are no exact metrics, it’s determined by the algorithm based on your actions — how often and how much you've searched and added people), then the search result will be limited to three profiles instead of 1000 (by default 100 pages, with 10 profiles per page). The limit resets at the beginning of each month. Naturally, premium accounts do not have this limitation..

But not long ago, for a pet project, I started to experiment a lot with the search on LinkedIn and suddenly encountered this limit. Naturally, I didn’t like it much, as I wasn’t using it for any commercial purposes, so my first thought was to study the limitation and try to circumvent it.

[An important clarification — the materials in this article are presented solely for informational and educational purposes. The author does not encourage their use for commercial purposes.]

Investigating the issue

We have: instead of ten profiles with pagination, the search returns only three, after which a block with a ‘premium account recommendation’ is inserted, and below are blurred and non-clickable profiles.

Immediately, my hand reaches for the developer console to check these hidden profiles — perhaps we can remove some styles causing the blur, or extract information from the block in the markup. But, as expected, these profiles are merely placeholder images and store no information.

Obidemo omejitev iskanja na LinkedInu, igrajoč se z API-jem

Okay, now let's check the Network tab and verify if the alternative search results output really triggers, returning only three profiles. We find the query we’re interested in ‘/api/search/blended’ and look at the response.

Obidemo omejitev iskanja na LinkedInu, igrajoč se z API-jem

Profiles come in the `included` array, but there are as many as 15 entities in it. In this case, the first three of them are objects with additional information, each object contains information about a specific profile (for example, whether the profile is premium).

Obidemo omejitev iskanja na LinkedInu, igrajoč se z API-jem

Следующие 12 — это реальные профили — результаты поиска, из которых нам покажут только три. Как уже можно догадаться, показываются только те, на кого приходит дополнительная информация (первые три объекта). Например, если взять ответ с профиля без лимита, то придет 28 сущностей — 10 объектов с дополнительной информацией и 18 профилей.

Ответ для профиля без лимитаObidemo omejitev iskanja na LinkedInu, igrajoč se z API-jem
Obidemo omejitev iskanja na LinkedInu, igrajoč se z API-jem

Почему профилей приходит больше 10, хотя запрашивается именно 10, и они никак не участвуют в отображении, даже на следующей странице их не будет — пока не знаю. Если проанализировать URL запроса, то можно увидеть, что count=10 (сколько профилей вернуть в ответе, максимум 49).

Obidemo omejitev iskanja na LinkedInu, igrajoč se z API-jem

Буду рад любым комментариям по этому поводу.

Экспериментируем

Хорошо, самое главное, мы теперь точно знаем — профилей приходит в ответе больше, чем нам показывают. Значит, мы можем достать больше данных, несмотря на лимит. Давайте попробуем получить API сами, прямо из консоли, при помощи fetch.

Obidemo omejitev iskanja na LinkedInu, igrajoč se z API-jem

Ожидаемо, получаем ошибку 403. Это связано с безопасностью, здесь мы не отсылаем CSRF токен (CSRF на Википедии. Если в двух словах — к каждому запросу добавляется уникальный токен, который проверяется на сервере на подлинность).

Obidemo omejitev iskanja na LinkedInu, igrajoč se z API-jem

Его можно скопировать из любого другого успешного запроса или же из cookies, где он хранится в поле ‘JSESSIONID’.

Где найти токенЗаголовок другого запроса:

Obidemo omejitev iskanja na LinkedInu, igrajoč se z API-jem

Или из cookies, прямо через консоль:

Obidemo omejitev iskanja na LinkedInu, igrajoč se z API-jem

Пробуем еще раз, в этот раз передаем в fetch настройки, в которых указываем параметром в header наш csrf-token.

Obidemo omejitev iskanja na LinkedInu, igrajoč se z API-jem

Успех, нам приходят все 10 профилей. :tada:

Из-за разницы заголовков структура ответа немного отличается от того, что приходит в оригинальном запросе. Можно получить такую же структуру, если добавить ‘Accept: ‘application/vnd.linkedin.normalized+json+2.1’, к нам в объект, рядом с csrf токеном.
Пример ответа с добавленным заголовкомObidemo omejitev iskanja na LinkedInu, igrajoč se z API-jem

Больше о заголовке Accept

Kaj zdaj?

Дальше можно редактировать (руками или автоматизировать) параметр `start`, указывающий на индекс, начиная с которого нам отдадут 10 профилей (по умолчанию = 0) из всего результата поиска. Иначе говоря, инкрементируя его на 10 после каждого запроса, у нас получается обычная постраничная выдача, по 10 профилей за раз.

Na tej točki sem imel dovolj podatkov in svobode, da sem lahko nadaljeval z delom na svojem pet-projektu. A bilo bi greh, da ne preizkusim teh podatkov prikazati neposredno na mestu, saj jih imam. V Emberju, ki se uporablja na fronti, ne bomo posegali. Na strani je bil povezan jQuery, in obudivši v spomin osnovno sintakso, lahko v nekaj minutah ustvarimo naslednje.

Koda v 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="/sl/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="/sl/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, получаем данные и отображаем профили
const fetchProfiles = () =&gt; {
 // токен
 const csrf = 'ajax:9082932176494192209';
 
 // объект с настройками запроса, передаем токен
 const settings = { headers: { 'csrf-token': csrf } }

 // URL запроса, с динамическим индексом старта в конце
 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}`; 
 /* делаем запрос, для каждого профиля в ответе вызываем рендер блока, и после инкрементируем стартовый индекс на 10 */
 fetch(url, settings).then(response =&gt; response.json()).then(data =&gt; {
 data.elements[0].elements.forEach(createProfileBlock);
 nextItemIndex += 10;
});
};


// удаляем все профили из списка
$('.search-results__list').find('li').remove();
// вставляем кнопку загрузки профилей
$('.search-results__list').after('<button id="load-more">Učitaj više</button>');
// dodajemo funkcionalnost gumbu
$('#load-more').addClass('artdeco-button').on('click', fetchProfiles);

// postavljamo prema zadanim postavkama indeks profila za zahtjev
window.nextItemIndex = 0;

Če to izvedete neposredno v konzoli na strani iskanja, bo dodalo gumb, ki naloži 10 novih profilov ob vsakem kliku in jih prikaže v seznamu. Seveda, pred tem je potrebno spremeniti žeton in URL na potrebne. Profilni blok bo vseboval ime, položaj, lokacijo, povezavo na profil in sliko s prekrivanjem.

Obidemo omejitev iskanja na LinkedInu, igrajoč se z API-jem

Zaključek

Tako smo z minimalno napora uspeli najti ranljivo mesto in vrniti iskanje brez omejitev. Dovolj je bilo analizirati podatke in njihov potek, pogledati v samo prošnjo.

Ne morem reči, da to predstavlja resen problem za LinkedIn, ker ne nosi nobene grožnje. Največ, kar se lahko zgodi, je izguba dobička zaradi takšnih 'obhodov', ki omogočajo, da se ne plača za premium. Morda je ta odgovor strežnika potreben za pravilno delovanje drugih delov strani, ali pa je to preprosto lenoba razvijalcev, pomanjkanje virov, kar jim ne omogoča, da bi naredili dobro. (Omejitev se je pojavila januarja 2015, pred tem omejitev ni bilo).

P.S.

Seveda je koda v jQuery precej primitiven primer možnosti. Trenutno sem ustvaril razširitev za brskalnik za svoje potrebe. Dodaja kontrolne gumbe in izrisuje celovite profile s slikami, gumbom za povabilo in skupnimi povezavami. Poleg tega dinamično zbira filtre lokacij, podjetij in drugega, izlušči žeton iz piškotkov. Tako da ni več potrebno nič trdo kodirati. Prav tako dodaja dodatna polja nastavitev, kot so 'kolikšne profile naj zahteva naenkrat, do 49'.

Obidemo omejitev iskanja na LinkedInu, igrajoč se z API-jem

Na tem dodatku še vedno delam in v načrtu je, da ga objavim v odprtem dostopu. Pišite, če vas zanima.

Izvor: habr.com

Kupite zanesljivo gostovanje za spletne strani z zaščito pred DDoS, VPS VDS strežniki 🔥 Kupite zanesljivo gostovanje za spletne strani z zaščito pred DDoS, VPS VDS strežniki | ProHoster