惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Scott Helme
Scott Helme
N
Netflix TechBlog - Medium
AI
AI
Security Latest
Security Latest
GbyAI
GbyAI
P
Proofpoint News Feed
Y
Y Combinator Blog
A
Arctic Wolf
G
Google Developers Blog
U
Unit 42
爱范儿
爱范儿
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
V
Vulnerabilities – Threatpost
Know Your Adversary
Know Your Adversary
Cisco Talos Blog
Cisco Talos Blog
T
Tor Project blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
T
Threatpost
L
Lohrmann on Cybersecurity
C
CERT Recently Published Vulnerability Notes
C
Check Point Blog
B
Blog RSS Feed
The GitHub Blog
The GitHub Blog
Microsoft Azure Blog
Microsoft Azure Blog
博客园 - 【当耐特】
博客园 - Franky
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
C
Cisco Blogs
云风的 BLOG
云风的 BLOG
NISL@THU
NISL@THU
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Microsoft Security Blog
Microsoft Security Blog
T
The Blog of Author Tim Ferriss
阮一峰的网络日志
阮一峰的网络日志
Latest news
Latest news
L
LINUX DO - 最新话题
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
美团技术团队
WordPress大学
WordPress大学
L
LangChain Blog
Stack Overflow Blog
Stack Overflow Blog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
酷 壳 – CoolShell
酷 壳 – CoolShell
大猫的无限游戏
大猫的无限游戏
The Hacker News
The Hacker News
Simon Willison's Weblog
Simon Willison's Weblog
V
V2EX
Project Zero
Project Zero
博客园_首页

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Цены в долларах на Kufar.by
comerc · 2026-05-20 · via Все публикации подряд на Хабре

Время на прочтение5 мин

Охват и читатели0

Кейс

Kufar.by - это примерно как avito.ru, только в Беларуси. После очередного “улучшения” там стало невозможно выбирать авто и недвижимость: цены показываются только в белорусских рублях, хотя рынок всегда будет в долларах (боже, храни Америку). Поэтому я сделал небольшой Chrome Extension, который добавляет рядом ориентировочную цену в долларах. Пока только для авто и недвиги. И да, по ощущениям, ЛПРы, которые это выкатывали, никогда не покупали ни то ни другое на своём сайте.

Пролог

Попытка #1

Цена в долларах. Верните обратно! Невозможно ориентироваться.

Первоначальное сообщение с претензией (которое отправлял в форме на сайте) не цитируется в почте, только ответ. Что уже странно, но “это база”. Получил вот такие волшебные ответы:

Ответ #1

По белорусскому законодательству расчёты и ценообразование внутри страны должны производиться в национальной валюте, то есть в белорусских рублях.

Раньше продавцы часто указывали цены в долларах, так как рынок недвижимости исторически был привязан к иностранной валюте. Чтобы не вводить покупателей в заблуждение и избежать путаницы (например, когда цена написана в долларах, а подразумевается в белорусских рублях), мы приняли решение изменить отображение цен на объекты недвижимости, приведя все объявления к единому стандарту.

Данное решение также основано на новых требованиях законодательства, касающихся оформления договоров с агентствами недвижимости, где цены фиксируются исключительно в белорусских рублях.

При этом мы стремимся максимально сохранить удобство для наших пользователей: возможность подачи объявления и фильтрацию предложений по валюте мы оставляем. Это позволяет вам по-прежнему работать с привычными ориентирами и находить наиболее подходящие варианты.

Если остались вопросы, то, пожалуйста, спрашивайте. Буду рада помочь.

Попытка #2

Вы не думаете о своих пользователях. Сижу с калькулятором и пересчитываю цену каждого объявления. Кто это придумал… С другой стороны, у конкурентов есть возможность извлечь свою пользу. Может это и к лучшему.

Ответ #2

Андрей, мы понимаем, что эти нововведения могли доставить неудобства, при этом следует учитывать, что данное решение также основано на требованиях законодательства.

Мы стремимся максимально сохранить удобство для наших пользователей: возможность подачи объявления и фильтрацию предложений по валюте мы оставляем. Также технические специалисты занимаются вопросом добавления валютного калькулятора.

Благодарим вас за обратную связь и что решили с нами поделиться мнением ☺️

Попытка #3

realt.by & onliner.by указывают “ориентировочную цену” в долларах, а вам можно пожелать удачи.

Ответ #3

А нет ответа.

Хватит это терпеть

Но зачем спорить, когда можно сделать себе требуемый интерфейс поверх текущего - #тыжеинженер. Нужен небольшой Chrome Extension, который открывает страницы Куфара, находит цены в белорусских рублях и рядом показывает приблизительную цену в долларах.

Без аккаунтов, без серверной части, без прокси. Просто контент-скрипт и курс НБРБ.

Исходники лежат тут: comerc/try-kufar.

Что получилось

Расширение работает на двух доменах (авто и недвига):

{
  "content_scripts": [
    {
      "matches": [
        "https://auto.kufar.by/*",
        "https://re.kufar.by/*"
      ],
      "js": ["src/content.js"],
      "css": ["src/content.css"],
      "run_at": "document_idle"
    }
  ]
}

Идея простая: на странице есть цены вида 68 683 р. или 256 873 р.. Скрипт превращает их в:

