Многие из нас знают сайт Pikabu.ru. Это довольно популярный российский медиа-ресурс с огромной посещаемостью. Не так давно у сайта сменился владелец, началась волна довольно спорных нововведений... об одном из которых я и хотел бы написать здесь.
Практически все популярные ресурсы монетизируются тем или иным образом. Пикабу - не исключение. Тут и рекламные баннеры, и спонсорские посты, а последнее время ещё и вакансии. Вполне естественно, что пользователи не хотят видеть эту рекламу и пользуются различными плагинами типа AdBlock для её блокировки.
Да, есть сайты, которые показывают баннер на весь экран, когда определяют использование AdBlock. У многих этот баннер можно просто скрыть, нажав на соответствующую кнопку... Но это ведь не наш метод, правда? Лучше сделать так, чтобы сайт тормозил, если включен AdBlock, и свалить вину на него!
Итак, откроем код актуальных скриптов для десктопной версии сайта: https://cs.pikabu.ru/apps/ub/5.3.0/desktop/app.efa0bb5da0cd.mo.js. Где-то в нём есть вот такой кусок (слегка деобфусцировано):
10958: (t,e,s)=>{
"use strict";
s.d(e, {
P: ()=>d
});
s(88674), (66992), s(33948);
var i = s(33877), o = s(26639);
const n = ["pub300x250", "pub300x250m", "pub728x90", "text-ad", "textAd", "textad", "textads", "text-ads", "text-ad-links", "sidebar-block_placeholder", "BrokenAd"]
, r = "width:1px!important;height:1px!important;position:absolute!important;left:-10000px!important;top:-1000px!important;";
let a = false, l = 0;
const c = 60000;
async function d(t=3) { // функция, определяющая наличие AdBlock
const e = Date.now();
if (e - l < c)
return a;
const s = document.createElement("div"); // создаём div
// устанавливаем ему классы, соответствующие классам рекламных блоков
s.classList.add(...n),
s.setAttribute("style", r), // устанавливаем стиль, скрывая его
document.body.append(s), // добавляем на страницу
await (0, o.W)(), // ожидаем (o.W выполняет requestAnimationFrame)
t = Math.max(Math.floor(t) || 0, 0); // t - количество попыток, не меньше 0
do { // делаем несколько попыток в цикле
if (a = h(s), a || !t) break; // проверяем, есть ли AdBlock
await (0, i.g)(1000) // ждём (i.g выполняет setTimeout)
} while (t--);
return l = e, s.remove(), a // возвращаем флаг наличия AdBlock в переменной a
}
function h(t) { // непосредственно детектор AdBlock
// если у тела страницы есть атрибут abp - детектим AdBlock Plus
// если размеры элемента стали нулевыми (хотя в стилях ширина и высота 1),
// то значит, что-то (AdBlock) скрыло этот элемент
if (document.body.getAttribute("abp") || null === t.offsetParent || 0 === t.offsetHeight || 0 === t.offsetLeft || 0 === t.offsetTop || 0 === t.offsetWidth || 0 === t.clientHeight || 0 === t.clientWidth)
return true;
const e = window.getComputedStyle(t, null); // берём текущие стили элемента
// если элемент скрыт через display: none или visibility: hidden,
// то это тоже AdBlock
return "none" === e.display || "hidden" === e.visibility
}
}
В целом этот код очень похож на скрипт detect-adblock.js, с некоторыми изменениями. Возможно, используется какое-то схожее готовое решение - я не знаю.
Эта функция для определения AdBlock используется здесь:
(0, K.P)().then((t=>{ // K.P - это приведённая выше функция
// проверяется имя хоста - видимо, чтобы исключить localhost при разработке
const e = String(window.location.hostname).match(/pikabu\.[a-z]+/i)[0];
// генерируем букву латинского алфавита
const s = String.fromCharCode(65 + ~~((new Date).getMinutes() / 5));
// удаляем cookie с названием bs и устанавливаем новое значение:
// если AdBlock обнаружен, то буква + "1"; если нет - буква + "0"
0 === e.indexOf("pikabu") && (R.CookieStorage.remove("bs"),
R.CookieStorage.set("bs", s + (t ? "1" : "0"), {
expires: 1,
domain: "." + e
}))
}
)),
Cookie - это единственный способ незаметно отправить данные на сервер при открытии страницы в браузере, до её непосредственной загрузки и независимо от пути открываемой страницы.
Для чего же нужна эта кука? Если в ней записан "0" ("A0", "E0" и т. п.) - то буквально ничего не происходит. Если же в её значении присутствует "1" ("C1", "K1" и т. п.), то в HTML-код загружаемой страницы будет встроен следующий блок:
<script type="text/javascript">(() => {
let xhr = new XMLHttpRequest();
let header = 'llasalbbubrrl';
let nT4QtqOeG = 'https://pikabu.ru/zk8XGt3f/vnrQV4589/36d29eT5k9v/PHSIcV-PRTr7OMRGowfmn-ShbH4uV6l8mYEoNuGlnBi5osxOCjpUTnVGHBb1NIcZzXJujrhvoXwLalc1niy';
let url = '/ajax/?key=' + (() => {
let x = '';
for (let key = 0; key < 32; key++) {
x += ((Math.random() * 16) | 0).toString(16);
}
x += '-' + (Math.random().toString(10)).slice(2, 10);
return x;
})();
xhr['op'+'en']('GET',url+(Math.floor(Math.random()*8)+1)+'&vn_buff=%5B14373139%2C13566390%5D&page=4&_=77161239542451');
xhr.setRequestHeader(header, nT4QtqOeG);
xhr.onreadystatechange = () => {
if (xhr.readyState !== 4 || xhr.status !== 200) {
return;
}
eval(xhr["resp"+"onseText"]);
};
xhr['se'+'nd']();
})();
(() => {
let cookieName = 'crookie';
let userMatchedName = 'cmtchd';
let ttl = 14 * 24 * 3600 * 1000;
if (document.cookie.indexOf(cookieName) !== -1 && document.cookie.indexOf(userMatchedName) !== -1) {
return;
}
let xhr = new XMLHttpRequest();
xhr['op'+'en']('GET', 'https://http-check-headers.yandex.ru');
xhr.withCredentials = true;
xhr.onreadystatechange = () => {
if (xhr.readyState !== 4 || xhr.status !== 200) {
return;
}
if (xhr.response) {
let expires = new Date(Date.now() + ttl).toUTCString();
document.cookie = cookieName + '=' + xhr.response
+ ';path=/;domain=.pikabu.ru;Secure;SameSite=None;expires=' + expires;
expires = new Date(Date.now() + ttl / 2).toUTCString();
document.cookie = userMatchedName + '=' + btoa(String(Number(new Date()))).replace(/=/gi, '')
+ ';path=/;domain=.pikabu.ru;Secure;SameSite=None;expires=' + expires;
}
};
xhr['se'+'nd']();
})();
// здесь была длинная строчка миницифированного скрипта, которую я вырезал
</script>
Сам код выглядит слегка подозрительно, как будто какая-то малварь... но дело даже не в этом. Здесь присутствуют два XHR-запроса, выполняемых в синхронном режиме. То есть во время выполнения этих запросов браузер "подвисает". Для того, чтобы сделать их асинхронными, достаточно передать true
третьим параметром в методе XMLHttpRequest.open
. Мне кажется, про это знает буквально каждый веб-разработчик из тех, кто ещё пользуется XMLHttpRequest
. Более того, про недостаток синхронных запросов говорится во многих местах:
-
в документации к методу XMLHttpRequest.open от Mozilla:
Note: Synchronous requests on the main thread can be easily disruptive to the user experience and should be avoided; in fact, many browsers have deprecated synchronous XHR support on the main thread entirely. Synchronous requests are permitted in Workers.
-
на отдельной странице у Mozilla, посвящённой синхронным и асинхронным запросам:
Warning: Synchronous XHR requests often cause hangs on the web, especially with poor network conditions or when the remote server is slow to respond. Synchronous XHR is now deprecated and should be avoided in favor of asynchronous requests.
-
в спецификации WhatWG:
Synchronous XMLHttpRequest outside of workers is in the process of being removed from the web platform as it has detrimental effects to the end user’s experience. (This is a long process that takes many years.) Developers must not pass false for the async argument when the current global object is a Window object. User agents are strongly encouraged to warn about such usage in developer tools and may experiment with throwing an "InvalidAccessError" DOMException when it occurs.
-
в популярном российском учебнике по Javascript:
Выглядит, может быть, и неплохо, но синхронные запросы используются редко, так как они блокируют выполнение JavaScript до тех пор, пока загрузка не завершена. В некоторых браузерах нельзя прокручивать страницу, пока идёт синхронный запрос. Ну а если же синхронный запрос по какой-то причине выполняется слишком долго, браузер предложит закрыть «зависшую» страницу.
Да и буквально везде. Единственное, чем можно объяснить наличие синхронных запросов - это желание насолить пользователям и заставить их отключить AdBlock на сайте. И администрация ресурса обвиняет в тормозах именно блокировщики рекламы:
Выводы? А их нет. Просто вот такая монетизация. "Виноваты не мы, виноват AdBlock, не пользуйтесь им, смотрите больше нашей рекламы".
UPD 1: Скрипт, загружаемый таким странным способом, оказался безвредным и содержащим код рекламной системы AdFox от Яндекса. В этом легко убедиться, скопировав значение переменной nT4QtqOeG
в строку браузера и перейдя по ней. Короче говоря, если одна реклама режется - затормозим браузер и будем показывать другую рекламу.
Также появился официальный ответ от администрации. Верить им или нет - решайте сами.
Комментарии (24)
Javian
18.09.2023 17:27+3Наверное это для авторизованных пользователей? Без авторизации у меня не наблюдаются "тормоза" интерфейса.
Stam_emg
18.09.2023 17:27как вариант - это может быть волнами и не у всех. я вот "накопил" за 10 лет чтобы в настройках профиля выключить рекламу. с ublock тормозов не наблюдаю. но может и совпадение, не разбирался, тк лень.
kalmarius
18.09.2023 17:27+1А я наблюдаю - причем даже в мобильной Опере с включенным встроенным блокировщиком (на планшете, версия сайта не мобильная). Реклама отключена в настройках.
sasmoney
18.09.2023 17:27+2Не вижу смысла в adblock на норм сайтах, на которых действительно думают головой и вставляют блоки так чтобы они не мешали просмотру страницы
click0
И какие правила для Adblock нужно добавить, чтоб "тормоза" прекратились?
ertaquo Автор
В одном из списков заметил такую строку фильтра для AdBlock Plus:
Возможно, поможет, не проверял.
Kvazzzar
Проверил, не работает :(
bogolt
открыть файл
/etc/hosts
и отредактировать его, чтобы первая строка выглядела так:
127.0.0.1 localhost pikabu.ru
теперь каждый раз когда вы его случайно откроете чтобы смыть в унитаз пару часов своей жизни, ваш браузер радостно напомнит вам что вы его заблокировали, а дальше вы и сами вспомните зачем это сделали.
Boilerplate
Я примерно так и сделал пол года назад. Несколько дней назад с мобилки зашел на него, в топе горячего было тема "Их герой", что-то про Бандеру и мужские члены. 3к плюсов, 8 часов висело к тому времени на сайте. Это даже не пропаганда, а какое-то днищенское дно уровня надписей на заборе. Я тогда знатно прифигел.
pantsarny
Лучше 0.0.0.0, не будет ожидания и таймаута
Voiddancer
Я так и сделал пару лет назад, потом ещё на роутере для телефона заблочил. Существенно помогло.
Akr0n
Неужели такая зависимость развивается, что надо идти на такие меры?
Voiddancer
Я так понимаю, у меня что-то вроде СДВГ, это один из симптомов.
vvbob
Как и большинство других подобных лент. Надо работать, но при любом небольшом перерыве от скуки заходишь полистать и залипаешь на часы. Тоже пришел к выводу что надо как минимум не держать открытой вкладки с такими сайтами. Так что оно может быть и хорошо, что все там всё всрато работает, меньше соблазна туда ходить.
shasoftX
Хабр с удовольствием бы хотел стать таким же наркосайтом, чтобы пользователи часами на нем сидели. Но не срастается.
vvbob
А в чем проблема? Выкинуть всю эту идиотскую систему кармы, разрешить постить в ленту все что угодно, от сисек до "горячих новостей", модерирование свести к минимуму, и вуаля, готова очередная инфопомойка для любителей скроллинга ленты.
yarkov
Вот именно так я и поступил полгода назад. Ни капли не жалею ))
alek0585
Можно попробовать пропатчить
XMLHttpRequest
чтобы он всегда отсылал асинхронные запросы. По идее будет работать пока программисты из пикабу не добавят скрипт, где начнут фибоначи вычислять при детекте адблока.denchik09
что-то вроде:
встроить можно, например, в userscript и запускать через tampermonkey/greasemonkey
MatVL
Перестать заходить на пикабу