Скачиваем оценки с kinopoisk.ru

Nov 4, 2020 23:02 · 706 words · 4 minute read python

У меня есть некоторая паранойя касательно моих данных. Мне постоянно кажется, что я могу их потерять. Поэтому, я стараюсь делать бэкапы всего, что только можно: фото, видео, проектов… и рейтинга просмотренных фильмов. Один раз меня это уже спасло - когда внезапно закрылся ImhoNet. Тогда я перебрался на Kinopoisk. И что-то не уверен в правильности своего решения. После того, как он убил аккаунт жены, добавив туда кучу мусора, я решил проверить не случилось ли это с моим, а заодно и сделать бэкап. Удивительно, но я не нашёл этой функции в интерфейсе. Хотя казалось бы, это мои данные, и я должен уметь их скачивать. Интересно, что они говорят гражданам ЕС, т.к. GDPR явно требует этой функциональности?!

Обращение в техподдержку ситуацию не улучшило - мне прислали ссылку с отпиской. “Дорогие пользователи, вам эта функция не нужна. Просто доверьтесь Яндекс.” Тоже мне джедаи, довериться им… Причём, судя по ответам, Кинопоиск имел такую функцию раньше, но удалил. И это лишь первый “Yandex, WTF?!".

Куки Kinopoisk

Никакого API я не нашёл, так что самый простой способ - написать скрапер для парсинга страниц с оценками. Спасибо, что можно увеличить количество объектов на странице до 200. Это позволило мне обойтись всего десятком запросов. Я воспользовался Scrapy как одним из популярных фреймворков для парсинга. Полный код можно посмотреть на GitHub, но ничего технически интересного там нет. Скрапер как скрапер - со своими заморочками и обходами “защиты”. Но мне хотелось бы остановиться на куках (они ж нужны для работы с профилем). Когда я открыл страницу, то был слегка поражён - 50 кук! 50, Карл! “Яндекс, WTF?!” Из интересного:

  • есть куки для названия кнопок: “profile_button”, “mykp_button”
  • ваша геопривязка аж в 4х куках: “user-geo-region-id”, “user-geo-country-id”, “location”, “user_country”
  • ваши идентификаторы для всех сервисов Яндекса: “yandexuid”, “_ym_uid”, “yuidss”, “yandex_gid”, “yandex_login”, “uid”…
  • какие-то ключи с сигнатурами: “desktop_session_key”, “yandex_ugc_rating_status” (не проще было бы JWT использовать? ах да, NIH)
  • не смогли договориться csrf и определили 2 куки: “_csrf” и “_csrf_csrf_token”
  • булевые флаги: “mda_exp_enabled”, “gdpr”, “mda”, “undefinedClosed”… Причём некоторые в формате 0/1, другие в yes/no, а некоторые true/false
  • куча сессий, включая “PHPSESSID”, который, кстати, не используется

Да и вообще используются на этой странице всего 2 куки: “location” и “ya_sess_id”. 2, Карл! Я знаю, что куки действуют на весь сайт, и одна страница не показательна. Но можно было бы договроиться об идентификаторах, а некоторые настройки использовать с сервера. Хотя зачем? Кому нужна такая оптимизация кроме пользователей?

Пока далеко не ушли, вот куки, возвращаемые сервером. Видимо, проще продублировать нежели договориться внутри компании:

куки для отслеживания

Как Kinopoisk скрывает оценки

Повторюсь, сам скрапер довольно обычный за исключением самой важной части - оценок. Они зашифрованы. Зашифрованы, Карл! На фронтенде! “Yandex, WTF?!” Вместо циферки в оранжевом квадратике на страницу приходит вот такой код:

<script>eval(decodeURIComponent(HVZG(atob(`QAl1E1dTQAsEE1dTQAsEE1dTQAsEE1dTQAsEE1dTQAsEE1dTQAsEE1dTQAsEE1dTQAsEE1dTQAsEE1dTQAsEE1dTQAsEE1dTQAsEE1dTQAsEE1dTQAsEE1dTQAsEE1dTQAsEE1dTQAsEE1dTQAsEE1dTQAsEQxc8AVhAV0sTEEpcE1dbQA52UAwPCBwHd0BRVQwFDlVQUhwGdUBRVUtVQgwNAhwHd0BRVRwGAV1GVw4RBCZGVwlBRQAROlpbUgBGVngRBFVGVw4CBVNUAwlRDlxbXFhXB10HVwkMVAdSUgkND1BQUABXVUBRUhwGdUBRVVZWXEBQJBwGBkBRURwGDkBRUhwGBRcCEVBaUToWFlxGaVBSXQkHAUBRUhwGD0BUIRwGD0BQJxwEd0BRVRwGBkBRVRwGBkBRVRwGBkBRVRwGBkBRVRwGBkBRVRwGBkBRVRwGBkBRVRwGBkBRVRwGBkBRVRwGBkBRVRwGBkBRVRwGBkBRVRwGBkBRVRwGBkBRVRwGBkBRVRwGBkBRVRwGBkBRVRwGBkBRVRwGBg==`),`e946ec`)));function HVZG(data,key){var result=[];for(var i=0;i<data.length;i++){var xored=data.charCodeAt(i)^key.charCodeAt(i%key.length);result.push(String.fromCharCode(xored));}return result.join(``);}</script>

Посмотрим внимательнее на функцию HVZG (кстати, она меняет название каждый день):

function HVZG(data,key) {
    var result = [];
    for (var i=0; i<data.length; i++) {
        var xored = data.charCodeAt(i) ^ key.charCodeAt(i % key.length);
        result.push(String.fromCharCode(xored));
    }
    return result.join(``);
}

Т.е. это обычный XOR по ключу. Зачем, Яндекс?! Ты действительно считаешь, что это как-то поможет от скачивания оценок?! Мой лоб уже начал болеть от фейспалмов.

Анализ производительности страницы

Но это ещё не всё - там же ещё eval. Так вот, расшифрованные данные:

ur_data.push({film: 462553, rating: '8', user_code: 'b3f65449b07e86feda2565540', obj: $('#rating_user_462553')});

Т.е. там не просто циферка, там кусок кода, который выполняется после окончания загрузки! Интересно, как это аффектит производительность?

дамп Google Chrome

Через полчаса процессор всё ещё был загружен на 20%, но расход памяти уменьшился до 400Mb. “Yandex, WTF?!” Что ты говорил про заботу о пользователях? Ах да, забыл упомянуть, что этот скрипт вставляется в каждую строку с фильмом! Т.е. эта функция у меня на странице продублирована минимум 200 раз. 200 раз, Карл! До такого уровня мне действительно ещё очень и очень далеко.

Заключение

Оценки-то я скачал, но моё чувство прекрасного было изнасиловано разработчиками Кинопоиска. Тонны мусора, нелепые попытки скрыть оценки, eval'ы… Я представляю какой бардак творится при разработке, если на выходе такое. И, глядя на культуру разраобтки, я думаю “действительно ли я доверяю Яндекс, и не пора бы свалить на IMDB?". Да, там другой менталитет, и оценки некоторых фильмов могут быть не релевантны, но он хотя бы действительно заботится о пользователях.

P.S.

А после всех оценок есть кнопка “Удалить оценки или заменить их на просмотры”. Ага, так легко потерять свои данные. Она - единственное, что отделяет вас от этого.