68 683 р. <span class="kufar-usd-price">≈ 25 165 $</span>

Выглядит как маленький зелёный бейдж. Не замена цены, а подсказка рядом.

Курс доллара

Курс берётся из официального API Нацбанка:

const RATE_URL = "https://api.nbrb.by/exrates/rates/USD?parammode=2";

Запрос делает не content script, а background service worker. Так меньше сюрпризов с CORS и проще хранить кеш.

chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
  if (!message || message.type !== "kufar:getUsdRate") {
    return false;
  }

  getUsdRate()
    .then((rateInfo) => sendResponse({ ok: true, rateInfo }))
    .catch((error) => sendResponse({ ok: false, error: error.message }));

  return true;
});

Кеш на 6 часов:

const CACHE_TTL_MS = 6 * 60 * 60 * 1000;

Если API временно не отвечает, берётся последнее сохранённое значение. Для такой задачи этого более чем достаточно: это не трейдинг, это “понять порядок цены”.

Как ищем цены на странице

У Куфара много разных блоков. Листинг, страница объявления, попап на карте, “Похожие объявления”, “Ранее вы смотрели”, автостраницы. Классы тоже не подарок: CSS modules, хеши, разные компоненты. (Бардак в танковых войсках).

Поэтому привязываться к одному классу нельзя. Основной поиск идёт по текстовым узлам:

const PRICE_RE = /(^|[^\d])(\d[\d\s\u00a0.,]*?)\s*(р\.|руб\.?|BYN)(?=$|[^\wа-яё])/i;

Дальше TreeWalker проходит по тексту страницы:

const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, {
  acceptNode(node) {
    return isPriceTextNode(node)
      ? NodeFilter.FILTER_ACCEPT
      : NodeFilter.FILTER_REJECT;
  }
});

Но этого мало. Иногда цена разбита по вложенным span, иногда лежит в блоке с class*="price". Поэтому есть второй проход:

const candidates = root.querySelectorAll([
  "[class*='price' i]",
  "[data-testid*='price' i]",
  "[aria-label*='цен' i]",
  "[aria-label*='price' i]"
].join(","));

Это не идеально академически, зато устойчиво к реальной вёрстке.

Динамический DOM

Куфар дорисовывает части страницы после загрузки. Например, попап на карте появляется не сразу. Поэтому обычного запуска один раз недостаточно.

Решение стандартное:

const observer = new MutationObserver(() => {
  clearTimeout(scanTimer);
  scanTimer = window.setTimeout(() => scanPrices(), 250);
});

observer.observe(document.body, {
  childList: true,
  subtree: true,
  characterData: true
});

Каждое изменение DOM не сканируется мгновенно. Есть debounce на 250 мс, чтобы не устроить странице маленький стресс-тест.

Конвертация

Сама математика смешная:

badge.textContent = `≈ ${formatUsd.format(byn / rateInfo.rate)}`;

Форматирование через Intl.NumberFormat:

const formatUsd = new Intl.NumberFormat("ru-RU", {
  style: "currency",
  currency: "USD",
  maximumFractionDigits: 0
});

То есть 87 914 р. превращается примерно в ≈ 32 212 $.

Несколько неприятных мелочей

Самое интересное было не в курсе и не в регулярке. Самое интересное было в вёрстке.

Например, похожие объявления внизу страницы имеют overflow: hidden и text-overflow: ellipsis. Бейдж добавлялся, но для длинных цен просто обрезался. Пришлось помечать строку цены своим классом и разрешать перенос:

p.kufar-usd-price-row {
  display: flex !important;
  flex-wrap: wrap;
  gap: 2px 0 !important;
  overflow: visible !important;
  text-overflow: clip !important;
  white-space: normal !important;
}

Для аренды есть отдельная история: 604.41 р. / мес.. После добавления доллара суффикс / мес. переставал влезать. Его пришлось вынести в отдельный span и принудительно отправить на новую строку:

.kufar-usd-rent-suffix {
  display: inline-flex;
  flex-basis: 100% !important;
}

А ещё на автостранице есть кнопка “Оформить в лизинг от … р. /м.”. Это не цена объявления, поэтому такие блоки пропускаются:

const SKIP_CONTEXT_RE = /лизинг|\/\s*м\./i;

В итоге расширение конвертирует основную цену авто, но не лезет в платежи по лизингу.

Установка из исходников

Пока расширение не в Chrome Web Store, ставится как unpacked extension.

git clone https://github.com/comerc/try-kufar.git
cd try-kufar

Дальше в Chrome:

  1. открыть chrome://extensions;

  2. включить Developer mode;

  3. нажать Load unpacked;

  4. выбрать папку try-kufar;

  5. открыть страницу Куфара.

Если меняли код локально, надо нажать reload у расширения на странице chrome://extensions, а потом перезагрузить вкладку с Куфаром. Это важно: content scripts и CSS не всегда обновляются просто по F5.

Итог

Получился маленький пользовательский слой поверх сайта. Куфар показывает цены в белорусских рублях, как ему хочется или как ему надо. Расширение рядом показывает привычный ориентир в долларах.

Никакой магии. Просто немного DOM, MutationObserver, API Нацбанка и терпение к чужой вёрстке. Codex решил проблему за пару часов!