Абыходзім ліміт пошуку LinkedIn, гуляючы з API

ліміт

Ёсць на LinkedIn такое абмежаванне - Ліміт камерцыйнага выкарыстання. Вельмі верагодна, што вы, як і я да нядаўняга часу, ніколі не сутыкаліся і не чулі пра яго.

Абыходзім ліміт пошуку LinkedIn, гуляючы з API

Сутнасць ліміту ў тым, што калі вы выкарыстоўваеце пошук людзей па-за вашымі кантактамі занадта часта (дакладных метрык няма, вырашае алгарытм, на аснове вашых дзеянняў — як часта і шмат шукалі, дадавалі людзей), то вынік пошуку будзе абмежаваны трыма профілямі, замест 1000 ( па змаўчанні 100 старонак, па 10 профіляў на старонку). Ліміт скідаецца на пачатку кожнага месяца. Натуральна, прэміум акаўнты такога абмежавання не маюць.

Але не так даўно, для аднаго пет-праекта, я пачаў шмат гуляцца з пошукам на LinkedIn і раптоўна атрымаў гэтае абмежаванне. Натуральна, такое мне не вельмі спадабалася, бо я не выкарыстоўваў яго ў якіх-небудзь камерцыйных мэтах, таму першай думкай было вывучыць абмежаванне і паспрабаваць яго абыйсці.

[Важнае ўдакладненне - матэрыялы ў артыкуле прадстаўлены выключна ў азнаямленчых і навучальных мэтах. Аўтар не заахвочвае іх выкарыстанне ў камерцыйных мэтах.]

Вывучаем праблему

Маем: замест дзесяці профіляў з пагінацыяй, пошук выдае толькі тры, пасля якіх устаўляецца блок з "рэкамендацыяй" прэміум акаўнта і ніжэй ідуць размытыя і не клікабельныя профілі.

Адразу ж рука цягнецца ў кансоль распрацоўніка, каб паглядзець гэтыя ўтоеныя профілі - магчыма, мы можам прыбраць нейкія стылі, якія ставяць блюр, або выняць інфармацыю з блока ў разметцы. Але, цалкам чакана, гэтыя профілі ўсяго толькі карцінкі-заглушкі і ніякай інфармацыі не захоўваюць.

Абыходзім ліміт пошуку LinkedIn, гуляючы з API

Добра, зараз паглядзім ва ўкладку Network і праверым, ці сапраўды спрацоўвае альтэрнатыўная выдача вынікаў пошуку, якая вяртае толькі тры профіля. Знаходзім цікавы для нас запыт да “/api/search/blended” і глядзім на адказ.

Абыходзім ліміт пошуку LinkedIn, гуляючы з API

