限制
LinkedIn上有这样一个限制——
限制的本质是,如果您过于频繁地搜索联系人之外的人(没有确切的指标,算法根据您的操作来决定 - 您搜索的频率和数量、添加的人),那么搜索结果将限制为三个配置文件,而不是 1000 个(默认 100 页,每页 10 个配置文件)。 该限额在每个月初重置。 自然,
但不久前,为了一个喜欢的项目,我开始大量使用 LinkedIn 搜索,突然遇到了这个限制。 当然,我不太喜欢这个,因为我没有将它用于任何商业目的,所以我的第一个想法是研究它的限制并尝试绕过它。
[重要的澄清:本文中的材料仅供参考和教育目的。 作者不鼓励将其用于商业目的。]
我们正在研究这个问题
我们有:搜索只返回三个,而不是十个带分页的配置文件,之后插入一个带有高级帐户“推荐”的块,下面是模糊且不可点击的配置文件。
立即,手伸向开发人员控制台来查看这些隐藏的配置文件 - 也许我们可以删除一些模糊的样式,或者从标记中的块中提取信息。 但是,不出所料,这些配置文件只是
好的,现在让我们看看“网络”选项卡,检查仅返回三个配置文件的替代搜索结果是否确实有效。 我们找到我们感兴趣的“/api/search/blished”请求并查看响应。
配置文件位于“included”数组中,但其中已经有 15 个实体。在本例中,前三个是具有附加信息的对象,每个对象包含有关特定配置文件的信息(例如,配置文件是否是高级的) )。
接下来的 12 个是真实的个人资料 - 搜索结果,其中只有三个会显示给我们。 正如您已经猜到的,它仅显示那些接收附加信息的对象(前三个对象)。 例如,如果您从无限制的配置文件中获取答案,您将收到 28 个实体 - 10 个附加对象。 信息和 18 个配置文件。
个人资料答案无限制
为什么有超过 10 个配置文件到达,尽管正好请求了 10 个,并且它们不以任何方式参与显示,甚至在下一页上它们也不会参与显示 - 我还不知道。 如果分析请求 URL,您可以看到 count=10(响应中返回的配置文件数量,最多 49)。
我很高兴收到有关此事的任何评论。
我们来实验一下
好吧,我们现在确定的最重要的事情是,响应中的个人资料比他们向我们展示的要多。 这意味着尽管有限制,我们仍可以获得更多数据。 让我们尝试使用 fetch 直接从控制台自行提取 API。
正如预期的那样,我们收到错误 403。这是由于安全原因,这里我们不发送 CSRF 令牌(
它可以从任何其他成功的请求或 cookie 中复制,存储在“JSESSIONID”字段中。
在哪里可以找到令牌另一个请求的标头:
或者从cookies中,直接通过控制台:
让我们再试一次,这次我们将设置传递给 fetch,其中我们将 csrf-token 指定为标头中的参数。
成功,我们收到全部 10 份个人资料。 :田田:
由于标头的不同,响应的结构与原始请求中收到的结构略有不同。 如果将“Accept: 'application/vnd.linkedin.normalized+json+2.1”添加到我们的对象中(csrf 令牌旁边),您可以获得相同的结构。
添加标头的示例响应
有关 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-CN/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-CN/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 更改为所需的。 个人资料块将包含姓名、职位、位置、个人资料链接和占位符图像。
结论
因此,我们能够以最小的努力找到弱点并不受限制地重新进行搜索。 分析数据及其路径、研究请求本身就足够了。
我不能说这对 LinkedIn 来说是一个严重的问题,因为它不会构成任何威胁。 这种“变通办法”最大的损失就是利润损失,它可以让您避免支付溢价。 也许这样的服务器响应对于网站其他部分的正确操作是必要的,或者这只是开发人员的懒惰和缺乏资源而不允许它做得很好。 (该限制于2015年XNUMX月出现;在此之前没有限制)。
PS
当然,jQuery 代码是这些功能的一个相当原始的示例。 目前我已经创建了一个浏览器扩展来满足我的需求。 它添加了控制按钮并呈现带有图片、邀请按钮和一般连接的完整配置文件。 此外,它还动态收集位置、公司和其他事物的过滤器,并从 cookie 中检索令牌。 因此无需再对任何内容进行硬编码。 嗯,它添加了额外的设置字段,例如“一次请求多少个配置文件,最多 49 个”。
我仍在致力于此添加,并计划将其向公众发布。 有兴趣就写吧。
来源: habr.com