通过使用API​​绕过LinkedIn搜索限制

[因为我的扩展名得到了外国观众的广泛关注,所以我将我的原始文章翻译成了英文]

极限值


作为最受好评的专业网络,对于免费帐户,LinkedIn受到商业使用限制 (CUL)的限制 。 最有可能的是,直到最近,您和我一样从未遇到过,也从未听说过这件事。

图片

CUL的要点是,当您过多地搜索连接/网络外部的人员时,搜索结果将受到限制,仅显示3个配置文件而不是1000(默认情况下为100页,每页10个配置文件)。 没有人知道,如何测量“经常”,没有精确的指标; 该算法根据您的操作来决定它-您搜索的频率和添加的连接数。 免费的CUL会在每个日历月1日的PST午夜重置,您会再次获得1000个搜索结果,谁知道会持续多久。 当然, 高级帐户没有这样的限制

但是,不久前,我开始在LinkedIn搜索中搜索一些宠物项目,然后突然陷入了这个CUL中。 显然,我不太喜欢它。 毕竟,我还没有将搜索用于任何商业目的。 因此,我的第一个想法是探索此限制并尝试绕过它。

[重要说明-本文中的所有源材料仅供参考和教育用途。 作者不鼓励将其用于商业目的。]


重要更新-LinkedIn考虑了此后门,最近他们已对其进行了修复。 后门和插件不再起作用。



研究问题


我们得到的是:搜索工具显示的不是3个带有分页的配置文件,而是3个,然后是“建议”购买高级帐户,然后您看到其他模糊的不可点击的配置文件。

作为开发人员,您首先要打开“浏览器开发人员工具”来检查那些隐藏/模糊的配置文件-也许您可以删除一些导致模糊的样式,或者从布局/标记块中提取数据。 但是,可以预期,这些配置文件仅显示为占位符图像 ,不包含任何数据。

图片

好的,现在让我们转到“网络”标签,检查替代搜索结果是否确实仅返回3个配置文件。 因此,我们找到了我们感兴趣的查询-“ / api / search / blended”-并查看响应。

图片

配置文件包含在“ included”数组中,但其中已经有15个实体。 在这种情况下,它们中的前3个是带有附加数据的对象-每个对象都包含有关特定配置文件的信息(例如,如果配置文件是高级配置文件)。

接下来的12个配置文件是真实的-实际上就是这些搜索结果,其中只有3个显示给我们。 正如您已经猜到的,LinkedIn仅显示那些接收了其他信息的配置文件(前三个对象)。 例如,如果您无限制地浏览配置文件中的搜索响应,则将返回28个实体-10个具有附加信息的对象和18个配置文件。

对配置文件的响应没有限制
图片

图片

我不确定为什么为什么要返回10个以上的配置文件,而请求10个配置文件却又不显示给用户-即使在下一页也看不到它们。 如果分析请求URL,您会看到count = 10(响应中返回多少个配置文件,最大值为49)。

图片

对此问题的任何评论将不胜感激。

实验性


好的,现在我们确定最重要的事情是-响应附带的个人资料比LinkedIn向我们显示的更多。 因此,尽管有CUL,我们仍可以获得更多数据。 让我们尝试使用提取功能直接从控制台中提取API。

图片

不出所料,我们收到403错误。 这是一个安全问题-在这里,我们不发送CSRF令牌( 维基百科上为CSRF)。 简而言之,它是添加到每个请求的唯一令牌,并在服务器端检查其真实性。

图片

您可以从其他任何成功请求或从存储在“ JSESSIONID”字段中的cookie复制它。

在哪里找到代币
其他要求的标题:

图片

从cookie,直接通过控制台:

图片

让我们再试一次; 这次,我们将设置传递给fetch。 在设置中,我们将CSRF令牌指定为标头中的参数。

图片

成功,将返回所有10个配置文件。 塔达
由于头中的差异,响应结构与原始请求中的响应结构略有不同。 如果您将'Accept:'application / vnd.linkedin.normalized + json + 2.1'添加到CSRF令牌旁边的对象中,则可以获得完全相同的结构。