Профілі прыходзяць у масіве `included`, але сутнасцяў у ім аж 15. У дадзеным выпадку, першыя тры з іх - аб'екты з дадатковай інфармацыяй, кожны аб'ект змяшчае інфармацыю па канкрэтным профілі (напрыклад, ці з'яўляецца профіль прэміумам).

Абыходзім ліміт пошуку LinkedIn, гуляючы з API

Наступныя 12 гэта рэальныя профілі - вынікі пошуку, з якіх нам пакажуць толькі тры. Як ужо можна здагадацца, паказвае толькі тых, на каго прыходзіць дадатковая інфармацыя (першыя тры аб'екты). Напрыклад, калі ўзяць адказ з профіля без ліміту, то прыйдзе 28 сутнасцяў - 10 аб'ектаў з доп. інфармацыяй і 18 профіляў.

Адказ для профіля без лімітуАбыходзім ліміт пошуку LinkedIn, гуляючы з API
Абыходзім ліміт пошуку LinkedIn, гуляючы з API

Чаму профіляў прыходзіць больш за 10, хоць запытваецца менавіта 10, і яны ніяк не ўдзельнічаюць у адлюстраванні, нават на наступнай старонцы іх не будзе - пакуль не ведаю. Калі прааналізаваць урл запыту, то можна ўбачыць, што count=10 (колькі профіляў вярнуць у адказе, максімум 49).

Абыходзім ліміт пошуку LinkedIn, гуляючы з API

Буду рады любым каментарам наконт гэтага.

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

Добра, самае галоўнае мы зараз сапраўды ведаем - профіляў прыходзіць у адказе больш, чым нам паказваюць. Значыць, мы можам дастаць больш дадзеных, нягледзячы на ​​ліміт. Давайце паспрабуем тузануць апі самі, прама з кансолі, пры дапамозе fetch.

Абыходзім ліміт пошуку LinkedIn, гуляючы з API

Чакана, атрымліваем памылку, 403. Гэта звязана з бяспекай, тут мы не дасылаем CSRF токен (CSRF на Вікіпедыі. Калі ў двух словах - да кожнага запыту дадаецца унікальны токен, які правяраецца на серверы на сапраўднасць).

Абыходзім ліміт пошуку LinkedIn, гуляючы з API

Яго можна скапіяваць з любога іншага паспяховага запыту ці з cookies, дзе ён захоўваецца ў поле 'JSESSIONID'.

Дзе знайсці токенЗагаловак іншага запыту:

Абыходзім ліміт пошуку LinkedIn, гуляючы з API

Або з куки, прама праз кансоль:

Абыходзім ліміт пошуку LinkedIn, гуляючы з API

Спрабуем яшчэ раз, у гэты раз перадаем у fetch налады, у якіх паказваем параметрам у header наш csrf-token.

Абыходзім ліміт пошуку LinkedIn, гуляючы з API

Поспех, нам прыходзяць усе 10 профіляў. :tada:

З-за розніцы загалоўкаў структура адказу крыху адрозніваецца ад таго, што ў прыходзіць у арыгінальным запыце. Можна атрымаць такую ​​ж структуру, калі дадаць 'Accept: 'application/vnd.linkedin.normalized+json+2.1', да нас у аб'ект, побач з csrf токенам.
Прыклад адказу з дабаўленым загалоўкамАбыходзім ліміт пошуку LinkedIn, гуляючы з API

Больш аб загалоўку Accept

Што далей?

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

На гэтым этапе ў мяне было дастаткова звестак і свабоды, каб працягваць працу над пет-праектам. Але грэх было не паспрабаваць гэтыя дадзеныя адлюстраваць прама на месцы, раз ужо яны на руках. У Ember, які выкарыстоўваецца на фронце, лезці не будзем. На сайце быў падлучаны jQuery, і адкапаўшы ў памяці ведаў базавага сінтаксісу, можна за пару хвілін стварыць наступнае.

Код на 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="/be/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="/be/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>`
    );
};

// дергаем апи, получаем данные и рендерим профили
const fetchProfiles = () => {
    // токен
   const csrf = 'ajax:9082932176494192209';
    
   // объект с настройками запроса, передаем токен
   const settings = { headers: { 'csrf-token': csrf } }

    // урл запроса, с динамическим индексом старта в конце
   const url = `https://www.linkedin.com/voyager/api/search/blended?count=10&filters=List(geoRegion-%3Ejp%3A0,network-%3ES,resultType-%3EPEOPLE)&origin=FACETED_SEARCH&q=all&queryContext=List(spellCorrectionEnabled-%3Etrue,relatedSearchesEnabled-%3Etrue)&start=${nextItemIndex}`; 
    /* делаем запрос, для каждого профиля в ответе вызываем рендер блока, и после инкрементируем стартовый индекс на 10 */
    fetch(url, settings).then(response => response.json()).then(data => {
        data.elements[0].elements.forEach(createProfileBlock);
        nextItemIndex += 10;
});
};


// удаляем все профили из списка
$('.search-results__list').find('li').remove();
// вставляем кнопку загрузки профилей
$('.search-results__list').after('<button id="load-more">Load More</button>');
// добавляем функционал на кнопку
$('#load-more').addClass('artdeco-button').on('click', fetchProfiles);

// ставим по умолчания индекс профиля для запроса
window.nextItemIndex = 0;

Калі выканаць гэта прама ў кансолі на старонцы пошуку, тое гэта дадасць кнопку, якая загружае 10 новых профіляў пры кожным націску, і што рэндыруецца іх спісам. Вядома, токен і урл перад гэтым памяняць на неабходны. Блок профілю будзе змяшчаць імя, пасаду, лакацыю, спасылку на профіль і карцінку-заглушку.

Абыходзім ліміт пошуку LinkedIn, гуляючы з API

Заключэнне

Такім чынам, пры мінімуме намаганняў, мы змаглі знайсці ўразлівае месца і вярнуць сабе пошук без абмежаванняў. Дастаткова было прааналізаваць дадзеныя і іх шлях, зазірнуць у сам запыт.

Я не магу сказаць, што гэта з'яўляецца сур'ёзнай праблемай для LinkedIn, таму што ніякай пагрозы не нясе. Максімум, гэта страчаны прыбытак з-за падобных "абыходаў", якая дазваляе не плаціць за прэміум. Магчыма, такі адказ сервера неабходны для карэктнай працы іншых частак сайта, ці ж гэта проста лянота распрацоўнікаў недахоп рэсурсаў, які не дазваляе зрабіць добра. (Абмежаванне з'явілася са студзеня 2015 года, да гэтага ліміту не было).

PS

Натуральна, код на jQuery даволі прымітыўны прыклад магчымасцяў. У дадзены момант я стварыў extension для браўзэра пад свае патрэбы. Ён дадае кнопкі кантролю і рэндэрыт паўнавартасныя профілі з малюначкамі, кнопкай запрашэння і агульнымі канэктамі. Плюс дынамічна збірае фільтры лакацый, кампаній і іншага, дастае токен з печыва. Так што нічога хардакдзіць ужо не трэба. Ну і дадае дадатковыя палі налад, а-ля "колькі профіляў запытваць за раз, да 49".

Абыходзім ліміт пошуку LinkedIn, гуляючы з API

Над гэтым дадаткам я ўсё яшчэ працую і ў планах выкласці яго ў адкрыты доступ. Пішыце, калі вам цікава.

Крыніца: habr.com

Дадаць каментар