Apiet LinkedIn meklēŔanas ierobežojumus, spēlējot ar API

Limits

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

Apiet LinkedIn meklēŔanas ierobežojumus, spēlējot ar API

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

However, not long ago, for a pet project, I started experimenting a lot with LinkedIn searches and suddenly encountered this limitation. Naturally, I was not pleased, as I wasn't using it for any commercial purposes, so my first thought was to research the limit and try to bypass it.

[Important clarification — the materials in the 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 only returns three, after which there is a block suggesting a premium account, and below are blurred and non-clickable profiles.

Immediately, one reaches for the developer console to check these hidden profiles — perhaps we can remove some styles that apply the blur, or extract information from the block in the markup. But, quite predictably, these profiles are just placeholder images and do not store any information.

Apiet LinkedIn meklēŔanas ierobežojumus, spēlējot ar API

Okay, now let's look in the Network tab and check if the alternative search result returns only three profiles. We find the request to "/api/search/blended" and look at the response.

Apiet LinkedIn meklēŔanas ierobežojumus, spēlējot ar API

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

Apiet LinkedIn meklēŔanas ierobežojumus, spēlējot ar API

Nākamie 12 ir reāli profili — meklēŔanas rezultāti, no kuriem mums rādÄ«s tikai trÄ«s. Kā jau var nojaust, parādÄ«s tikai tos, par kuriem nāk papildus informācija (pirmie trÄ«s objekti). Piemēram, ja ņem vērā atbildi no profila bez limita, tad saņemsim 28 entitātes — 10 objektus ar papildu informāciju un 18 profilus.

Atbilde profilam bez limitaApiet LinkedIn meklēŔanas ierobežojumus, spēlējot ar API
Apiet LinkedIn meklēŔanas ierobežojumus, spēlējot ar API

Kāpēc profilu nāk vairāk nekā 10, lai gan tiek pieprasÄ«ti tieÅ”i 10, un tie nekādā veidā nepiedalās attēloÅ”anā, pat nākamajā lapā to nebÅ«s — pagaidām nezinu. Ja analizējam pieprasÄ«juma URL, tad var redzēt, ka count=10 (cik profilu atgriezt atbildē, maksimāli 49).

Apiet LinkedIn meklēŔanas ierobežojumus, spēlējot ar API

BūŔu priecīgs par jebkādiem komentāriem Ŕajā jautājumā.

Eksperimentējam

Labi, vissvarÄ«gākais, ko tagad noteikti zinām — profilu atnāk vairāk, nekā mums parāda. Tas nozÄ«mē, ka varam iegÅ«t vairāk datus, neskatoties uz limitu. Mēģināsim izsist API paÅ”i, tieÅ”i no konsoles, ar fetch palÄ«dzÄ«bu.

Apiet LinkedIn meklēŔanas ierobežojumus, spēlējot ar API

GaidoÅ”i, saņemam kļūdu, 403. Tas saistÄ«ts ar droŔību, Å”eit mēs nesÅ«tām CSRF tokenu (CSRF Vikipēdijā. Ja divos vārdos — katram pieprasÄ«jumam pievieno unikālu tokenu, kuru pārbauda serverÄ« uz autentiskumu).

Apiet LinkedIn meklēŔanas ierobežojumus, spēlējot ar API

To var nokopēt no jebkura cita veiksmīga pieprasījuma vai arī no sīkdatnēm, kur tas tiek glabāts laukā 'JSESSIONID'.

Kur atrast tokenuCita pieprasījuma galvene:

Apiet LinkedIn meklēŔanas ierobežojumus, spēlējot ar API

Vai no sÄ«kdatnēm, tieÅ”i caur konsoli:

Apiet LinkedIn meklēŔanas ierobežojumus, spēlējot ar API

Mēģinām vēlreiz, Å”oreiz pārsÅ«tām fetch iestatÄ«jumus, kuros norādām parametrā galvenē mÅ«su csrf-token.

Apiet LinkedIn meklēŔanas ierobežojumus, spēlējot ar API

Veiksmīgi, mēs saņemam visus 10 profilus. :tada:

Sakarā ar atŔķirÄ«bu galvenēs atbildes struktÅ«ra nedaudz atŔķiras no tās, kas nāk oriÄ£inālajā pieprasÄ«jumā. Var iegÅ«t tādu paÅ”u struktÅ«ru, ja pievieno 'Accept: 'application/vnd.linkedin.normalized+json+2.1', mÅ«su objektam, blakus csrf tokenam.
Atbildes piemērs ar pievienoto galveniApiet LinkedIn meklēŔanas ierobežojumus, spēlējot ar API

Vairāk par Accept galveni

Kas tālāk?

Tālāk var rediģēt (ar roku vai automatizēt) parametru `start`, kas norāda uz indeksu, sākot no kura mums atdos 10 profilus (noklusējuma = 0) no visa meklēŔanas rezultāta. Citiem vārdiem sakot, palielinot to par 10 pēc katra pieprasÄ«juma, mums iegÅ«st klasisku lapu izsniegÅ”anu, pa 10 profiliem katrā reizē.

Å ajā posmā man bija pietiekami daudz datu un brÄ«vÄ«bas, lai turpinātu darbu pie sava pet-projekta. Bet bÅ«tu grēks neizmantot Å”os datus tieÅ”i vietā, ja tie ir pieejami. Ember, kas tiek izmantots frontendā, netiks skarts. Vietnē bija pieslēgts jQuery, un, atsaucoties uz atmiņā saglabātajām pamatzināŔanām, var dažu minūŔu laikā izveidot sekojoÅ”o.

Kods uz 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="/lv/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="/lv/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>`
 );
};

// izsaucam API, iegūstam datus un izveidojam profilus
const fetchProfiles = () =&gt; {
 // token
 const csrf = 'ajax:9082932176494192209';
 
 // objekts ar pieprasījuma iestatījumiem, nododam tokenu
 const settings = { headers: { 'csrf-token': csrf } }

 // pieprasījuma URL, ar dinamisku sākuma indeksu beigās
 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}`; 
 /* veicam pieprasījumu, katram profilam atbildē izsaucam bloka izveides funkciju, un pēc tam inkrementējam sākuma indeksu par 10 */
 fetch(url, settings).then(response =&gt; response.json()).then(data =&gt; {
 data.elements[0].elements.forEach(createProfileBlock);
 nextItemIndex += 10;
});
};