添加了标头的响应样本
图片

有关接受标题的更多信息

接下来是什么?


现在您可以编辑(手动或自动)指定索引的“ start”参数,从该参数开始,我们可以从整个搜索结果中接收10个配置文件(默认= 0)。 换句话说,在每次请求后将其增加10,我们得到通常的分页,每页10个配置文件。

在这个阶段,我有足够的数据和自由来继续进行这个宠物项目。 但是如果不尝试立即显示这些数据将是一个罪过,因为它已经掌握在我手中。 我们不会深入探讨用于前端的Ember。 由于JQuery已集成在网站上,因此您可以从内存中挖掘出基本语法的知识,并在几分钟内创建以下内容。

jQuery代码
/* render the block, receive profile data, and insert the block in the profiles' list, using this data */ 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="/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="/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>` ); }; // fetch data and render the profiles const fetchProfiles = () => { // token const csrf = 'ajax:9082932176494192209'; // bject with the request settings, pass the token const settings = { headers: { 'csrf-token': csrf } } // request URL, with a dynamic start index at the end 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}`; /* make a request, for each profile in the response call the block rendering, and then increment the starting index by 10 */ fetch(url, settings).then(response => response.json()).then(data => { data.elements[0].elements.forEach(createProfileBlock); nextItemIndex += 10; }); }; // delete all profiles from the list $('.search-results__list').find('li').remove(); // insert the 'download profiles' button $('.search-results__list').after('<button id="load-more">Load More</button>'); // add the functionality to the button $('#load-more').addClass('artdeco-button').on('click', fetchProfiles); // set the default profile index for the request window.nextItemIndex = 0; 

如果您直接在搜索页面上的控制台中执行此操作,则将添加一个按钮,该按钮每次单击都会加载10个新的配置文件,并将其呈现为列表。 当然,您必须将令牌和URL适当地更改为必要的令牌和URL。 概要文件块将包含名称,工作位置,位置,概要文件的链接和占位符图像。

图片

结论
因此,我们只需花费很少的精力,就可以找到一个薄弱环节并重新获得搜索选项,而没有任何限制。 分析数据及其路径并查看查询本身就足够了。

我不能说这对LinkedIn是一个严重的问题,因为它不构成威胁。 最糟糕的情况是由于这种“绕过”而导致的利润损失,从而使您无法购买高级帐户。 也许,这种服务器的响应对于网站其他部分正常工作是必要的,或者这仅仅是开发人员缺乏资源的懒惰,这不允许做得更好(CUL于2015年1月出现,当时没有限制完全没有)。

聚苯乙烯


老ps
当然,jQuery代码是功能的非常原始的示例。 目前,我已经创建了一个浏览器扩展程序来满足我的需求。 它添加了控制按钮并使用图片,邀请按钮和相互连接来渲染功能齐全的配置文件。 此外,它动态收集位置,公司和其他事物的过滤器,并从cookie中提取令牌。 因此,您无需进行任何硬编码。 此外,该扩展程序还会添加其他设置字段,例如“一次请求多少个配置文件,最多49个”。

例子

我仍在开发此扩展程序,并计划将其公开发布。
给我发短信,如果你有兴趣。

出于普遍需求将该扩展作为开源产品发布,我创建了一个浏览器扩展并将其发布以供公众使用(免费甚至没有矿工)。 它不仅具有极限旁路功能,还具有其他一些功能。 您可以查看一下并在这里下载-adam4leos.imtqy.com

由于这是Alpha版本,因此请立即通知我有关错误,您的想法,甚至有关
酷炫的UI。 我会不断完善扩展程序,并会不时发布新版本。

重要更新-LinkedIn考虑了此后门,最近他们已对其进行了修复。 后门和插件不再起作用。

Source: https://habr.com/ru/post/zh-CN466801/


All Articles