透過 API 繞過 LinkedIn 的搜尋限制

極限值

LinkedIn上有這樣一個限制—— 商業用途限制。您很可能像我一樣,直到最近才遇到或聽說過它。

透過 API 繞過 LinkedIn 的搜尋限制

限制的本質是,如果您過於頻繁地搜尋聯絡人以外的人(沒有確切的指標,演算法根據您的操作來決定 - 您搜尋的頻率和數量、新增的人),那麼搜尋結果將限制為三個設定文件,而不是1000 個(預設100 頁,每頁10 個設定檔)。該限額在每個月初重置。自然, 高級帳戶沒有此限制.

但不久前,為了一個喜歡的項目,我開始大量使用 LinkedIn 搜索,突然遇到了這個限制。當然,我不太喜歡這個,因為我沒有將它用於任何商業目的,所以我的第一個想法是研究它的限制並嘗試繞過它。

[重要的澄清:本文中的資料僅供參考和教育目的。作者不鼓勵將其用於商業目的。]

我們正在研究這個問題

我們有:搜尋只返回三個,而不是十個帶有分頁的配置文件,之後插入一個帶有高級帳戶“推薦”的區塊,下面是模糊且不可點擊的配置文件。

立即,手伸向開發人員控制台來查看這些隱藏的配置文件 - 也許我們可以刪除一些模糊的樣式,或者從標記中的區塊中提取資訊。但是,不出所料,這些設定檔只是 佔位符圖片 並且不儲存任何資訊。

透過 API 繞過 LinkedIn 的搜尋限制

好的,現在讓我們看看「網路」選項卡,檢查僅傳回三個設定檔的替代搜尋結果是否確實有效。我們找到我們感興趣的“/api/search/blished”請求並查看回應。

透過 API 繞過 LinkedIn 的搜尋限制

配置檔案位於「included」數組中,但其中已經有15 個實體。在本例中,前三個是具有附加資訊的對象,每個對象包含有關特定配置文件的資訊(例如,配置文件是否為高級的) )。

透過 API 繞過 LinkedIn 的搜尋限制

接下來的 12 個是真實的個人資料 - 搜尋結果,其中只有三個會顯示給我們。正如您已經猜到的,它僅顯示那些接收附加資訊的物件(前三個物件)。例如,如果您從無限制的設定檔中取得答案,您將收到 28 個實體 - 10 個附加物件。資訊和 18 個設定檔。

個人資料答案無限制透過 API 繞過 LinkedIn 的搜尋限制
透過 API 繞過 LinkedIn 的搜尋限制

為什麼有超過 10 個配置文件到達,儘管正好請求了 10 個,並且它們不以任何方式參與顯示,甚至在下一頁上它們也不會參與顯示 - 我還不知道。如果分析請求 URL,您可以看到 count=10(回應中傳回的設定檔數量,最多 49)。

透過 API 繞過 LinkedIn 的搜尋限制

我很高興收到有關此事的任何評論。

我們來實驗一下

好吧,我們現在確定的最重要的事情是,回應中的個人資料比他們向我們展示的要多。這意味著儘管有限制,我們仍可以獲得更多數據。讓我們嘗試使用 fetch 直接從控制台自行提取 API。

透過 API 繞過 LinkedIn 的搜尋限制

正如預期的那樣,我們收到錯誤 403。這是由於安全原因,這裡我們不發送 CSRF 令牌(維基百科上的 CSRF。簡而言之,每個請求都會添加一個唯一的令牌,並在伺服器上檢查其真實性)。

透過 API 繞過 LinkedIn 的搜尋限制

它可以從任何其他成功的請求或 cookie 複製,儲存在「JSESSIONID」欄位中。

在哪裡可以找到令牌另一個請求的標頭:

透過 API 繞過 LinkedIn 的搜尋限制

或從cookies中,直接透過控制台:

透過 API 繞過 LinkedIn 的搜尋限制

讓我們再試一次,這次我們將設定傳遞給 fetch,其中我們將 csrf-token 指定為標頭中的參數。

透過 API 繞過 LinkedIn 的搜尋限制

成功,我們收到全部 10 份個人資料。 :田田:

由於標頭的不同,回應的結構與原始請求中收到的結構略有不同。如果將「Accept: ‘application/vnd.linkedin.normalized+json+2.1’加入我們的物件中,靠近 csrf 令牌,您可以獲得相同的結構。
添加標頭的範例響應透過 API 繞過 LinkedIn 的搜尋限制

有關 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="/zh-TW/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="/zh-TW/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 個新設定檔並將它們呈現在清單中。當然,在執行此操作之前,請將令牌和 URL 變更為所需的。個人資料區塊將包含姓名、職位、位置、個人資料連結和占位符圖像。

透過 API 繞過 LinkedIn 的搜尋限制

結論

因此,我們能夠以最小的努力找到弱點並不受限制地重新進行搜索。分析資料及其路徑、研究請求本身就足夠了。

我不能說這對 LinkedIn 來說是一個嚴重的問題,因為它不會構成任何威脅。這種「變通辦法」最大的損失就是利潤損失,它可以讓您避免支付溢價。也許這樣的伺服器回應對於網站其他部分的正確操作是必要的,或者這只是開發人員的懶惰和缺乏資源而不允許它做得很好。 (此限制於2015年XNUMX月出現;在此之前沒有限制)。

聚苯乙烯

當然,jQuery 程式碼是這些功能的一個相當原始的範例。目前我已經創建了一個瀏覽器擴充功能來滿足我的需求。它添加了控制按鈕並呈現帶有圖片、邀請按鈕和一般連接的完整配置。此外,它還動態收集位置、公司和其他事物的過濾器,並從 cookie 中檢索令牌。因此無需再對任何內容進行硬編碼。嗯,它添加了額外的設定字段,例如「一次請求多少個配置文件,最多 49 個」。

透過 API 繞過 LinkedIn 的搜尋限制

我仍在致力於此添加,並計劃將其向公眾發布。有興趣就寫吧。

來源: www.habr.com

添加評論