// dzēšam visus profilus no saraksta
$('.search-results__list').find('li').remove();
// ievietojam profilu ielādes pogu
$('.search-results__list').after('<button id="load-more">Ielādēt vairāk</button>');
// pievienojam funkcionalitāti pogai
$('#load-more').addClass('artdeco-button').on('click', fetchProfiles);

// iestata noklusējuma profila indeksu pieprasījumam
window.nextItemIndex = 0;

Ja to izpildÄ«s tieÅ”i lapas meklēŔanas konsolē, tas pievienos pogu, kas ielādē 10 jaunus profilus katru reizi, kad tā tiek nospiesta, un izsauc tos sarakstā. Protams, pirms tam ir jāmaina piekļuves tokens un URL uz vajadzÄ«go. Profila bloks saturēs vārdu, amatu, atraÅ”anās vietu, saiti uz profilu un attēlu-zemÅ«dens attēlu.

Apiet LinkedIn meklēŔanas ierobežojumus, spēlējot ar API

Secinājums

Tādējādi ar minimālu piepÅ«li mēs spējam atrast ievainojamÄ«bu un atgriezt meklēŔanu bez ierobežojumiem. Bija jāanalizē dati un to ceļŔ, jāiekļūst paŔā pieprasÄ«jumā.

Es nevaru teikt, ka tas ir nopietns problēmas jautājums LinkedIn, jo tas nerada nekādas briesmas. Maksimums, tas ir pazaudēta peļņa no Ŕādiem "apmēriem", kas ļauj nemaksāt par premium. Iespējams, Ŕāda servera atbilde ir nepiecieÅ”ama citu vietnes daļu normālai darbÄ«bai, vai varbÅ«t tas ir vienkārÅ”i izstrādātāju nevērÄ«ba, viņu resursu trÅ«kums neļauj izdarÄ«t labu darbu. (Ierobežojums parādÄ«jās no 2015. gada janvāra, pirms tam ierobežojumu nebija.).

P.S.

Protams, kods uz jQuery ir diezgan primitÄ«vs Ŕādu iespēju piemērs. Å obrÄ«d esmu izveidojis pārlÅ«kprogrammas paplaÅ”inājumu savām vajadzÄ«bām. Tas pievieno kontroles pogas un renderē pilnvērtÄ«gus profilus ar attēliem, uzaicinājuma pogu un kopējiem savienojumiem. Plus, tas dinamiski vāc atraÅ”anās vietu, uzņēmumu un citu filtru datus, iegÅ«st token no sÄ«kdatnēm. Tādējādi nav nepiecieÅ”ams nekādas koda Ā«grÅ«tiĀ». Un pievieno papildu iestatÄ«jumu laukus, piemēram, "cik profilus pieprasÄ«t vienlaicÄ«gi, lÄ«dz 49".

Apiet LinkedIn meklēŔanas ierobežojumus, spēlējot ar API

Es joprojām strādāju pie Ŕī papildinājuma un plānoju to izdot publiskai pieejamÄ«bai. Rakstiet, ja jums tas interesē.

Avots: habr.com

Iegādājieties uzticamu tÄ«mekļa hostingu ar DDoS aizsardzÄ«bu, VPS VDS serveriem šŸ”„ Iegādājieties uzticamu tÄ«mekļa hostingu ar DDoS aizsardzÄ«bu, VPS VDS serveriem | ProHoster