<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Blogging on kenji.blog</title><link>http://kenji.blog/ru/categories/blogging/</link><description>Recent content in Blogging on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>ru</language><copyright>kenjinote</copyright><lastBuildDate>Sun, 13 Sep 2026 02:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/ru/categories/blogging/index.xml" rel="self" type="application/rss+xml"/><item><title>Введение в генератор статических сайтов с использованием Hugo (руководство по миграции с WordPress)</title><link>http://kenji.blog/ru/p/hugo-static-site-generator-wordpress-migration/</link><pubDate>Sun, 13 Sep 2026 02:00:00 +0900</pubDate><guid>http://kenji.blog/ru/p/hugo-static-site-generator-wordpress-migration/</guid><description>&lt;img src="http://kenji.blog/p/hugo-static-site-generator-wordpress-migration/img/eyecatch.jpg" alt="Featured image of post Введение в генератор статических сайтов с использованием Hugo (руководство по миграции с WordPress)" />&lt;p>В современной веб-разработке и ведении блогов скорость загрузки сайта, его безопасность и простота обслуживания стали критически важными факторами. «WordPress», долгое время занимавший подавляющую долю рынка как основа для блогов и корпоративных сайтов, полюбился многим пользователям благодаря своей гибкой экосистеме плагинов и интуитивно понятной панели управления. Однако, поскольку он полагается на взаимодействие с базой данных и динамическую генерацию страниц на стороне сервера (обработка на PHP), он также подвержен уязвимостям при резком увеличении трафика и проблемам с задержками отображения (latency).&lt;/p>
&lt;p>Поэтому в последние годы стремительную популярность набирают «генераторы статических сайтов» (SSG: Static Site Generator). В этой статье мы подробно рассмотрим «&lt;strong>Hugo&lt;/strong>» — один из множества SSG, разработанный на языке Go и известный своей невероятной скоростью сборки. Мы досконально разберем всё: от сравнения технической архитектуры с динамическими CMS (Content Management System), такими как WordPress, до конкретных шагов по миграции, оценки производительности с использованием математических моделей, а также уникальной структуры каталогов Hugo и порядка поиска шаблонов.&lt;/p>
&lt;hr>
&lt;h2 id="1-технические-различия-между-динамическими-cms-wordpress-и-генераторами-статических-сайтов-hugo">1. Технические различия между динамическими CMS (WordPress) и генераторами статических сайтов (Hugo)
&lt;/h2>&lt;p>В механизмах доставки веб-сайтов WordPress и Hugo используют фундаментально разные подходы.&lt;/p>
&lt;h3 id="11-архитектура-wordpress-динамическая-генерация">1.1 Архитектура WordPress (Динамическая генерация)
&lt;/h3>&lt;p>WordPress — это типичный пример динамической CMS, которая собирает страницу на стороне сервера при каждом запросе. Когда пользователь (браузер) обращается к странице, веб-сервер (Apache, Nginx и т.д.) выполняет скрипт PHP и отправляет запрос к реляционной базе данных, такой как MySQL (или MariaDB). Полученный из базы данных контент (данные статей, категории, теги, настройки сайта и т.д.) объединяется с файлами шаблонов для генерации итогового HTML, который затем возвращается клиенту.&lt;/p>
&lt;p>Этот механизм имеет то преимущество, что позволяет генерировать различный контент для каждого посетителя в реальном времени (например, корзина в интернет-магазине или страницы только для авторизованных пользователей). Однако без правильно спроектированного механизма кэширования (обратный прокси-сервер, плагины и т.д.) он приводит к интенсивному потреблению ресурсов сервера.&lt;/p>
&lt;h3 id="12-архитектура-hugo-предварительная-генерация-во-время-сборки">1.2 Архитектура Hugo (Предварительная генерация во время сборки)
&lt;/h3>&lt;p>С другой стороны, Hugo, как следует из названия «генератор статических сайтов», генерирует контент не «во время запроса», а «во время сборки» (build time). Контент хранится не в базе данных, а в виде локальных файлов Markdown, которые версионируются с помощью Git или аналогичных систем.
Когда разработчик выполняет команду (&lt;code>hugo&lt;/code>), Hugo считывает файлы Markdown, подставляет данные в указанные HTML-шаблоны (файлы макетов) и генерирует набор готовых чистых файлов HTML/CSS/JS.&lt;/p>
&lt;p>Сгенерированный набор файлов (статические ресурсы) можно доставлять, просто разместив их в «средах статического хостинга», таких как Amazon S3, Cloudflare Pages, Netlify, Vercel или на простом сервере Nginx. Поскольку база данных и серверные языки (например, PHP) не требуются, риски безопасности (такие как SQL-инъекции или уязвимости PHP) резко снижаются, а скорость доставки максимизируется за счет кэширования на граничных узлах CDN (Content Delivery Network).&lt;/p>
&lt;p>Ниже на диаграмме Mermaid показаны различия в каждой из архитектур.&lt;/p>
&lt;pre class="mermaid">
flowchart TD
subgraph WordPress[&amp;#34;Динамическая CMS (WordPress)&amp;#34;]
direction TB
Req1[&amp;#34;Запрос пользователя&amp;#34;] --&amp;gt; WebServer1[&amp;#34;Веб-сервер (Nginx/Apache)&amp;#34;]
WebServer1 --&amp;gt; PHP[&amp;#34;Среда выполнения PHP&amp;#34;]
PHP &amp;lt;--&amp;gt; DB[&amp;#34;База данных (MySQL)&amp;#34;]
PHP --&amp;gt; HTML1[&amp;#34;Генерация HTML&amp;#34;]
HTML1 --&amp;gt; Res1[&amp;#34;Возврат ответа&amp;#34;]
end
subgraph Hugo[&amp;#34;Генератор статических сайтов (Hugo)&amp;#34;]
direction TB
Dev[&amp;#34;Разработчик / CI/CD&amp;#34;] --&amp;gt; HugoBuild[&amp;#34;Процесс сборки Hugo&amp;#34;]
Markdown[&amp;#34;Файлы Markdown&amp;#34;] --&amp;gt; HugoBuild
Templates[&amp;#34;Шаблоны (HTML)&amp;#34;] --&amp;gt; HugoBuild
HugoBuild --&amp;gt; StaticFiles[&amp;#34;Статические файлы (HTML/CSS/JS)&amp;#34;]
StaticFiles --&amp;gt; CDN[&amp;#34;CDN / Статический хостинг&amp;#34;]
Req2[&amp;#34;Запрос пользователя&amp;#34;] --&amp;gt; CDN
CDN --&amp;gt; Res2[&amp;#34;Мгновенный возврат ответа&amp;#34;]
end
&lt;/pre>
&lt;hr>
&lt;h2 id="2-оценка-производительности-с-использованием-математических-моделей">2. Оценка производительности с использованием математических моделей
&lt;/h2>&lt;p>Одним из самых больших преимуществ перехода с WordPress на Hugo является улучшение производительности (скорости отображения). Чтобы понять это количественно, давайте выразим это с помощью простой математической модели.&lt;/p>
&lt;p>Время до завершения загрузки страницы (Load Time: $T_{load}$) можно в целом разделить на время ответа сервера (TTFB: Time To First Byte) и время, затрачиваемое браузером на рендеринг и получение ресурсов ($T_{render}$).&lt;/p>
$$ T_{load} = T_{ttfb} + T_{render} $$&lt;p>В случае динамической CMS (WordPress) $T_{ttfb}$ представляет собой сумму следующих компонентов: сетевой задержки ($T_{network}$), времени выполнения серверного скрипта ($T_{php}$) и времени обработки запросов к базе данных ($T_{db}$).&lt;/p>
$$ T_{ttfb\_wp} = T_{network} + T_{php} + T_{db} $$&lt;p>В условиях высокой концентрации доступа (при высокой нагрузке) $T_{php}$ и $T_{db}$ увеличиваются нелинейно, и вся система может стать узким местом. В математическом выражении мы можем наблюдать следующее ухудшение времени отклика по отношению к количеству запросов ($N$) (где $k$ — коэффициент накладных расходов на обработку):&lt;/p>
$$ T_{php}(N) \approx O(N^k), \quad T_{db}(N) \approx O(N^k) \quad \text{where } k > 1 $$&lt;p>С другой стороны, в архитектуре, объединяющей генератор статических сайтов (Hugo) и CDN, отсутствует динамическая обработка на стороне сервера (PHP и запросы к БД). Поскольку контент кэшируется на граничных серверах, распределенных по всему миру, $T_{ttfb}$ зависит исключительно от сетевой задержки от клиента до ближайшего граничного сервера ($T_{edge}$).&lt;/p>
$$ T_{ttfb\_hugo} = T_{edge} $$&lt;p>В результате выполняется условие $T_{edge} \ll (T_{network} + T_{php} + T_{db})$, и показатель TTFB кардинально сокращается до нескольких миллисекунд или десятков миллисекунд. Кроме того, даже при увеличении количества запросов $N$ время отклика остается практически постоянным ($O(1)$) благодаря функциям балансировки нагрузки граничных серверов.&lt;/p>
$$ \lim_{N \to \infty} T_{ttfb\_hugo}(N) \approx \text{Constant} $$&lt;p>В этом и заключается математическое обоснование того, почему Hugo (статические сайты) обладает чрезвычайной устойчивостью к всплескам трафика (например, при вирусном эффекте).&lt;/p>
&lt;hr>
&lt;h2 id="3-базовая-структура-и-принципы-работы-hugo">3. Базовая структура и принципы работы Hugo
&lt;/h2>&lt;p>Чтобы освоить Hugo, необходимо понять его уникальную структуру каталогов, а также концепции «Front Matter» и «Порядок поиска шаблонов» (Template Lookup Order).&lt;/p>
&lt;h3 id="31-подробное-объяснение-структуры-каталогов">3.1 Подробное объяснение структуры каталогов
&lt;/h3>&lt;p>При создании нового проекта Hugo (с помощью &lt;code>hugo new site mysite&lt;/code>) генерируется следующая структура каталогов:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">mysite/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── archetypes/ # Шаблоны для создания нового контента (заготовки Front Matter)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── assets/ # Файлы, обрабатываемые Hugo Pipes (SCSS/Sass, JavaScript и т.д.)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── content/ # Фактический контент сайта (файлы Markdown). Служит заменой БД.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── data/ # Внешние данные и настройки, используемые на всем сайте (JSON, TOML, YAML, CSV и т.д.)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── layouts/ # HTML-шаблоны, определяющие внешний вид сайта (используют Go html/template)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── public/ # Место вывода сгенерированных статических файлов после выполнения команды сборки
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── static/ # Статические файлы, публикуемые &amp;#34;как есть&amp;#34; (изображения, favicon, robots.txt и т.д.)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── themes/ # Каталог для сторонних или собственных тем
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└── hugo.toml # Файл конфигурации всего сайта (ранее основным был config.toml)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>В WordPress контент хранится в таблице &lt;code>wp_posts&lt;/code> MySQL, в то время как в Hugo всё управляется в виде текстовых файлов (преимущественно Markdown) в каталоге &lt;code>content/&lt;/code>. Это значительно упрощает версионирование контента с помощью Git.&lt;/p>
&lt;h3 id="32-управление-контентом-markdown-и-front-matter">3.2 Управление контентом: Markdown и Front Matter
&lt;/h3>&lt;p>Каждый файл статьи в Hugo имеет блок метаданных в самом верху, который называется «Front Matter», за которым следует основной текст (Markdown). Front Matter можно писать в форматах TOML, YAML или JSON, но чаще всего используется YAML.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nn">---&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">title&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Понимание таксономий в Hugo&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">date&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="ld">2026-09-13T10:00:00&lt;/span>&lt;span class="m">+09&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="m">00&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">draft&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">categories&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;Технические руководства&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">tags&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;Hugo&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;Go&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">aliases&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;/old-category/hugo-taxonomy/&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nn">---&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="l">Здесь начинается основной текст. Он пишется на **Markdown**.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="l">Мы объясним мощные возможности Hugo...&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Здесь стоит обратить внимание на ключ &lt;code>aliases&lt;/code>. При переходе с WordPress изменение постоянных ссылок (URL) будет иметь серьезные негативные последствия для SEO. Используя функцию алиасов в Hugo, достаточно указать старый URL, и Hugo автоматически сгенерирует HTML для перенаправления (переадресация через meta refresh). Это чрезвычайно удобно, так как избавляет от необходимости настраивать редиректы на стороне сервера (например, в .htaccess).&lt;/p>
&lt;h3 id="33-порядок-поиска-шаблонов-template-lookup-order">3.3 Порядок поиска шаблонов (Template Lookup Order)
&lt;/h3>&lt;p>Одной из мощных возможностей Hugo является его гибкий механизм поиска шаблонов (Template Lookup Order). При рендеринге определенной страницы Hugo осуществляет поиск по каталогам и именам файлов в определенном порядке, чтобы найти наиболее подходящий шаблон.&lt;/p>
&lt;p>Например, при рендеринге отдельной статьи (Single Page) &lt;code>content/post/hello-world.md&lt;/code> Hugo ищет файлы макетов примерно в следующем порядке:&lt;/p>
&lt;ol>
&lt;li>&lt;code>layouts/post/single.html&lt;/code>&lt;/li>
&lt;li>&lt;code>layouts/post/list.html&lt;/code> (Не ошибка, но обычно используется для списков)&lt;/li>
&lt;li>&lt;code>layouts/_default/single.html&lt;/code>&lt;/li>
&lt;li>&lt;code>themes/&amp;lt;THEME_NAME&amp;gt;/layouts/post/single.html&lt;/code>&lt;/li>
&lt;li>&lt;code>themes/&amp;lt;THEME_NAME&amp;gt;/layouts/_default/single.html&lt;/code>&lt;/li>
&lt;/ol>
&lt;p>Разработчики могут &lt;strong>переопределять (override)&lt;/strong> шаблоны темы, не изменяя исходный код темы напрямую, а просто создавая файл с тем же именем в каталоге &lt;code>layouts/&lt;/code> своего проекта. Это позволяет вносить собственные настройки, не мешая будущим обновлениям базовой темы.&lt;/p>
&lt;h3 id="34-таксономии-taxonomy">3.4 Таксономии (Taxonomy)
&lt;/h3>&lt;p>Система классификации, эквивалентная «Категориям» и «Тегам» в WordPress, в Hugo называется «Таксономией» (Taxonomy).
По умолчанию Hugo поддерживает таксономии &lt;code>categories&lt;/code> и &lt;code>tags&lt;/code>, но, отредактировав файл &lt;code>hugo.toml&lt;/code>, вы можете свободно добавлять пользовательские таксономии (например, &lt;code>series&lt;/code>, &lt;code>authors&lt;/code> и т.д.).&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># Пример hugo.toml&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">taxonomies&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">category&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;categories&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">tag&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;tags&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">series&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;series&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">author&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;authors&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Это позволяет упорядочивать контент и составлять списки по различным осям.&lt;/p>
&lt;hr>
&lt;h2 id="4-процесс-миграции-с-wordpress-на-hugo">4. Процесс миграции с WordPress на Hugo
&lt;/h2>&lt;p>Ключом к успешному переходу с WordPress на Hugo является то, насколько чисто вы сможете преобразовать динамический контент из базы данных в статические файлы (Markdown + Front Matter) с сохранением существующей структуры URL.&lt;/p>
&lt;p>Ниже показана типичная схема пайплайна миграции:&lt;/p>
&lt;pre class="mermaid">
flowchart LR
WPDB[&amp;#34;База данных WP&amp;#34;] --&amp;gt;|Плагин| Exporter[&amp;#34;Инструмент экспорта&amp;#34;]
Exporter --&amp;gt;| Извлечение текста, \nметаданных, изображений | ZipFile[&amp;#34;Zip-архив / Папка&amp;#34;]
ZipFile --&amp;gt;|Распаковка| MarkdownFiles[&amp;#34;Файлы Markdown \n(content/)&amp;#34;]
ZipFile --&amp;gt;|Распаковка| ImageFiles[&amp;#34;Изображения \n(static/wp-content/)&amp;#34;]
MarkdownFiles --&amp;gt; Formatting[&amp;#34;Проверка и форматирование \n(Исправление шорткодов)&amp;#34;]
Formatting --&amp;gt; Git[&amp;#34;Коммит в Git&amp;#34;]
&lt;/pre>
&lt;h3 id="41-извлечение-данных-и-преобразование-в-markdown">4.1 Извлечение данных и преобразование в Markdown
&lt;/h3>&lt;p>Для экспорта данных WordPress для использования в Hugo проще и надежнее всего использовать специальные плагины. Вот несколько типичных подходов:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Использование плагина Jekyll Exporter&lt;/strong>
Поскольку Hugo имеет очень схожую структуру данных с Jekyll (еще одним SSG), использование плагина «Jekyll Exporter» для WordPress является распространенной практикой. После установки и запуска этого плагина все записи и статические страницы конвертируются в файлы Markdown с Front Matter, которые можно загрузить в виде ZIP-архива вместе с изображениями.&lt;/li>
&lt;li>&lt;strong>Собственные скрипты с использованием WordPress API&lt;/strong>
Метод, при котором с помощью Python, Node.js и т.д. выполняются запросы к REST API WordPress (&lt;code>/wp-json/wp/v2/posts&lt;/code>), анализируются данные JSON и самостоятельно генерируются файлы Markdown. Этот метод эффективен для сайтов, интенсивно использующих сложные произвольные поля (например, ACF), с которыми плагины не могут справиться в полной мере.&lt;/li>
&lt;li>&lt;strong>Использование инструмента wp2hugo&lt;/strong>
Также существует подход с использованием инструментов командной строки (CLI), написанных на Go и т.д., для прямой конвертации из файла экспорта XML WordPress (WXR) в формат Hugo.&lt;/li>
&lt;/ol>
&lt;h3 id="42-сохранение-структуры-постоянных-ссылок-url">4.2 Сохранение структуры постоянных ссылок (URL)
&lt;/h3>&lt;p>Чтобы сохранить SEO-рейтинг, критически важно сохранить URL-адреса такими, какими они были во времена WordPress. Если в WordPress были настроены постоянные ссылки вида &lt;code>https://example.com/2026/09/13/my-post/&lt;/code>, необходимо указать аналогичную структуру постоянных ссылок в &lt;code>hugo.toml&lt;/code> для Hugo.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">permalinks&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">post&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;/:year/:month/:day/:slug/&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>В качестве альтернативы можно принудительно зафиксировать URL-адреса, явно указав параметр &lt;code>url&lt;/code> во Front Matter для каждой статьи.
Кроме того, для страниц, чьи URL-адреса изменятся, следует настроить редиректы с помощью ранее упомянутого параметра &lt;code>aliases&lt;/code>.&lt;/p>
&lt;h3 id="43-конвертация-шорткодов">4.3 Конвертация шорткодов
&lt;/h3>&lt;p>Специфичные для WordPress шорткоды (например, &lt;code>[gallery]&lt;/code>, &lt;code>[caption]&lt;/code> или собственные коды различных плагинов) часто остаются в виде исходных строк при экспорте, поэтому требуется их обработка.
Их можно либо удалить все сразу с помощью скриптов замены (sed или Python), либо использовать мощную &lt;strong>функцию пользовательских шорткодов&lt;/strong> Hugo (путем создания собственных макетов в &lt;code>layouts/shortcodes/&lt;/code>), чтобы они корректно рендерились на стороне Hugo после миграции.&lt;/p>
&lt;hr>
&lt;h2 id="5-инструменты-cli-сборка-и-развертывание-в-hugo">5. Инструменты CLI, сборка и развертывание в Hugo
&lt;/h2>&lt;p>После завершения процесса миграции настало время собрать сайт с помощью Hugo и опубликовать его для всего мира. Поставляемый в виде бинарного файла на языке Go, Hugo может похвастаться удивительной скоростью, позволяющей собирать сайты из тысяч или даже десятков тысяч страниц всего за несколько секунд.&lt;/p>
&lt;h3 id="51-запуск-локального-сервера-разработки">5.1 Запуск локального сервера разработки
&lt;/h3>&lt;p>При написании статей или настройке дизайна необходимо запускать локальный сервер.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Команда запуска сервера разработки (используйте -D для включения черновиков)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">hugo server -D
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>После выполнения этой команды сайт будет доступен для предварительного просмотра по адресу &lt;code>http://localhost:1313/&lt;/code>. В Hugo встроена мощная функция «LiveReload»: как только вы отредактируете и сохраните файл Markdown, шаблон или CSS, экран браузера будет автоматически и мгновенно обновлен. Это делает процесс написания и разработки намного более комфортным, чем в панели администратора WordPress.&lt;/p>
&lt;h3 id="52-сборка-для-продакшена-и-оптимизация-производительности">5.2 Сборка для продакшена и оптимизация производительности
&lt;/h3>&lt;p>Чтобы сгенерировать статические файлы для развертывания в рабочей (продакшен) среде, просто введите команду &lt;code>hugo&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Выполнение сборки для продакшена. Опция --minify минимизирует HTML/CSS/JS&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">hugo --minify
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Эта команда выведет файлы всего сайта в каталог &lt;code>public/&lt;/code>. Добавление опции &lt;code>--minify&lt;/code> удаляет ненужные переносы строк и пробелы, еще больше сокращая размер файлов. Это напрямую способствует уменьшению сетевой задержки ($T_{network}$), описанной в математической модели ранее.&lt;/p>
&lt;h3 id="53-автоматизация-развертывания-cicd">5.3 Автоматизация развертывания (CI/CD)
&lt;/h3>&lt;p>Генерировать статические файлы каждый раз на локальном ПК и загружать их по FTP — неэффективно. В современной практике управления SSG лучшим подходом является создание среды CI/CD, в которой отправка (push) в репозиторий Git (например, на GitHub) служит триггером для автоматической сборки и развертывания.&lt;/p>
&lt;p>Например, базовая конфигурация (файл YAML) для развертывания на Cloudflare Pages или GitHub Pages с использованием GitHub Actions выглядит следующим образом:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># Пример .github/workflows/hugo.yml&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Deploy Hugo site to GitHub Pages&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">on&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">push&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">branches&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;main&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">workflow_dispatch&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">permissions&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">contents&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">read&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">pages&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">write&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">id-token&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">write&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">jobs&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">build&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">runs-on&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">ubuntu-latest&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">steps&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Checkout&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">actions/checkout@v3&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">with&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">submodules&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">recursive&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># Если темы управляются как подмодули&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">fetch-depth&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">0&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Setup Hugo&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">peaceiris/actions-hugo@v2&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">with&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">hugo-version&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;latest&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">extended&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Build&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">hugo --minify&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Upload artifact&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">actions/upload-pages-artifact@v2&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">with&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">path&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">./public&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">deploy&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">environment&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">github-pages&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">url&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">${{ steps.deployment.outputs.page_url }}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">runs-on&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">ubuntu-latest&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">needs&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">build&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">steps&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Deploy to GitHub Pages&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">id&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">deployment&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">actions/deploy-pages@v2&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>С такими настройками мы получаем автоматизированный пайплайн, в котором достаточно выполнить действие «написать статью в Markdown и отправить (push) на GitHub», и через несколько минут обновленный сайт будет опубликован в рабочей среде.&lt;/p>
&lt;hr>
&lt;h2 id="6-преимущества-seo-и-управления-после-миграции">6. Преимущества SEO и управления после миграции
&lt;/h2>&lt;p>Владельцы сайтов, завершившие переход с WordPress на Hugo, чаще всего отмечают три следующих существенных преимущества:&lt;/p>
&lt;h3 id="61-кардинальное-улучшение-скорости-сайта-и-показателей-core-web-vitals">6.1 Кардинальное улучшение скорости сайта и показателей Core Web Vitals
&lt;/h3>&lt;p>За счет устранения запросов к базе данных и рендеринга на стороне сервера время загрузки страницы сокращается до миллисекунд. Это напрямую приводит к значительному улучшению показателей «Core Web Vitals» (LCP, FID/INP, CLS), которые являются факторами ранжирования Google. Можно ожидать снижения показателя отказов пользователей и улучшения позиций в SEO.&lt;/p>
&lt;h3 id="62-освобождение-от-угроз-безопасности">6.2 Освобождение от угроз безопасности
&lt;/h3>&lt;p>Поскольку WordPress широко используется по всему миру, он постоянно является мишенью для атак. Всегда существуют риски взлома через уязвимости плагинов или обхода формы входа в систему с помощью атак методом перебора (brute force).
Однако статические сайты, сгенерированные с помощью Hugo, не имеют ни базы данных, ни среды PHP, ни даже панели управления (формы входа). У хакеров нет возможности проникнуть на сервер и изменить базу данных, поэтому риски безопасности сводятся практически к нулю.&lt;/p>
&lt;h3 id="63-эксплуатация-не-требующая-обслуживания">6.3 Эксплуатация, не требующая обслуживания
&lt;/h3>&lt;p>Эксплуатация WordPress требует постоянного обслуживания: обновления ядра, обновления плагинов, перехода на новые версии PHP и т.д. Вам всегда приходится опасаться риска того, что сайт сломается из-за проблем с совместимостью.
В случае с Hugo достаточно обновлять сам инструмент по мере необходимости, а код сайта представляет собой набор независимых текстовых файлов. Это дает подавляющее чувство уверенности в том, что «сайт не сломается, даже если его оставить в покое».&lt;/p>
&lt;hr>
&lt;h2 id="7-заключение">7. Заключение
&lt;/h2>&lt;p>В этой статье мы подробно рассмотрели переход с динамических CMS, таких как WordPress, на мощный генератор статических сайтов «Hugo» на базе языка Go: от различий в технической архитектуре и математического доказательства производительности до конкретных шагов по миграции.&lt;/p>
&lt;p>Хотя переход на генераторы статических сайтов требует начальных затрат на обучение (работа с Git, синтаксис Markdown, выполнение команд CLI из терминала, понимание спецификаций шаблонизатора и т.д.), он приносит отдачу в виде «подавляющей скорости загрузки», «надежной безопасности» и «отсутствия необходимости в обслуживании», которая с лихвой окупает эти затраты.&lt;/p>
&lt;p>Если вашему веб-сайту не требуются частые изменения дизайна или сложная динамическая обработка (например, функции только для зарегистрированных пользователей или продвинутые функции интернет-магазина), и он предназначен в первую очередь для распространения информации (блог, медиа, корпоративный сайт), то переход на Hugo станет одной из самых эффективных технических инвестиций. Обязательно воспользуйтесь этой статьей как руководством и сделайте первый шаг к управлению веб-сайтами нового поколения с использованием Hugo.&lt;/p></description></item><item><title>SEO-оптимизация блога на Hugo: Настройки Frontmatter для резкого увеличения трафика</title><link>http://kenji.blog/ru/p/hugo-blog-seo-frontmatter-tips/</link><pubDate>Sat, 12 Sep 2026 12:00:00 +0900</pubDate><guid>http://kenji.blog/ru/p/hugo-blog-seo-frontmatter-tips/</guid><description>&lt;img src="http://kenji.blog/p/hugo-blog-seo-frontmatter-tips/img/eyecatch.jpg" alt="Featured image of post SEO-оптимизация блога на Hugo: Настройки Frontmatter для резкого увеличения трафика" />&lt;p>Hugo — это один из самых быстрых в мире генераторов статических сайтов (SSG), написанный на языке Go. Благодаря потрясающей скорости сборки и гибкой системе шаблонов он получил высокую оценку многих инженеров и блогеров. Однако того, что сайт генерируется и отображается быстро, недостаточно для того, чтобы поисковые системы (такие как Google или Bing) высоко его оценили и доставили статьи пользователям.&lt;/p>
&lt;p>Для повышения позиций в поиске, увеличения виральности в социальных сетях и, как следствие, резкого роста посещаемости блога необходима тщательная SEO-оптимизация (поисковая оптимизация). Сердцем SEO в Hugo является взаимодействие между &lt;strong>Frontmatter&lt;/strong>, который описывается в начале каждой статьи в формате Markdown, и &lt;strong>шаблонами (Layouts)&lt;/strong>, которые интерпретируют его и разворачивают метаданные внутри тега HTML &lt;code>&amp;lt;head&amp;gt;&lt;/code>.&lt;/p>
&lt;p>В этой статье мы подробно, в объеме более 10 000 символов, рассмотрим, как максимально использовать возможности Hugo и реализовать продвинутое SEO: от настройки Frontmatter до различных метатегов, OGP (Open Graph Protocol), Twitter Cards и вывода структурированных данных с использованием JSON-LD.&lt;/p>
&lt;hr>
&lt;h2 id="1-математические-основы-seo-и-трафика">1. Математические основы SEO и трафика
&lt;/h2>&lt;p>Перед тем как перейти к конкретной реализации, давайте математически поймем, почему важны подробные SEO-метаданные. Поисковый трафик $T$, который может получить веб-сайт, определяется объемом поиска целевых ключевых слов и показателем кликабельности (CTR), основанным на позиции в поиске.&lt;/p>
&lt;p>В виде формулы это можно выразить следующим образом:&lt;/p>
$$ T = \sum_{i=1}^{n} V_i \times CTR(R_i) $$&lt;ul>
&lt;li>$V_i$ : Ежемесячный объем поиска для ключевого слова $i$&lt;/li>
&lt;li>$R_i$ : Позиция в поиске для ключевого слова $i$&lt;/li>
&lt;li>$CTR(R_i)$ : Показатель кликабельности на позиции $R_i$&lt;/li>
&lt;/ul>
&lt;p>Среди них позиция в поиске $R_i$ зависит от многих факторов, таких как качество контента и обратные ссылки (PageRank), но первоначальный алгоритм PageRank от Google моделируется следующим образом:&lt;/p>
$$ PR(u) = \frac{1-d}{N} + d \sum_{v \in B(u)} \frac{PR(v)}{L(v)} $$&lt;ul>
&lt;li>$PR(u)$ : PageRank страницы $u$&lt;/li>
&lt;li>$d$ : Коэффициент затухания (обычно 0.85)&lt;/li>
&lt;li>$B(u)$ : Множество страниц, ссылающихся на страницу $u$&lt;/li>
&lt;li>$L(v)$ : Количество исходящих ссылок со страницы $v$&lt;/li>
&lt;/ul>
&lt;p>Здесь важно отметить, что &lt;strong>помимо усилий по повышению позиции $R_i$, необходимо максимально увеличить кликабельность $CTR(R_i)$&lt;/strong>. Оптимизируя заголовки и сниппеты (description), отображаемые в результатах поиска (SERP), а также привлекательные изображения (OGP) при публикации в социальных сетях, можно намеренно повысить $CTR(R_i)$. SEO-настройки во Frontmatter напрямую связаны с максимизацией этого $CTR$.&lt;/p>
&lt;hr>
&lt;h2 id="2-процесс-сборки-hugo-и-роль-frontmatter">2. Процесс сборки Hugo и роль Frontmatter
&lt;/h2>&lt;p>Hugo считывает Frontmatter (YAML/TOML/JSON) в файлах Markdown и передает его в шаблонизатор в качестве переменных страницы. Давайте сначала визуально поймем этот поток информации.&lt;/p>
&lt;pre class="mermaid">
flowchart TD
A[&amp;#34;Файл Markdown&amp;#34;] --&amp;gt; B[&amp;#34;Парсинг Frontmatter&amp;#34;]
A --&amp;gt; C[&amp;#34;Парсинг контента&amp;#34;]
B --&amp;gt; D[&amp;#34;Переменные страницы Hugo (.Title, .Params)&amp;#34;]
C --&amp;gt; D
E[&amp;#34;layouts/partials/head.html&amp;#34;] --&amp;gt; F[&amp;#34;Шаблонизатор Go&amp;#34;]
D --&amp;gt; F
F --&amp;gt; G[&amp;#34;Финальный HTML-тег &amp;lt;head&amp;gt;&amp;#34;]
G --&amp;gt; H[&amp;#34;Googlebot / Краулеры&amp;#34;]
G --&amp;gt; I[&amp;#34;Скраперы социальных сетей (OGP)&amp;#34;]
&lt;/pre>
&lt;p>Таким образом, значения, установленные во Frontmatter, передаются в &lt;code>head.html&lt;/code> как переменные, такие как &lt;code>.Title&lt;/code> или &lt;code>.Params.description&lt;/code>, и выводятся как финальные HTML-метаданные. Следовательно, успех SEO состоит из двух этапов: «определение правильной информации во Frontmatter» и «ее правильное преобразование в HTML в шаблоне».&lt;/p>
&lt;hr>
&lt;h2 id="3-настройка-базовых-метаданных-title-description-canonical-url">3. Настройка базовых метаданных: Title, Description, Canonical URL
&lt;/h2>&lt;p>Самыми основными тегами, помогающими поисковым системам понять содержимое страницы, являются &lt;code>&amp;lt;title&amp;gt;&lt;/code> и &lt;code>&amp;lt;meta name=&amp;quot;description&amp;quot;&amp;gt;&lt;/code>. Кроме того, во избежание штрафов за дублирование контента обязателен тег &lt;code>&amp;lt;link rel=&amp;quot;canonical&amp;quot;&amp;gt;&lt;/code>.&lt;/p>
&lt;h3 id="31-пример-настройки-frontmatter">3.1. Пример настройки Frontmatter
&lt;/h3>&lt;p>Во Frontmatter статьи мы подготовим специальные поля для SEO.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nn">---&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">title: &amp;#39;SEO-оптимизация блога на Hugo&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Настройки Frontmatter для резкого увеличения трафика&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">seo_title: &amp;#39;Полное руководство по SEO в Hugo: Увеличение трафика с помощью Frontmatter&amp;#39; # Опционально&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">для поисковых систем&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">description&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;Методы продвинутой SEO-оптимизации с использованием Frontmatter в Hugo. Подробное руководство по настройке OGP, JSON-LD и метаданных.&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">slug&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;hugo-seo-frontmatter-tips&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">canonicalUrl&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;https://example.com/post/hugo-seo-frontmatter-tips/&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># Явный канонический URL&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nn">---&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="32-реализация-layoutspartialsheadhtml">3.2. Реализация &lt;code>layouts/partials/head.html&lt;/code>
&lt;/h3>&lt;p>Мы создадим HTML-шаблон для правильного вывода этих переменных.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="c">&amp;lt;!-- Оптимизация заголовка (Title) --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{{ $title := .Title }}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{{ if .Params.seo_title }}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> {{ $title = .Params.seo_title }}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{{ end }}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">title&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>{{ $title }} | {{ .Site.Title }}&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">title&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&amp;lt;!-- Оптимизация описания (Description) --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{{ $description := .Summary | plainify | truncate 120 }}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{{ if .Params.description }}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> {{ $description = .Params.description }}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{{ end }}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">meta&lt;/span> &lt;span class="na">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;description&amp;#34;&lt;/span> &lt;span class="na">content&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ $description }}&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&amp;lt;!-- Canonical URL (Канонизация) --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{{ $canonical := .Permalink }}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{{ if .Params.canonicalUrl }}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> {{ $canonical = .Params.canonicalUrl }}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{{ end }}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">link&lt;/span> &lt;span class="na">rel&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;canonical&amp;#34;&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ $canonical }}&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&amp;lt;!-- Управление роботами (например, запрет индексации) --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{{ if .Params.noindex }}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">meta&lt;/span> &lt;span class="na">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;robots&amp;#34;&lt;/span> &lt;span class="na">content&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;noindex, nofollow&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{{ else }}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">meta&lt;/span> &lt;span class="na">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;robots&amp;#34;&lt;/span> &lt;span class="na">content&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;index, follow&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{{ end }}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Используя &lt;code>.Summary&lt;/code> из Hugo в качестве запасного варианта, вы можете автоматически извлекать начало статьи, даже если &lt;code>description&lt;/code> не задан.&lt;/p>
&lt;hr>
&lt;h2 id="4-ogp-и-twitter-cards-максимизация-ctr-в-социальных-сетях">4. OGP и Twitter Cards: Максимизация CTR в социальных сетях
&lt;/h2>&lt;p>Для того чтобы статья при публикации в социальных сетях, таких как Twitter (X) и Facebook, отображалась в привлекательном карточном формате, необходимы настройки Open Graph Protocol (OGP) и Twitter Cards. Они также генерируются динамически из Frontmatter.&lt;/p>
&lt;h3 id="41-проблемы-встроенных-шаблонов">4.1. Проблемы встроенных шаблонов
&lt;/h3>&lt;p>В Hugo есть удобный встроенный шаблон &lt;code>{{ template &amp;quot;_internal/opengraph.html&amp;quot; . }}&lt;/code>, но ему не хватает возможностей кастомизации, и бывают случаи, когда он не подходит для конкретных требований (например, для определенных языковых сред). Поэтому настоятельно рекомендуется реализовать собственные OGP-теги в &lt;code>head.html&lt;/code>.&lt;/p>
&lt;h3 id="42-указание-изображений-во-frontmatter">4.2. Указание изображений во Frontmatter
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nn">---&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">image&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;img/eyecatch.jpg&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">images&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;img/eyecatch-large.jpg&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># Для указания нескольких изображений или абсолютных путей&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nn">---&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="43-код-пользовательской-реализации-ogp-и-twitter-cards">4.3. Код пользовательской реализации OGP и Twitter Cards
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="c">&amp;lt;!-- Open Graph Protocol --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">meta&lt;/span> &lt;span class="na">property&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;og:title&amp;#34;&lt;/span> &lt;span class="na">content&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ $title }}&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">meta&lt;/span> &lt;span class="na">property&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;og:description&amp;#34;&lt;/span> &lt;span class="na">content&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ $description }}&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">meta&lt;/span> &lt;span class="na">property&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;og:type&amp;#34;&lt;/span> &lt;span class="na">content&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ if .IsPage }}article{{ else }}website{{ end }}&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">meta&lt;/span> &lt;span class="na">property&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;og:url&amp;#34;&lt;/span> &lt;span class="na">content&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ .Permalink }}&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">meta&lt;/span> &lt;span class="na">property&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;og:site_name&amp;#34;&lt;/span> &lt;span class="na">content&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ .Site.Title }}&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&amp;lt;!-- Разрешение OGP Image --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{{ $ogImage := &amp;#34;&amp;#34; }}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{{ if .Params.image }}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> {{ $ogImage = .Params.image | absURL }}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{{ else if .Params.images }}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> {{ $ogImage = index .Params.images 0 | absURL }}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{{ else if .Site.Params.defaultImage }}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> {{ $ogImage = .Site.Params.defaultImage | absURL }}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{{ end }}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{{ if $ogImage }}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">meta&lt;/span> &lt;span class="na">property&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;og:image&amp;#34;&lt;/span> &lt;span class="na">content&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ $ogImage }}&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">meta&lt;/span> &lt;span class="na">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;twitter:image&amp;#34;&lt;/span> &lt;span class="na">content&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ $ogImage }}&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">meta&lt;/span> &lt;span class="na">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;twitter:card&amp;#34;&lt;/span> &lt;span class="na">content&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;summary_large_image&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{{ else }}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">meta&lt;/span> &lt;span class="na">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;twitter:card&amp;#34;&lt;/span> &lt;span class="na">content&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;summary&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{{ end }}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&amp;lt;!-- Twitter Cards --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">meta&lt;/span> &lt;span class="na">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;twitter:title&amp;#34;&lt;/span> &lt;span class="na">content&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ $title }}&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">meta&lt;/span> &lt;span class="na">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;twitter:description&amp;#34;&lt;/span> &lt;span class="na">content&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ $description }}&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{{ if .Site.Params.twitterAccount }}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">meta&lt;/span> &lt;span class="na">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;twitter:site&amp;#34;&lt;/span> &lt;span class="na">content&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;@{{ .Site.Params.twitterAccount }}&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{{ end }}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Используя функцию &lt;code>absURL&lt;/code>, мы преобразуем URL изображения, указанный как относительный путь, в абсолютный. Поскольку для OGP обязательны абсолютные пути, этот процесс крайне важен.&lt;/p>
&lt;hr>
&lt;h2 id="5-реализация-структурированных-данных-json-ld">5. Реализация структурированных данных (JSON-LD)
&lt;/h2>&lt;p>В современном SEO &lt;strong>JSON-LD (JavaScript Object Notation for Linked Data)&lt;/strong> стал основной технологией для точной передачи семантической структуры страницы поисковым системам. Его настройка увеличивает вероятность отображения расширенных сниппетов (звездный рейтинг, имя автора, дата публикации и т.д.) в результатах поиска.&lt;/p>
&lt;h3 id="51-структура-json-ld">5.1. Структура JSON-LD
&lt;/h3>&lt;p>В статьях блога мы в основном реализуем две схемы: &lt;code>Article&lt;/code> (Статья) и &lt;code>BreadcrumbList&lt;/code> (Хлебные крошки).&lt;/p>
&lt;pre class="mermaid">
flowchart TD
A[&amp;#34;Определения Schema.org&amp;#34;] --&amp;gt; B[&amp;#34;Схема Article&amp;#34;]
A --&amp;gt; C[&amp;#34;Схема BreadcrumbList&amp;#34;]
B --&amp;gt; D[&amp;#34;headline&amp;#34;]
B --&amp;gt; E[&amp;#34;datePublished&amp;#34;]
B --&amp;gt; F[&amp;#34;dateModified&amp;#34;]
B --&amp;gt; G[&amp;#34;author&amp;#34;]
B --&amp;gt; H[&amp;#34;image&amp;#34;]
C --&amp;gt; I[&amp;#34;Элемент списка 1&amp;#34;]
C --&amp;gt; J[&amp;#34;Элемент списка 2&amp;#34;]
I --&amp;gt; K[&amp;#34;position: 1&amp;#34;]
I --&amp;gt; L[&amp;#34;name: Главная&amp;#34;]
J --&amp;gt; M[&amp;#34;position: 2&amp;#34;]
J --&amp;gt; N[&amp;#34;name: Категория / Блог&amp;#34;]
&lt;/pre>
&lt;h3 id="52-генерация-json-ld-в-шаблонах-hugo">5.2. Генерация JSON-LD в шаблонах Hugo
&lt;/h3>&lt;p>Используя переменные Frontmatter, такие как &lt;code>.Date&lt;/code> и &lt;code>.Lastmod&lt;/code>, мы динамически выводим JSON-LD. Мы описываем это в &lt;code>head.html&lt;/code> с помощью тега &lt;code>&amp;lt;script type=&amp;quot;application/ld+json&amp;quot;&amp;gt;&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;span class="lnt">59
&lt;/span>&lt;span class="lnt">60
&lt;/span>&lt;span class="lnt">61
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">{{ if .IsPage }}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">script&lt;/span> &lt;span class="na">type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;application/ld+json&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;@context&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;https://schema.org&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;@type&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;Article&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;mainEntityOfPage&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;@type&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;WebPage&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;@id&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;{{ .Permalink }}&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;headline&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;{{ .Title | htmlEscape }}&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;description&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;{{ $description | htmlEscape }}&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;image&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;{{ $ogImage }}&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;datePublished&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;{{ .Date.Format &amp;#34;&lt;/span>&lt;span class="mi">2006&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">01&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">02&lt;/span>&lt;span class="nx">T15&lt;/span>&lt;span class="o">:&lt;/span>&lt;span class="mi">04&lt;/span>&lt;span class="o">:&lt;/span>&lt;span class="mi">05&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">07&lt;/span>&lt;span class="o">:&lt;/span>&lt;span class="mi">00&lt;/span>&lt;span class="s2">&amp;#34; }}&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;dateModified&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;{{ .Lastmod.Format &amp;#34;&lt;/span>&lt;span class="mi">2006&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">01&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">02&lt;/span>&lt;span class="nx">T15&lt;/span>&lt;span class="o">:&lt;/span>&lt;span class="mi">04&lt;/span>&lt;span class="o">:&lt;/span>&lt;span class="mi">05&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">07&lt;/span>&lt;span class="o">:&lt;/span>&lt;span class="mi">00&lt;/span>&lt;span class="s2">&amp;#34; }}&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;author&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;@type&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;Person&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;{{ if .Params.author }}{{ .Params.author }}{{ else }}{{ .Site.Params.author }}{{ end }}&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;publisher&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;@type&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;Organization&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;{{ .Site.Title }}&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;logo&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;@type&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;ImageObject&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;url&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;{{ .Site.Params.logo | absURL }}&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&amp;lt;!-- Схема BreadcrumbList --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">script&lt;/span> &lt;span class="na">type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;application/ld+json&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;@context&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;https://schema.org&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;@type&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;BreadcrumbList&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;itemListElement&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;@type&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;ListItem&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;position&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;Home&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;item&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;{{ .Site.BaseURL }}&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{{&lt;/span> &lt;span class="nx">$position&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="mi">2&lt;/span> &lt;span class="p">}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{{&lt;/span> &lt;span class="nx">range&lt;/span> &lt;span class="p">.&lt;/span>&lt;span class="nx">Params&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">categories&lt;/span> &lt;span class="p">}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">,{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;@type&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;ListItem&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;position&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{{&lt;/span> &lt;span class="nx">$position&lt;/span> &lt;span class="p">}},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;{{ . }}&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;item&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;{{ &amp;#34;&lt;/span>&lt;span class="nx">categories&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="s2">&amp;#34; | relLangURL }}{{ . | urlize | lower }}/&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{{&lt;/span> &lt;span class="nx">$position&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">add&lt;/span> &lt;span class="nx">$position&lt;/span> &lt;span class="mi">1&lt;/span> &lt;span class="p">}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{{&lt;/span> &lt;span class="nx">end&lt;/span> &lt;span class="p">}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">,{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;@type&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;ListItem&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;position&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{{&lt;/span> &lt;span class="nx">$position&lt;/span> &lt;span class="p">}},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;{{ .Title | htmlEscape }}&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;item&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;{{ .Permalink }}&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{{ end }}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>При развертывании строк в JSON-LD важно использовать &lt;code>htmlEscape&lt;/code> (или &lt;code>jsonify&lt;/code>), чтобы предотвратить поломку двойных кавычек. Это позволяет избежать синтаксических ошибок JSON независимо от того, какие символы используются во Frontmatter.&lt;/p>
&lt;hr>
&lt;h2 id="6-продвинутые-методы-использования-frontmatter">6. Продвинутые методы использования Frontmatter
&lt;/h2>&lt;p>Помимо базовых SEO-метаданных, Frontmatter в Hugo обладает функциями для реализации еще более продвинутых SEO-стратегий.&lt;/p>
&lt;h3 id="61-обработка-редиректов-с-помощью-псевдонимов-aliases">6.1. Обработка редиректов с помощью псевдонимов (Aliases)
&lt;/h3>&lt;p>При переходе со старого блогового сервиса на Hugo или изменении структуры постоянных ссылок (permalinks) необходимо перенаправлять трафик с существующих URL-адресов на новые. С помощью поля &lt;code>aliases&lt;/code> в Hugo можно автоматически генерировать страницы HTTP-Equiv refresh (мета-редирект) для старых URL.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nn">---&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">title&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;Новый заголовок статьи&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">slug&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;new-seo-post&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">aliases&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;/old-category/old-seo-post/&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;/2020/05/12/seo-tips/&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nn">---&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="62-срок-действия-статьи-и-планирование">6.2. Срок действия статьи и планирование
&lt;/h3>&lt;p>Для статей о кампаниях с ограниченным сроком действия или информации, теряющей ценность со временем, можно установить &lt;code>expiryDate&lt;/code>. Это позволит исключить их из результатов сборки и скрыть с сайта (возвращая ошибку 404) после определенной даты и времени. Это предотвращает сохранение в индексе старого контента низкого качества и снижение общего рейтинга сайта.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nn">---&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">title&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;SEO-методы эксклюзивно для 2026 года&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">publishDate&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;2026-01-01T00:00:00Z&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">expiryDate&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;2026-12-31T23:59:59Z&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nn">---&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;hr>
&lt;h2 id="7-производительность-сайта-и-core-web-vitals">7. Производительность сайта и Core Web Vitals
&lt;/h2>&lt;p>В SEO &lt;strong>скорость загрузки страницы&lt;/strong> так же важна, как и оптимизация тегов. Google учитывает показатели Core Web Vitals (LCP, FID/INP, CLS) в качестве факторов ранжирования.&lt;/p>
&lt;p>Хотя Hugo, будучи статическим сайтом, изначально обладает отличным показателем TTFB (Time to First Byte), для блогов с большим количеством изображений их оптимизация обязательна. Используя мощные функции обработки изображений (Image Processing) Hugo в сочетании с Frontmatter, вы можете автоматизировать изменение размера и конвертацию в форматы нового поколения (например, WebP) во время сборки.&lt;/p>
&lt;p>Например, можно создать шорткод, который автоматически генерирует изображение в формате WebP на стороне шаблона из пути к изображению, указанного во Frontmatter. Это может значительно повысить SEO-оценку.&lt;/p>
&lt;hr>
&lt;h2 id="8-заключение">8. Заключение
&lt;/h2>&lt;p>При ведении блога на Hugo Frontmatter — это не просто «список настроек», а «панель управления» для взаимодействия с поисковыми системами и социальными сетями.&lt;/p>
&lt;p>Полностью реализовав следующие пункты, описанные в этой статье, вы сделаете SEO-фундамент вашего блога прочным:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Динамическая генерация базовых метаданных&lt;/strong>: Надежный вывод Title, Description и Canonical.&lt;/li>
&lt;li>&lt;strong>Оптимизация для шеринга в соцсетях&lt;/strong>: Повышение CTR за счет пользовательской реализации OGP и Twitter Cards.&lt;/li>
&lt;li>&lt;strong>Полная поддержка структурированных данных&lt;/strong>: Поддержка расширенных результатов поиска (Rich Results) с помощью JSON-LD (Article, Breadcrumb).&lt;/li>
&lt;li>&lt;strong>Продвинутое управление трафиком&lt;/strong>: Перенаправления через Aliases и управление роботами с помощью метатегов.&lt;/li>
&lt;/ol>
&lt;p>Алгоритмы поисковых систем развиваются с каждым днем, но фундаментальный принцип SEO — предоставление сигналов, помогающих поисковикам «правильно понять содержание страницы» — остается неизменным. В совершенстве овладев гибким шаблонизатором Hugo и Frontmatter, вы сможете продолжать подавать эти сигналы в высочайшем качестве и добиться резкого увеличения трафика в вашем блоге.&lt;/p></description></item><item><title>Как бесплатно и сверхбыстро опубликовать блог Hugo с помощью Cloudflare Pages и GitHub Pages</title><link>http://kenji.blog/ru/p/hugo-hosting-cloudflare-vs-github-pages/</link><pubDate>Sat, 12 Sep 2026 12:00:00 +0900</pubDate><guid>http://kenji.blog/ru/p/hugo-hosting-cloudflare-vs-github-pages/</guid><description>&lt;img src="http://kenji.blog/p/hugo-hosting-cloudflare-vs-github-pages/img/eyecatch.jpg" alt="Featured image of post Как бесплатно и сверхбыстро опубликовать блог Hugo с помощью Cloudflare Pages и GitHub Pages" />&lt;p>Скорость загрузки (производительность), эксплуатационные расходы и безопасность являются критически важными факторами при управлении веб-сайтом или блогом. Раньше доминировала комбинация динамической CMS (системы управления контентом), такой как WordPress, и арендованного сервера, но сегодня огромное внимание привлекает архитектура, называемая «Jamstack». В частности, объединив «Hugo», сверхбыстрый генератор статических сайтов (SSG), написанный на языке Go, с современными сервисами хостинга, такими как Cloudflare Pages или GitHub Pages, можно создать &lt;strong>совершенно бесплатную и невероятно быструю&lt;/strong> среду для блога.&lt;/p>
&lt;p>В этой статье мы глубоко погрузимся в технические аспекты и пошагово объясним, как опубликовать статический сайт на Hugo в Cloudflare Pages и GitHub Pages. Мы рассмотрим различия в архитектуре каждой платформы, настройку CI/CD (непрерывная интеграция/непрерывное развертывание) с использованием GitHub Actions, оптимизацию DNS, стратегии кэширования, а также внедрение аналитики, уважающей конфиденциальность пользователей.&lt;/p>
&lt;hr>
&lt;h2 id="1-основы-генераторов-статических-сайтов-ssg-и-jamstack">1. Основы генераторов статических сайтов (SSG) и Jamstack
&lt;/h2>&lt;h3 id="11-почему-статические-сайты">1.1 Почему статические сайты?
&lt;/h3>&lt;p>Традиционные динамические CMS (например, WordPress) при каждом запросе пользователя отправляют запрос к базе данных (например, MySQL) и динамически генерируют HTML на стороне сервера (например, с помощью PHP) перед его возвратом. Хотя этот метод обеспечивает высокую гибкость, он имеет низкую устойчивость к резким всплескам трафика (вирусный эффект или DDoS-атаки) и часто приводит к усложнению инфраструктуры, например, из-за необходимости установки кэширующего сервера (Redis или Varnish) на переднем крае.&lt;/p>
&lt;p>С другой стороны, генераторы статических сайтов (SSG), использующие архитектуру Jamstack (JavaScript, APIs, and Markup), генерируют все HTML-файлы, CSS и JavaScript заранее (на этапе сборки). При поступлении запроса от пользователя веб-сервер (или CDN) просто возвращает уже сгенерированный статический файл, что обеспечивает невероятную скорость и надежную безопасность.&lt;/p>
&lt;h3 id="12-преимущества-hugo">1.2 Преимущества Hugo
&lt;/h3>&lt;p>Среди множества SSG, таких как Next.js, Gatsby, Jekyll и Astro, главной отличительной чертой Hugo является &lt;strong>скорость сборки&lt;/strong>. Благодаря параллельной обработке на языке Go даже сайты с тысячами или десятками тысяч страниц собираются всего за несколько секунд. Это значительно сокращает время ожидания в пайплайне CI/CD и напрямую улучшает опыт разработчика (DX: Developer Experience).&lt;/p>
&lt;hr>
&lt;h2 id="2-сравнение-архитектуры-хостинговых-сервисов">2. Сравнение архитектуры хостинговых сервисов
&lt;/h2>&lt;p>Следующая задача — решить, где хостить статические файлы, сгенерированные Hugo. Основные варианты включают Cloudflare Pages, GitHub Pages и Netlify, но они различаются по своей сетевой архитектуре.&lt;/p>
&lt;h3 id="21-cdn-и-периферийные-вычисления-edge-computing">2.1 CDN и периферийные вычисления (Edge Computing)
&lt;/h3>&lt;p>Все эти платформы используют глобально распределенные CDN (сети доставки контента) для доставки контента. Однако ключевым фактором дифференциации является не только кэширование статических файлов, но и возможность выполнения маршрутизации запросов и перезаписи заголовков в ближайшем к пользователю PoP (Point of Presence) с помощью «периферийных вычислений».&lt;/p>
&lt;pre class="mermaid">
graph TD
A[&amp;#34;Пользователь (Токио)&amp;#34;] --&amp;gt;|Низкая задержка| B[&amp;#34;Периферийный узел (NRT)&amp;#34;]
C[&amp;#34;Пользователь (Нью-Йорк)&amp;#34;] --&amp;gt;|Низкая задержка| D[&amp;#34;Периферийный узел (EWR)&amp;#34;]
E[&amp;#34;Пользователь (Лондон)&amp;#34;] --&amp;gt;|Низкая задержка| F[&amp;#34;Периферийный узел (LHR)&amp;#34;]
B --&amp;gt; G[&amp;#34;Оригинальное хранилище / Хранилище объектов&amp;#34;]
D --&amp;gt; G
F --&amp;gt; G
&lt;/pre>
&lt;h3 id="22-github-pages">2.2 GitHub Pages
&lt;/h3>&lt;p>GitHub Pages — это сервис, который позволяет публиковать файлы HTML, CSS и JavaScript непосредственно из репозитория GitHub. В основе лежит CDN, такой как Fastly, что обеспечивает достаточную производительность. Тем не менее, как чистая инфраструктура он имеет некоторые ограничения: настройка заголовков (например, &lt;code>Cache-Control&lt;/code> или заголовки безопасности) ограничена, а настройка перенаправлений зависит от meta refresh в HTML или плагинов Jekyll.&lt;/p>
&lt;h3 id="23-cloudflare-pages">2.3 Cloudflare Pages
&lt;/h3>&lt;p>Cloudflare Pages — это сервис хостинга статических сайтов, построенный на базе крупнейшей в мире Anycast-сети Cloudflare (охватывающей более 275 городов).
Он обеспечивает выдающиеся возможности настройки производительности, включая стандартную поддержку HTTP/3 (QUIC), оптимизацию изображений и интеграцию с периферийными функциями (Cloudflare Workers). Большим преимуществом является отсутствие платы за пропускную способность, что позволяет бесплатно работать с сайтом даже при резких всплесках трафика.&lt;/p>
&lt;h3 id="24-netlify">2.4 Netlify
&lt;/h3>&lt;p>Netlify является пионером Jamstack и предлагает решение «все в одном» для DX, объединяя функции форм, аутентификации (Identity) и бессерверных функций. Однако следует быть осторожным с контролем затрат, особенно для блогов с большим количеством изображений и видео, так как при превышении бесплатного лимита пропускной способности (100 ГБ в месяц) применяются высокие тарифы за использование.&lt;/p>
&lt;hr>
&lt;h2 id="3-теоретический-расчет-производительности-и-задержки-математическая-модель-в-latex">3. Теоретический расчет производительности и задержки (математическая модель в LaTeX)
&lt;/h2>&lt;p>При оценке производительности веб-сайта сокращение задержки (Latency) является важнейшим показателем. Давайте смоделируем, насколько использование CDN (Edge) сокращает задержку по сравнению с прямым доступом к исходному серверу.&lt;/p>
&lt;p>Пусть $C$ — вероятность того, что запрос пользователя попадет в кэш («коэффициент попадания в кэш» или Cache Hit Ratio), где $0 \le C \le 1$.
Пусть $L_{origin}$ — задержка до исходного сервера, а $L_{edge}$ — задержка до ближайшего периферийного узла.&lt;/p>
&lt;p>Новая средняя задержка $L_{new}$ рассчитывается как следующее математическое ожидание:&lt;/p>
$$ L_{new} = C \times L_{edge} + (1 - C) \times (L_{edge} + L_{origin}) $$&lt;p>Упростив это уравнение, получим:&lt;/p>
$$ L_{new} = L_{edge} + (1 - C) \times L_{origin} $$&lt;p>Например, если пользователь в Токио обращается к исходному серверу на восточном побережье США (Нью-Йорк), учитывая физическое расстояние по оптоволокну и задержку обработки маршрутизатором, $L_{origin}$ составит около 200 мс. С другой стороны, если используется CDN, например Cloudflare, пользователь может подключиться к периферийному узлу в Токио, и $L_{edge}$ сократится примерно до 10 мс.&lt;/p>
&lt;p>Предположим, что коэффициент попадания в кэш $C = 0.95$ (95%):&lt;/p>
$$ L_{new} = 10 + (1 - 0.95) \times 200 = 10 + 0.05 \times 200 = 10 + 10 = 20 \text{ мс} $$&lt;p>Таким образом, внедрение CDN может кардинально (примерно на 90%) сократить среднюю задержку с 210 мс до 20 мс.&lt;/p>
&lt;hr>
&lt;h2 id="4-построение-пайплайна-cicd-с-помощью-github-actions">4. Построение пайплайна CI/CD с помощью GitHub Actions
&lt;/h2>&lt;p>Для автоматизации процесса обновления блога Hugo мы настроим пайплайн CI/CD с использованием GitHub Actions. Благодаря этому вам достаточно написать статью в Markdown локально и выполнить &lt;code>git push&lt;/code>, после чего автоматически запустится сборка и сайт будет развернут на Cloudflare Pages или GitHub Pages.&lt;/p>
&lt;p>На следующей диаграмме последовательности показан весь процесс от отправки (push) статьи до её доставки пользователю:&lt;/p>
&lt;pre class="mermaid">
sequenceDiagram
participant U as &amp;#34;Пользователь (Автор)&amp;#34;
participant G as &amp;#34;Репозиторий GitHub&amp;#34;
participant A as &amp;#34;GitHub Actions&amp;#34;
participant C as &amp;#34;Cloudflare Pages&amp;#34;
participant V as &amp;#34;Посетитель&amp;#34;
U-&amp;gt;&amp;gt;G: &amp;#34;git push origin main&amp;#34;
G-&amp;gt;&amp;gt;A: &amp;#34;Запуск события Push&amp;#34;
A-&amp;gt;&amp;gt;A: &amp;#34;Checkout и настройка Hugo&amp;#34;
A-&amp;gt;&amp;gt;A: &amp;#34;hugo --minify (Сборка)&amp;#34;
A-&amp;gt;&amp;gt;C: &amp;#34;Развертывание в Cloudflare Pages (Direct Upload)&amp;#34;
C--&amp;gt;&amp;gt;A: &amp;#34;URL развертывания (Успех)&amp;#34;
V-&amp;gt;&amp;gt;C: &amp;#34;Запрос веб-страницы&amp;#34;
C--&amp;gt;&amp;gt;V: &amp;#34;Возврат кэшированного HTML (Edge)&amp;#34;
&lt;/pre>
&lt;h3 id="41-настройка-развертывания-для-cloudflare-pages-direct-upload">4.1 Настройка развертывания для Cloudflare Pages (Direct Upload)
&lt;/h3>&lt;p>В Cloudflare Pages есть два способа: подключить репозиторий GitHub для сборки в инфраструктуре Cloudflare или выполнить «Direct Upload» (прямую загрузку) статических файлов, собранных с помощью GitHub Actions. Если вы хотите более строго управлять версией Hugo и связывать его с другими задачами (такими как тестирование или оптимизация изображений), рекомендуется собирать проект в GitHub Actions и использовать Direct Upload.&lt;/p>
&lt;p>Ниже приведен практический пример файла &lt;code>.github/workflows/deploy.yml&lt;/code> для развертывания в Cloudflare Pages.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Deploy Hugo site to Cloudflare Pages&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">on&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">push&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">branches&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;main&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">workflow_dispatch&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">jobs&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">build-and-deploy&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">runs-on&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;ubuntu-latest&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">steps&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Checkout repository&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;actions/checkout@v4&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">with&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">submodules&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;recursive&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">fetch-depth&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">0&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Setup Hugo&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;peaceiris/actions-hugo@v3&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">with&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">hugo-version&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;0.125.0&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">extended&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Build Hugo Site&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;hugo --minify --gc&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">env&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">HUGO_ENVIRONMENT&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;production&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Deploy to Cloudflare Pages&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;cloudflare/pages-action@v1&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">with&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">apiToken&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">${{ secrets.CLOUDFLARE_API_TOKEN }}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">accountId&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">${{ secrets.CLOUDFLARE_ACCOUNT_ID }}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">projectName&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;your-project-name&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">directory&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;public&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">gitHubToken&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">${{ secrets.GITHUB_TOKEN }}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">branch&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;main&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>В этом пайплайне файлы HTML/CSS/JS минифицируются с помощью опции &lt;code>--minify&lt;/code>, а ненужные файлы удаляются с помощью &lt;code>--gc&lt;/code>. Это основы оптимизации производительности.&lt;/p>
&lt;hr>
&lt;h2 id="5-глубокое-погружение-в-настройки-dns-пользовательские-домены-и-записи-cname--alias">5. Глубокое погружение в настройки DNS: пользовательские домены и записи CNAME / ALIAS
&lt;/h2>&lt;p>При использовании собственного домена (например, &lt;code>kenji.blog&lt;/code>) правильная настройка DNS (Domain Name System) просто необходима.&lt;/p>
&lt;h3 id="51-ограничения-записей-cname-и-zone-apex">5.1 Ограничения записей CNAME и Zone Apex
&lt;/h3>&lt;p>Обычно при направлении поддомена (например, &lt;code>www.kenji.blog&lt;/code>) на внешний сервис используется запись &lt;code>CNAME&lt;/code>. Однако спецификации DNS (RFC 1034) не позволяют устанавливать запись &lt;code>CNAME&lt;/code> для корневого домена (Zone Apex, также называемого голым доменом, например, &lt;code>kenji.blog&lt;/code>). Это правило связано с тем, что в Zone Apex обязательно должны присутствовать записи SOA (Start of Authority), NS (Name Server) и MX (Mail Exchange), а CNAME не может сосуществовать с другими записями ресурсов.&lt;/p>
&lt;h3 id="52-решение-записи-alias--aname--cname-flattening">5.2 Решение: записи ALIAS / ANAME / CNAME Flattening
&lt;/h3>&lt;p>Для решения этой проблемы современные DNS-провайдеры предлагают собственные расширения:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Записи ALIAS / ANAME&lt;/strong>: Динамически разрешают имена на стороне DNS-сервера и возвращают клиенту конечную запись A (IP-адрес). Поддерживается такими сервисами, как Amazon Route 53.&lt;/li>
&lt;li>&lt;strong>CNAME Flattening&lt;/strong>: Функция, предоставляемая Cloudflare. Она действует так, как если бы CNAME был настроен для Zone Apex, но при этом авторитетный DNS-сервер Cloudflare прозрачно возвращает клиенту автоматически разрешенные IP-адреса (записи A и AAAA).&lt;/li>
&lt;/ul>
&lt;p>При использовании Cloudflare Pages наиболее бесшовной и высокопроизводительной конфигурацией является делегирование серверов имен вашего домена в Cloudflare и использование этой функции «CNAME Flattening».&lt;/p>
&lt;hr>
&lt;h2 id="6-стратегия-кэширования-и-управление-http-заголовками">6. Стратегия кэширования и управление HTTP-заголовками
&lt;/h2>&lt;p>Еще одним ключом к ускорению работы статических сайтов является «стратегия кэширования». В Cloudflare Pages вы можете использовать сгенерированный файл (&lt;code>_headers&lt;/code> файл) для точного управления HTTP-заголовками ответа.&lt;/p>
&lt;h3 id="61-edge-cache-против-browser-cache">6.1 Edge Cache против Browser Cache
&lt;/h3>&lt;p>Кэш можно разделить на две основные категории: «Edge Cache» (кэш на периферии), хранящийся на стороне CDN, и «Browser Cache» (кэш браузера), сохраняемый в браузере пользователя.&lt;/p>
&lt;p>В идеале статические файлы (изображения, CSS, JS и т.д., чьи имена файлов содержат хэш) должны кэшироваться браузером на длительное время. С другой стороны, чтобы обновления HTML-файлов отражались немедленно, обычно делают кэш браузера коротким (или отключают его) и обрабатывают их с помощью кэша на периферии (edge cache).&lt;/p>
&lt;p>Пример настройки &lt;code>_headers&lt;/code> в Cloudflare Pages:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;span class="lnt">9
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl"># HTML-файлы не кэшируются браузером и проверяются каждый раз
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">/*.html
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Cache-Control: public, max-age=0, must-revalidate
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"># Ресурсные файлы (CSS/JS/изображения) кэшируются в браузере на 1 год
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">/assets/*
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Cache-Control: public, max-age=31536000, immutable
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">/img/*
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Cache-Control: public, max-age=31536000, immutable
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="62-формула-расчета-снижения-затрат-на-пропускную-способность">6.2 Формула расчета снижения затрат на пропускную способность
&lt;/h3>&lt;p>Настраивая правильные заголовки кэширования, вы можете значительно сократить объем передаваемых данных с сервера (периферии). Ежемесячные затраты на пропускную способность $Cost$ могут быть выражены следующей моделью на основе объема передачи $B_i$ каждого ресурса, коэффициента попадания в кэш $C_i$ и цены за единицу пропускной способности $R$:&lt;/p>
$$ Cost = \sum_{i=1}^{n} \left( B_i \times (1 - C_i) \times R \right) $$&lt;p>Поскольку Cloudflare не взимает плату за исходящий трафик ($R = 0$), прямые финансовые затраты составляют $0$. Однако, если вы используете другие инфраструктуры, такие как GitHub Pages, или используете AWS S3 в качестве бэкенда, максимизация коэффициента попадания в кэш $C_i$ имеет решающее значение для снижения затрат на инфраструктуру.&lt;/p>
&lt;hr>
&lt;h2 id="7-веб-аналитика-сочетающая-конфиденциальность-и-производительность">7. Веб-аналитика, сочетающая конфиденциальность и производительность
&lt;/h2>&lt;p>При ведении блога веб-аналитика (Web Analytics) необходима для понимания того, сколько пользователей его посещают. Долгое время Google Analytics (GA4) был стандартом де-факто, но в связи с недавними тенденциями в области защиты конфиденциальности (GDPR, CCPA) и отказом от сторонних файлов cookie, ситуация меняется.&lt;/p>
&lt;h3 id="71-влияние-на-веб-производительность">7.1 Влияние на веб-производительность
&lt;/h3>&lt;p>Внедрение Google Analytics (в частности, &lt;code>gtag.js&lt;/code> или Google Tag Manager) приводит к загрузке и выполнению множества внешних скриптов, что негативно сказывается на производительности (особенно на TTFB и времени блокировки основного потока).&lt;/p>
&lt;p>Давайте разобьем время загрузки сайта на компоненты:&lt;/p>
&lt;pre class="mermaid">
pie title Типичное распределение времени загрузки сайта
&amp;#34;TTFB (Время до первого байта)&amp;#34; : 15
&amp;#34;Загрузка контента (HTML/CSS)&amp;#34; : 20
&amp;#34;Парсинг DOM и рендеринг&amp;#34; : 45
&amp;#34;Аналитика и сторонний JS&amp;#34; : 20
&lt;/pre>
&lt;p>Нередко на сторонние JS-инструменты аналитики уходит около 20–30% всего времени загрузки.&lt;/p>
&lt;h3 id="72-внедрение-cloudflare-web-analytics">7.2 Внедрение Cloudflare Web Analytics
&lt;/h3>&lt;p>В связи с этим привлекают внимание аналитические инструменты, ориентированные на конфиденциальность и не использующие файлы cookie (Cookieless), такие как Cloudflare Web Analytics и Plausible Analytics.&lt;/p>
&lt;p>Cloudflare Web Analytics работает просто путем встраивания очень легкого фрагмента JavaScript и не выдает файлы cookie, поэтому вам не нужно устанавливать раздражающий баннер согласия на использование файлов cookie (Cookie Consent Banner).&lt;/p>
&lt;p>Его внедрение в Hugo также очень просто. Просто добавьте предоставленный фрагмент в &lt;code>layouts/partials/head.html&lt;/code> или &lt;code>layouts/partials/analytics.html&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">{{ if eq hugo.Environment &amp;#34;production&amp;#34; }}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&amp;lt;!-- Cloudflare Web Analytics --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">script&lt;/span> &lt;span class="na">defer&lt;/span> &lt;span class="na">src&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#39;https://static.cloudflareinsights.com/beacon.min.js&amp;#39;&lt;/span> &lt;span class="na">data-cf-beacon&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#39;{&amp;#34;token&amp;#34;: &amp;#34;YOUR_CLOUDFLARE_BEACON_TOKEN&amp;#34;}&amp;#39;&lt;/span>&lt;span class="p">&amp;gt;&amp;lt;/&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&amp;lt;!-- End Cloudflare Web Analytics --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{{ end }}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Добавив атрибут &lt;code>defer&lt;/code>, скрипт загружается асинхронно, не блокируя парсинг HTML, и выполняется после построения DOM. Это сводит к минимуму влияние на начальную скорость отображения (LCP: Largest Contentful Paint и FCP: First Contentful Paint).&lt;/p>
&lt;hr>
&lt;h2 id="8-заключение-и-лучшие-практики">8. Заключение и лучшие практики
&lt;/h2>&lt;p>При работе со статическими сайтами на Hugo использование современных хостинговых платформ, таких как Cloudflare Pages или GitHub Pages, дает огромные преимущества с точки зрения экономической эффективности, скорости загрузки и безопасности.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Сверхбыстрые сборки&lt;/strong>: Используйте скорость Hugo, чтобы минимизировать время выполнения пайплайна CI/CD (GitHub Actions).&lt;/li>
&lt;li>&lt;strong>Доставка на периферии&lt;/strong>: Используйте периферийную сеть Cloudflare для доставки контента пользователям по всему миру с задержками порядка миллисекунд.&lt;/li>
&lt;li>&lt;strong>Правильная настройка DNS&lt;/strong>: Используйте CNAME Flattening для безопасной и быстрой работы Zone Apex (пользовательский домен).&lt;/li>
&lt;li>&lt;strong>Оптимизация стратегии кэширования&lt;/strong>: Используйте &lt;code>_headers&lt;/code>, чтобы правильно разделить кэширование в браузере и кэширование на периферии для каждого типа ресурсов.&lt;/li>
&lt;li>&lt;strong>Легкая аналитика&lt;/strong>: Внедрите такие решения, как Cloudflare Web Analytics, которые уважают конфиденциальность без ущерба для производительности.&lt;/li>
&lt;/ol>
&lt;p>Объединив все это, вы сможете бесплатно создать масштабируемую и надежную систему блогов, способную выдерживать огромный трафик — миллионы просмотров страниц в месяц. Если вы планируете запустить технический блог, корпоративный сайт или сайт-портфолио, обязательно попробуйте эту комбинацию Jamstack + Hugo + Cloudflare Pages.&lt;/p></description></item><item><title>Как избежать нехватки идей для технических статей! Эффективные методы получения информации и генерации идей</title><link>http://kenji.blog/ru/p/tech-blog-idea-generation-and-input-strategy/</link><pubDate>Sat, 12 Sep 2026 12:00:00 +0900</pubDate><guid>http://kenji.blog/ru/p/tech-blog-idea-generation-and-input-strategy/</guid><description>&lt;img src="http://kenji.blog/p/tech-blog-idea-generation-and-input-strategy/img/eyecatch.jpg" alt="Featured image of post Как избежать нехватки идей для технических статей! Эффективные методы получения информации и генерации идей" />&lt;p>Если вы ведете технический блог в качестве инженера или исследователя, то почти наверняка сталкиваетесь с определенным препятствием. Это «нехватка идей» (или «исчерпание тем»). Даже если первые несколько статей написаны легко, со временем нередки переживания вроде «я не знаю, о чем писать дальше» или «мне катастрофически не хватает инпута для аутпута». Написание статей для технического блога сильно зависит не только от навыков письма, но и от проектирования целой системы ежедневного сбора знаний, их систематизации и создания новой ценности путем их комбинирования.&lt;/p>
&lt;p>В этой статье я максимально подробно и с технической точки зрения расскажу о &lt;strong>систематизированном конвейере ввода и вывода&lt;/strong>, который позволяет почти бесконечно генерировать идеи для технических статей. Мы начнем с механизма автоматического извлечения трендовых тем из высококачественных зарубежных источников, таких как Hacker News и Lobsters, с помощью API, и их регулярного запуска через GitHub Actions. Затем мы построим продвинутую систему управления личными знаниями (PKM: Personal Knowledge Management), в которой собранная информация будет систематизироваться в виде знаний с использованием метода Zettelkasten (Цеттелькастен) в Obsidian, а комбинация OpenAI Embeddings API и Pinecone (векторной базы данных) обеспечит возможность семантического поиска.&lt;/p>
&lt;p>Более того, чтобы компенсировать ограничения человеческой памяти, мы применим интервальное повторение (Spaced Repetition) на основе кривой забывания Эббингауза с использованием программы Anki. Мы глубоко погрузимся в процесс сублимации закрепленных знаний в новые идеи с помощью «комбинаторной креативности» (Combinatorial Creativity), подкрепляя это конкретными математическими моделями и примерами реализации на Python-скриптах.&lt;/p>
&lt;h2 id="1-информационная-энтропия-и-механизм-нехватки-идей">1. Информационная энтропия и механизм «нехватки идей»
&lt;/h2>&lt;p>Почему у нас возникает «нехватка идей»? С точки зрения теории информации можно сказать, что это состояние, когда «количество информации» в нашей системе знаний истощается или становится слишком однородным.&lt;/p>
&lt;p>Информационная энтропия $H(X)$, предложенная Клодом Шенноном, представляет собой неопределенность (или степень удивления) информации, получаемой из источника.&lt;/p>
$$ H(X) = - \sum_{i=1}^{n} P(x_i) \log_2 P(x_i) $$&lt;p>Здесь $X$ — это случайная величина тем, получаемых из источника, а $P(x_i)$ — вероятность встречи с этой темой $x_i$. Если вы обычно просматриваете одни и те же веб-сайты (например, только определенные местные новостные порталы или документацию по одному и тому же техническому стеку), то вероятность конкретного $P(x_i)$ становится крайне высокой, в результате чего энтропия всей системы $H(X)$ снижается. Состояние с низкой энтропией означает состояние «без новых открытий (удивлений)», что и является фундаментальной причиной «нехватки идей».&lt;/p>
&lt;p>Чтобы поддерживать энтропию на высоком уровне, необходимо намеренно включать источники информации, с которыми вы обычно не взаимодействуете, в качестве «шума», тем самым выравнивая распределение вероятностей соприкосновения с неизвестными темами. Это и есть главная причина для автоматизации получения информации из разнообразных источников.&lt;/p>
&lt;h2 id="2-создание-автоматизированного-конвейера-сбора-информации-api-hacker-news-и-lobsters">2. Создание автоматизированного конвейера сбора информации: API Hacker News и Lobsters
&lt;/h2>&lt;p>Для получения качественного инпута эффективно извлекать трендовую информацию из хороших инженерных сообществ с низким уровнем шума. Такие платформы, как Hacker News (управляемая Y Combinator) и Lobsters, идеально подходят в качестве мест, где ведутся глубокие технические дискуссии. Однако ежедневный просмотр этих сайтов отнимает время и истощает когнитивные ресурсы.&lt;/p>
&lt;p>Поэтому мы создадим скрипт на Python для автоматического извлечения статей с определенным рейтингом (score) из этих API.&lt;/p>
&lt;h3 id="python-скрипт-для-извлечения-трендовых-статей">Python-скрипт для извлечения трендовых статей
&lt;/h3>&lt;p>Следующий скрипт получает статьи, соответствующие определенным критериям, из Firebase API Hacker News и JSON-ленты Lobsters, и сохраняет их в виде Markdown-файла.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;span class="lnt">59
&lt;/span>&lt;span class="lnt">60
&lt;/span>&lt;span class="lnt">61
&lt;/span>&lt;span class="lnt">62
&lt;/span>&lt;span class="lnt">63
&lt;/span>&lt;span class="lnt">64
&lt;/span>&lt;span class="lnt">65
&lt;/span>&lt;span class="lnt">66
&lt;/span>&lt;span class="lnt">67
&lt;/span>&lt;span class="lnt">68
&lt;/span>&lt;span class="lnt">69
&lt;/span>&lt;span class="lnt">70
&lt;/span>&lt;span class="lnt">71
&lt;/span>&lt;span class="lnt">72
&lt;/span>&lt;span class="lnt">73
&lt;/span>&lt;span class="lnt">74
&lt;/span>&lt;span class="lnt">75
&lt;/span>&lt;span class="lnt">76
&lt;/span>&lt;span class="lnt">77
&lt;/span>&lt;span class="lnt">78
&lt;/span>&lt;span class="lnt">79
&lt;/span>&lt;span class="lnt">80
&lt;/span>&lt;span class="lnt">81
&lt;/span>&lt;span class="lnt">82
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">requests&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">json&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">datetime&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">datetime&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">os&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Настройки&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">HN_TOPSTORIES_URL&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;https://hacker-news.firebaseio.com/v0/topstories.json&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">HN_ITEM_URL&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;https://hacker-news.firebaseio.com/v0/item/&lt;/span>&lt;span class="si">{}&lt;/span>&lt;span class="s2">.json&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">LOBSTERS_URL&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;https://lobste.rs/hottest.json&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">MIN_HN_SCORE&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">100&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">MIN_LOBSTERS_SCORE&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">10&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">OUTPUT_DIR&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;./daily_inputs&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">get_hacker_news_trends&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;Получить топовые статьи с высоким рейтингом из Hacker News&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Fetching Hacker News top stories...&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">requests&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">HN_TOPSTORIES_URL&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">response&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">status_code&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="mi">200&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">[]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">story_ids&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">response&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">json&lt;/span>&lt;span class="p">()[:&lt;/span>&lt;span class="mi">30&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="c1"># Ограничить топ-30&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">trending_stories&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">story_id&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">story_ids&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">item_resp&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">requests&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">HN_ITEM_URL&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">format&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">story_id&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">item_resp&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">status_code&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">200&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">item&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">item_resp&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">json&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">item&lt;/span> &lt;span class="ow">and&lt;/span> &lt;span class="n">item&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;score&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">&amp;gt;=&lt;/span> &lt;span class="n">MIN_HN_SCORE&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">trending_stories&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;title&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">item&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;title&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;url&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">item&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;url&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;https://news.ycombinator.com/item?id=&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">story_id&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;score&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">item&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;score&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;source&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Hacker News&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">trending_stories&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">get_lobsters_trends&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;Получить статьи с высоким рейтингом из Lobsters&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Fetching Lobsters hottest stories...&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">requests&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">LOBSTERS_URL&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">response&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">status_code&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="mi">200&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">[]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">items&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">response&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">json&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">trending_stories&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">item&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">items&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">item&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;score&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">&amp;gt;=&lt;/span> &lt;span class="n">MIN_LOBSTERS_SCORE&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">trending_stories&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;title&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">item&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;title&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;url&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">item&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;url&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">item&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;comments_url&amp;#34;&lt;/span>&lt;span class="p">)),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;score&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">item&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;score&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;source&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Lobsters&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">trending_stories&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">save_to_markdown&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">stories&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;Сохранить полученные статьи как Markdown-файл&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">path&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">exists&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">OUTPUT_DIR&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">makedirs&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">OUTPUT_DIR&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">today_str&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">datetime&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">now&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">strftime&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;%Y-%m-&lt;/span>&lt;span class="si">%d&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">filepath&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">path&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">join&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">OUTPUT_DIR&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;trends_&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">today_str&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">.md&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">with&lt;/span> &lt;span class="nb">open&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">filepath&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;w&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">encoding&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;utf-8&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="n">f&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">f&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">write&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;# Daily Tech Trends: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">today_str&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="se">\n\n&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">story&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">stories&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">f&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">write&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;## [&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">story&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;title&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">](&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">story&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;url&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">)&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">f&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">write&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;- **Source**: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">story&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;source&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">f&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">write&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;- **Score**: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">story&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;score&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">f&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">write&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;- **Notes**: (добавьте свои заметки здесь)&lt;/span>&lt;span class="se">\n\n&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Saved &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="nb">len&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">stories&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> stories to &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">filepath&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="vm">__name__&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;__main__&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">hn_stories&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">get_hacker_news_trends&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">lobsters_stories&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">get_lobsters_trends&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">all_stories&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">hn_stories&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">lobsters_stories&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Сортировка по рейтингу по убыванию&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">all_stories&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">sort&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">key&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="k">lambda&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;score&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="n">reverse&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">save_to_markdown&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">all_stories&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Этот скрипт предлагает нечто большее, чем просто обычный RSS-ридер. Благодаря фильтрации по рейтингу (score), вы можете извлекать только те технические темы, которые действительно привлекают внимание сообщества (высокий уровень полезного сигнала при малом количестве шума).&lt;/p>
&lt;h2 id="3-планирование-и-автоматизация-с-помощью-github-actions">3. Планирование и автоматизация с помощью GitHub Actions
&lt;/h2>&lt;p>Запускать созданный Python-скрипт вручную каждый день утомительно. Основа автоматизации — максимальное сокращение вмешательства человека. С помощью функции Cron в GitHub Actions мы построим систему, которая ежедневно в заданное время будет выполнять скрипт и автоматически коммитить результаты в репозиторий.&lt;/p>
&lt;p>Создайте файл &lt;code>.github/workflows/daily_trends.yml&lt;/code> в корне вашего проекта и напишите следующее:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Daily Tech Trends Scraper&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">on&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">schedule&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">cron&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;0 0 * * *&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># Ежедневно в 0:00 UTC&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">workflow_dispatch&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># Для ручного запуска&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">jobs&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">scrape-and-commit&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">runs-on&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">ubuntu-latest&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">steps&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Checkout Repository&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">actions/checkout@v3&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Setup Python&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">actions/setup-python@v4&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">with&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">python-version&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;3.10&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Install Dependencies&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">|&lt;/span>&lt;span class="sd">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> python -m pip install --upgrade pip
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> pip install requests
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> &lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Run Scraper Script&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">python scripts/fetch_trends.py&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Commit and Push Changes&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">|&lt;/span>&lt;span class="sd">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> git config --local user.email &amp;#34;action@github.com&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> git config --local user.name &amp;#34;GitHub Action&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> git add daily_inputs/
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> git commit -m &amp;#34;Auto-update daily tech trends [skip ci]&amp;#34; || echo &amp;#34;No changes to commit&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> git push&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Благодаря этому каждое утро, открывая Obsidian, вы будете автоматически получать важные темы текущего дня, добавленные в ваш Inbox (&lt;code>daily_inputs/&lt;/code>) в формате Markdown.&lt;/p>
&lt;h2 id="4-создание-сети-знаний-с-помощью-zettelkasten-и-obsidian">4. Создание сети знаний с помощью Zettelkasten и Obsidian
&lt;/h2>&lt;p>Автоматически собранная информация пока остается просто «данными». Необходим процесс, который сублимирует их в «знания». Здесь на помощь приходят метод Zettelkasten (Цеттелькастен) и Obsidian.&lt;/p>
&lt;p>Zettelkasten — это метод ведения заметок, разработанный немецким социологом Никласом Луманом. Вместо того чтобы сортировать заметки по иерархическим папкам, отдельные заметки сохраняются небольшими (атомарными) и связываются друг с другом с помощью ссылок, образуя сеть знаний, подобную нейронным цепям мозга.&lt;/p>
&lt;p>В Zettelkasten в основном существует 3 типа заметок:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Fleeting Notes (Быстрые заметки)&lt;/strong>: Временные записи пришедших в голову идей или собранной информации. Markdown-файлы с трендовой информацией, которые мы автоматически генерировали ранее, подпадают под эту категорию.&lt;/li>
&lt;li>&lt;strong>Literature Notes (Литературные заметки)&lt;/strong>: Заметки, написанные своими словами после прочтения статей или книг.&lt;/li>
&lt;li>&lt;strong>Permanent Notes (Постоянные заметки)&lt;/strong>: Завершенные рассуждения по одной конкретной теме. Именно они становятся непосредственными семенами для статей в блоге.&lt;/li>
&lt;/ol>
&lt;p>Используя функцию обратных ссылок Obsidian (&lt;code>[[имя заметки]]&lt;/code>), можно, например, связать заметку «Владение в Rust» с заметкой «История сборки мусора», тем самым обнаруживая неожиданные связи между идеями.&lt;/p>
&lt;h2 id="5-семантический-поиск-с-использованием-векторной-базы-данных-pinecone-и-openai-embeddings">5. Семантический поиск с использованием векторной базы данных (Pinecone) и OpenAI Embeddings
&lt;/h2>&lt;p>Когда количество заметок увеличивается до сотен или тысяч, найти нужную с помощью обычного поиска по ключевым словам (полнотекстового поиска) становится трудно. В ситуациях вроде «я не могу вспомнить ключевые слова, но хочу найти заметку с похожей концепцией» на помощь приходит семантический поиск, использующий Embeddings из больших языковых моделей (LLM).&lt;/p>
&lt;p>Используя модель &lt;code>text-embedding-ada-002&lt;/code> (или &lt;code>text-embedding-3-small&lt;/code>) от OpenAI, мы преобразуем каждую Markdown-заметку в Obsidian в многомерный вектор (массив чисел с сотнями или тысячами измерений). В таком векторном пространстве тексты с близким смыслом будут иметь близкое физическое расстояние.&lt;/p>
&lt;p>Для измерения сходства между векторами широко используется косинусное сходство (Cosine Similarity).&lt;/p>
$$ \text{similarity} = \cos(\theta) = \frac{\mathbf{A} \cdot \mathbf{B}}{\|\mathbf{A}\| \|\mathbf{B}\|} = \frac{\sum_{i=1}^{n} A_i B_i}{\sqrt{\sum_{i=1}^{n} A_i^2} \sqrt{\sum_{i=1}^{n} B_i^2}} $$&lt;p>Здесь $\mathbf{A}$ и $\mathbf{B}$ — векторы строки запроса и заметки соответственно. Для быстрого выполнения этих вычислений используются векторные базы данных, такие как Pinecone или Qdrant.&lt;/p>
&lt;h3 id="пример-реализации-семантического-поиска">Пример реализации семантического поиска
&lt;/h3>&lt;p>Ниже приведена часть Python-скрипта, который сканирует директорию с заметками Obsidian, векторизует их через OpenAI API и загружает/обновляет (upsert) их в Pinecone.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;span class="lnt">59
&lt;/span>&lt;span class="lnt">60
&lt;/span>&lt;span class="lnt">61
&lt;/span>&lt;span class="lnt">62
&lt;/span>&lt;span class="lnt">63
&lt;/span>&lt;span class="lnt">64
&lt;/span>&lt;span class="lnt">65
&lt;/span>&lt;span class="lnt">66
&lt;/span>&lt;span class="lnt">67
&lt;/span>&lt;span class="lnt">68
&lt;/span>&lt;span class="lnt">69
&lt;/span>&lt;span class="lnt">70
&lt;/span>&lt;span class="lnt">71
&lt;/span>&lt;span class="lnt">72
&lt;/span>&lt;span class="lnt">73
&lt;/span>&lt;span class="lnt">74
&lt;/span>&lt;span class="lnt">75
&lt;/span>&lt;span class="lnt">76
&lt;/span>&lt;span class="lnt">77
&lt;/span>&lt;span class="lnt">78
&lt;/span>&lt;span class="lnt">79
&lt;/span>&lt;span class="lnt">80
&lt;/span>&lt;span class="lnt">81
&lt;/span>&lt;span class="lnt">82
&lt;/span>&lt;span class="lnt">83
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">os&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">glob&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">openai&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">OpenAI&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">pinecone&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">Pinecone&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ServerlessSpec&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Настройка ключей API (получение из переменных окружения)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">OPENAI_API_KEY&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">getenv&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;OPENAI_API_KEY&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">PINECONE_API_KEY&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">getenv&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;PINECONE_API_KEY&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">client&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">OpenAI&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">api_key&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">OPENAI_API_KEY&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">pc&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Pinecone&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">api_key&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">PINECONE_API_KEY&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">INDEX_NAME&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;obsidian-notes&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">OBSIDIAN_DIR&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;/path/to/obsidian/vault/PermanentNotes&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">init_pinecone&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;Инициализация индекса Pinecone&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">INDEX_NAME&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">pc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">list_indexes&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">names&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">pc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">create_index&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">INDEX_NAME&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dimension&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1536&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># Размерность text-embedding-3-small / ada-002&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">metric&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;cosine&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">spec&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">ServerlessSpec&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">cloud&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;aws&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">region&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;us-east-1&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">pc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">Index&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">INDEX_NAME&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">get_embedding&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;Векторизация текста с помощью OpenAI API&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">client&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">embeddings&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">create&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">input&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;text-embedding-3-small&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">response&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">embedding&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">sync_notes_to_pinecone&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">index&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;Чтение Markdown-файлов, векторизация и сохранение в Pinecone&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">md_files&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">glob&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">glob&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">path&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">join&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">OBSIDIAN_DIR&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;*.md&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">vectors&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">filepath&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">md_files&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">filename&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">path&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">basename&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">filepath&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">with&lt;/span> &lt;span class="nb">open&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">filepath&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;r&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">encoding&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;utf-8&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="n">f&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">content&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">f&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">read&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Обрабатывать, только если содержимое заметки не пустое&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">content&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">strip&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Embedding note: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">filename&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">embedding&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">get_embedding&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">content&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Формат Pinecone (id, vector, metadata)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">vectors&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">filename&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;values&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">embedding&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;metadata&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;text&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">content&lt;/span>&lt;span class="p">[:&lt;/span>&lt;span class="mi">500&lt;/span>&lt;span class="p">]}&lt;/span> &lt;span class="c1"># Часть текста для отображения в результатах поиска&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Загрузка батчами (upsert)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">vectors&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">index&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">upsert&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">vectors&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">vectors&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Successfully upserted &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="nb">len&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">vectors&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> notes.&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">search_similar_ideas&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">index&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">query_text&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">top_k&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;Поиск похожих заметок по запросу для генерации идей&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">query_embedding&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">get_embedding&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">query_text&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">results&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">index&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">query&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">vector&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">query_embedding&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">top_k&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">top_k&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">include_metadata&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">--- Search Results for: &amp;#39;&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">query_text&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#39; ---&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="k">match&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">results&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;matches&amp;#34;&lt;/span>&lt;span class="p">]:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Score: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">match&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;score&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="si">:&lt;/span>&lt;span class="s2">.4f&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> | Note: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">match&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;id&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Preview: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">match&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;metadata&amp;#39;&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="s1">&amp;#39;text&amp;#39;&lt;/span>&lt;span class="p">][:&lt;/span>&lt;span class="mi">100&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">...&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="vm">__name__&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;__main__&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">idx&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">init_pinecone&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># При первом запуске вызовите sync_notes_to_pinecone(idx) для построения БД&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">sync_notes_to_pinecone&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">idx&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Поиск для генерации идей для блога&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">search_similar_ideas&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">idx&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;Ускорение вывода машинного обучения в браузере с помощью WebAssembly&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Используя эту систему, на вопрос «Я хочу написать о WebAssembly, обсуждаемом на этой неделе на Hacker News, но писал ли я о чем-то подобном раньше?» ИИ мгновенно подберет семантически релевантные прошлые Permanent Notes. Это позволяет создавать глубокие статьи, максимально эффективно используя ваш накопленный багаж знаний.&lt;/p>
&lt;h2 id="6-кривая-забывания-эббингауза-и-интервальное-повторение-с-anki">6. Кривая забывания Эббингауза и интервальное повторение с Anki
&lt;/h2>&lt;p>Независимо от того, насколько отличные знания записаны в заметках, если эти знания не закрепились в голове автора, ему будет сложно свободно комбинировать различные концепции в процессе письма. Именно здесь на помощь приходит математическая модель механизма человеческой памяти — «кривая забывания Эббингауза».&lt;/p>
&lt;p>Кривая забывания аппроксимируется следующей формулой:&lt;/p>
$$ R = e^{-\frac{t}{S}} $$&lt;p>Где:&lt;/p>
&lt;ul>
&lt;li>$R$ — коэффициент удержания в памяти (Retrievability, в диапазоне от 0 до 1)&lt;/li>
&lt;li>$t$ — время, прошедшее с момента обучения&lt;/li>
&lt;li>$S$ — стабильность (Stability) или сила памяти&lt;/li>
&lt;/ul>
&lt;p>Сразу после изучения новой концепции параметр $S$ мал, и с течением времени $t$ значение $R$ резко падает (забывание). Однако, если провести повторение (Recall) в тот самый момент, когда вы почти забыли информацию, скорость последующего забывания замедляется ($S$ увеличивается), и знания переходят в долговременную память.&lt;/p>
&lt;p>Программное обеспечение «Anki» использует алгоритмы (такие как SuperMemo 2) для автоматического расчета этого оптимального времени повторения и предоставляет его в виде флеш-карточек.&lt;/p>
&lt;p>В качестве мощного подхода к созданию тем для технического блога можно выделить &lt;strong>преобразование содержимого Permanent Notes из Obsidian во флеш-карточки Anki&lt;/strong>.
Например, вы можете добавить в Anki фундаментальные технические вопросы, такие как: «Каковы 3 элемента CAP-теоремы?» или «Почему B-Tree индексы имеют производительность поиска O(log N)?», и сделать их повторение своей ежедневной рутиной. Когда знания индексируются в мозгу как долговременная память, во время принятия душа или прогулки информация на подсознательном уровне может соединиться, порождая озарение (эврика-момент): «О, я мог бы написать статью об алгоритмах консенсуса в распределенных системах».&lt;/p>
&lt;h2 id="7-комбинаторная-креативность-combinatorial-creativity">7. Комбинаторная креативность (Combinatorial Creativity)
&lt;/h2>&lt;p>С помощью описанного конвейера мы реализовали «разнообразный ввод информации», «систематизацию через Zettelkasten и AI-поиск» и «закрепление в долговременной памяти с помощью Anki». Последний шаг — это объединение этих элементов для генерации совершенно новых идей для технических статей через «комбинаторную креативность» (Combinatorial Creativity).&lt;/p>
&lt;p>Считается, что инновации и креативность возникают не из ничего, а из новых комбинаций существующих элементов. Широко известны слова Стива Джобса: «Креативность — это просто соединение вещей» (Creativity is just connecting things).&lt;/p>
&lt;p>В контексте технического блога можно представить себе следующую матрицу паттернов комбинаций:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>[Старая технология] × [Новая парадигма]&lt;/strong>: Например, «Антипаттерны проектирования современных микросервисов: уроки архитектуры COBOL».&lt;/li>
&lt;li>&lt;strong>[Фронтенд] × [Концепции бэкенда]&lt;/strong>: Например, «Объяснение алгоритма обновления Virtual DOM в React с точки зрения уровней изоляции транзакций в базах данных».&lt;/li>
&lt;li>&lt;strong>[Абстрактная математика/теория] × [Конкретная реализация]&lt;/strong>: Например, «Оптимизация планирования подов в Kubernetes через призму теории графов».&lt;/li>
&lt;/ol>
&lt;p>Чтобы намеренно генерировать такие комбинации, вы можете использовать ранее построенную систему семантического поиска Pinecone: извлеките случайные концепции A и B, и отправьте промпт ИИ (например, ChatGPT): «Предложи 5 вариантов заголовков и оглавлений для технического блога, объединяющих эти две концепции». Таким образом, вы можете бесконечно генерировать идеи с нестандартными углами зрения, о которых вы бы и не подумали сами.&lt;/p>
&lt;h2 id="8-архитектура-всей-системы">8. Архитектура всей системы
&lt;/h2>&lt;p>Ниже на Mermaid-блок-схеме представлена общая архитектура от «сбора информации до генерации идей», о которой шла речь выше, призванная предотвратить нехватку идей для технических статей.&lt;/p>
&lt;pre class="mermaid">
flowchart TD
A[&amp;#34;Hacker News / Lobsters API&amp;#34;] --&amp;gt;|Python-скрипт извлечения| B[&amp;#34;Необработанные данные трендов&amp;#34;]
C[&amp;#34;GitHub Actions (Cron)&amp;#34;] --&amp;gt;|Регулярное расписание| A
B --&amp;gt;|Конвертация в Markdown| D[&amp;#34;Daily Inputs (Fleeting Notes)&amp;#34;]
D --&amp;gt;|Ручное чтение и резюмирование| E[&amp;#34;Obsidian Zettelkasten&amp;#34;]
E --&amp;gt;|Преобразование в постоянные заметки| F[&amp;#34;Permanent Notes&amp;#34;]
F --&amp;gt;|Автоматическая синхронизация| G[&amp;#34;OpenAI Embeddings API&amp;#34;]
G --&amp;gt;|Преобразование в вектор| H[&amp;#34;Pinecone Vector Database&amp;#34;]
H --&amp;gt;|Семантический поиск| I[&amp;#34;Обнаружение и извлечение связанных знаний&amp;#34;]
F --&amp;gt;|Создание флеш-карточек| J[&amp;#34;Anki (Spaced Repetition)&amp;#34;]
J --&amp;gt;|Вдохновение из долговременной памяти| K[&amp;#34;Combinatorial Creativity&amp;#34;]
I --&amp;gt; K
K --&amp;gt;|Создание черновика и плана| L[&amp;#34;Blog Post Draft (Написание статьи)&amp;#34;]
&lt;/pre>
&lt;p>Отличительной чертой этой системы является то, что &lt;strong>«интеллектуальная работа, которая должна выполняться вручную (резюмирование, размышление, написание)», полностью отделена от «задач, которые следует делегировать машинам (сбор, поиск, планирование интервального повторения)»&lt;/strong>. Благодаря этому авторы могут сосредоточиться на самом важном, что добавляет ценность — на «размышлении» и «комбинировании».&lt;/p>
&lt;h2 id="9-модель-переходов-состояний-от-идеи-до-публикации">9. Модель переходов состояний: от идеи до публикации
&lt;/h2>&lt;p>Жизненный цикл идей, накопленных в Zettelkasten, вплоть до их окончательной публикации в качестве статьи блога, можно представить в виде следующей диаграммы переходов состояний. На каждом этапе используются подходящие инструменты и подходы.&lt;/p>
&lt;pre class="mermaid">
stateDiagram-v2
[*] --&amp;gt; Capture[&amp;#34;Idea Capture (Сбор информации)&amp;#34;]
Capture[&amp;#34;Idea Capture (Сбор информации)&amp;#34;] --&amp;gt; Fleeting[&amp;#34;Fleeting Notes (Временные заметки)&amp;#34;]
Fleeting[&amp;#34;Fleeting Notes (Временные заметки)&amp;#34;] --&amp;gt; Permanent[&amp;#34;Permanent Notes (Постоянные заметки)&amp;#34;]
Permanent[&amp;#34;Permanent Notes (Постоянные заметки)&amp;#34;] --&amp;gt; Brainstorming[&amp;#34;Brainstorming (AI семантический поиск)&amp;#34;]
Permanent[&amp;#34;Permanent Notes (Постоянные заметки)&amp;#34;] --&amp;gt; Memorization[&amp;#34;Memorization (Повторение в Anki)&amp;#34;]
Memorization[&amp;#34;Memorization (Повторение в Anki)&amp;#34;] --&amp;gt; Brainstorming[&amp;#34;Brainstorming (AI семантический поиск)&amp;#34;]
Brainstorming[&amp;#34;Brainstorming (AI семантический поиск)&amp;#34;] --&amp;gt; Outlining[&amp;#34;Outlining (Создание структуры/оглавления)&amp;#34;]
Outlining[&amp;#34;Outlining (Создание структуры/оглавления)&amp;#34;] --&amp;gt; Drafting[&amp;#34;Drafting (Написание черновика)&amp;#34;]
Drafting[&amp;#34;Drafting (Написание черновика)&amp;#34;] --&amp;gt; Review[&amp;#34;Review &amp;amp; Edit (Редактирование и проверка)&amp;#34;]
Review[&amp;#34;Review &amp;amp; Edit (Редактирование и проверка)&amp;#34;] --&amp;gt; Published[&amp;#34;Published (Публикация в блоге)&amp;#34;]
Published[&amp;#34;Published (Публикация в блоге)&amp;#34;] --&amp;gt; [*]
&lt;/pre>
&lt;p>Осознание этого рабочего процесса проясняет, «на какой стадии вы застряли прямо сейчас». Если нет идей, достаточно вернуться к фазе «Capture» или «Permanent» и проверить, нормально ли функционирует ваш конвейер инпута.&lt;/p>
&lt;h2 id="заключение-написание-статей--это-система">Заключение: написание статей — это «система»
&lt;/h2>&lt;p>«Нехватка идей для технического блога» вызвана не недостатком личных способностей или мотивации, а является &lt;strong>неизбежным результатом отсутствия системы циркуляции знаний&lt;/strong>.&lt;/p>
&lt;p>Как было показано в этой статье:&lt;/p>
&lt;ol>
&lt;li>Обеспечение качественного инпута с низким уровнем шума с помощью &lt;strong>API и автоматизации&lt;/strong>&lt;/li>
&lt;li>Создание сети знаний с помощью Zettelkasten в &lt;strong>Obsidian&lt;/strong>&lt;/li>
&lt;li>Семантический поиск по вашим собственным активам через &lt;strong>OpenAI и Pinecone&lt;/strong>&lt;/li>
&lt;li>Укрепление индексации в мозге с помощью &lt;strong>Anki&lt;/strong> и кривой забывания Эббингауза&lt;/li>
&lt;li>&lt;strong>Комбинаторная креативность&lt;/strong>, объединяющая существующие концепции&lt;/li>
&lt;/ol>
&lt;p>Построив комплексный конвейер, объединяющий все эти элементы, вы не только избежите нехватки идей для блога, но и создадите состояние, в котором чем больше вы пишете, тем больше новых идей воспроизводится самопроизвольно.&lt;/p>
&lt;p>Нет необходимости с самого начала строить все идеально. Начните с создания простого скрипта для обращения к API Hacker News и выработайте привычку записывать в Markdown заметки по интересным вам статьям. Я надеюсь, что ваш технический блог станет источником выдающихся идей нового поколения.&lt;/p></description></item><item><title>Стратегия переписывания старых технических статей с использованием Google Search Console</title><link>http://kenji.blog/ru/p/google-search-console-rewrite-strategy/</link><pubDate>Sat, 12 Sep 2026 12:00:00 +0900</pubDate><guid>http://kenji.blog/ru/p/google-search-console-rewrite-strategy/</guid><description>&lt;img src="http://kenji.blog/p/google-search-console-rewrite-strategy/img/eyecatch.jpg" alt="Featured image of post Стратегия переписывания старых технических статей с использованием Google Search Console" />&lt;h2 id="1-введение-важность-переписывания-и-подход-на-основе-данных-в-технических-блогах">1. Введение: Важность переписывания и подход на основе данных в технических блогах
&lt;/h2>&lt;p>При ведении технического блога или корпоративного медиа для разработчиков &amp;ldquo;переписывание прошлых статей&amp;rdquo; так же важно, а возможно, и более важно, чем постоянное написание новых. Особенно в сфере IT и технологий информация быстро устаревает, и нередко фрагменты кода или спецификации API, написанные несколько лет назад, теперь устарели (Deprecated). Однако, просто слепо обновляя старые статьи, невозможно максимизировать трафик (приток посетителей) из поисковых систем.&lt;/p>
&lt;p>Поэтому в этой статье будет рассказано о продвинутой стратегии, которая использует данные &lt;strong>Google Search Console (далее GSC)&lt;/strong> и &lt;strong>Google Analytics 4 (GA4)&lt;/strong>, а также подходы на основе данных и математических моделей для выявления технических статей, нуждающихся в переписывании, с целью значительного повышения позиций в поиске и показателя кликабельности (CTR).&lt;/p>
&lt;p>В частности, мы подробно рассмотрим методы, начиная от интеграции данных GSC и GA4 с помощью Python и BigQuery для выявления &amp;ldquo;статей упущенной выгоды&amp;rdquo; с низким CTR по отношению к количеству показов (impressions), до выявления недостающих ключевых слов в заголовках H2 и H3 с использованием анализа TF-IDF из области обработки естественного языка (NLP) для эффективного заполнения пробелов в контенте.&lt;/p>
&lt;hr>
&lt;h2 id="2-анализ-разрыва-между-ожидаемым-ctr-и-фактическим-ctr-введение-математической-модели">2. Анализ разрыва между ожидаемым CTR и фактическим CTR (Введение математической модели)
&lt;/h2>&lt;p>Одним из самых базовых показателей в SEO является &amp;ldquo;показатель кликабельности (CTR) относительно позиции в поиске&amp;rdquo;. Как правило, если позиция в поиске равна 1, CTR составляет около 25-30%, на 2-м месте — около 15%, после чего он резко падает. Эту зависимость между позицией и CTR можно смоделировать как распределение, подчиняющееся степенному закону (Power Law).&lt;/p>
&lt;p>Известно, что ожидаемый показатель кликабельности $CTR(r)$ для позиции $r$ аппроксимируется следующей формулой:&lt;/p>
$$
CTR(r) = a \cdot r^{-b}
$$&lt;p>Здесь $a$ — это ожидаемый CTR на 1-м месте (например, $0.30$ для 30%), а $b$ — параметр затухания (обычно от $1.0$ до $1.5$).&lt;/p>
&lt;p>Наиболее эффективный подход при выборе статей для переписывания — &lt;strong>найти статьи (ключевые слова), где &amp;ldquo;фактический CTR&amp;rdquo; значительно ниже &amp;ldquo;ожидаемого CTR&amp;rdquo;&lt;/strong>. Например, если позиция в поиске — 3-я (ожидаемый CTR около 10%), но фактический CTR составляет всего 2%, высока вероятность того, что поисковое намерение не совпадает с заголовком и описанием (description) или клики забирают факторы конкурентов, такие как расширенные сниппеты.&lt;/p>
&lt;p>Следующий график иллюстрирует разрыв между ожидаемым CTR и фактическим CTR в техническом блоге.&lt;/p>
&lt;pre class="mermaid">
xychart-beta
title Ожидаемый CTR против Фактического CTR по позициям
x-axis [&amp;#34;1&amp;#34;, &amp;#34;2&amp;#34;, &amp;#34;3&amp;#34;, &amp;#34;4&amp;#34;, &amp;#34;5&amp;#34;, &amp;#34;6&amp;#34;, &amp;#34;7&amp;#34;, &amp;#34;8&amp;#34;, &amp;#34;9&amp;#34;, &amp;#34;10&amp;#34;]
y-axis &amp;#34;CTR (%)&amp;#34; 0 --&amp;gt; 35
line [30.5, 15.2, 10.1, 7.5, 5.2, 4.1, 3.2, 2.5, 2.0, 1.5]
bar [32.1, 14.0, 8.5, 4.0, 5.0, 2.1, 1.5, 1.0, 1.2, 0.5]
&lt;/pre>
&lt;p>(* Линия графика показывает ожидаемый CTR, а гистограмма — фактический CTR. Можно заметить значительное отставание на 4-й и 8-й позициях.)&lt;/p>
&lt;hr>
&lt;h2 id="3-автоматическое-извлечение-данных-о-поисковой-эффективности-с-помощью-gsc-api-python">3. Автоматическое извлечение данных о поисковой эффективности с помощью GSC API (Python)
&lt;/h2>&lt;p>Хотя данные можно скачивать в формате CSV из веб-интерфейса GSC для анализа, для больших блогов или постоянного анализа лучше всего создать систему для автоматического извлечения данных на Python с помощью GSC API.&lt;/p>
&lt;p>Ниже приведен фрагмент кода на Python, использующий &lt;code>google-api-python-client&lt;/code> для получения данных о производительности по страницам и запросам (количество кликов, количество показов, CTR, средняя позиция) за определенный период.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">pandas&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="nn">pd&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">google.oauth2&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">service_account&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">googleapiclient.discovery&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">build&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">get_gsc_data&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">key_path&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">site_url&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">start_date&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">end_date&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Загрузка учетных данных и создание клиента API&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">credentials&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">service_account&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">Credentials&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">from_service_account_file&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">key_path&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">scopes&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;https://www.googleapis.com/auth/webmasters.readonly&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">service&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">build&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;searchconsole&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;v1&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">credentials&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">credentials&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Настройка полезной нагрузки запроса к API (указание страниц и запросов в качестве параметров)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">request&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;startDate&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">start_date&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;endDate&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">end_date&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;dimensions&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;page&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;query&amp;#39;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;rowLimit&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">25000&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Выполнение API-запроса&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">service&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">searchanalytics&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">query&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">siteUrl&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">site_url&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">body&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">request&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">execute&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Извлечение данных из ответа и преобразование в Pandas DataFrame&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">rows&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">response&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;rows&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">[])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">row&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">rows&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">keys&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">row&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;keys&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">data&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;page&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">keys&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;query&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">keys&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;clicks&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">row&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;clicks&amp;#39;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;impressions&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">row&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;impressions&amp;#39;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;ctr&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">row&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;ctr&amp;#39;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;position&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">row&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;position&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">pd&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">DataFrame&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Пример выполнения&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># df_gsc = get_gsc_data(&amp;#39;credentials.json&amp;#39;, &amp;#39;https://kenji.blog/&amp;#39;, &amp;#39;2026-08-01&amp;#39;, &amp;#39;2026-08-31&amp;#39;)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># print(df_gsc.head())&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>С помощью этого скрипта вы можете получить подробные данные, связывающие URL страниц и поисковые запросы, в виде DataFrame. Это позволяет получить полное представление о том, по каким ключевым словам показывается конкретная статья.&lt;/p>
&lt;hr>
&lt;h2 id="4-фильтрация-технических-ключевых-слов-с-помощью-регулярных-выражений-regex">4. Фильтрация технических ключевых слов с помощью регулярных выражений (Regex)
&lt;/h2>&lt;p>Одной из очень мощных функций при анализе технического блога является &lt;strong>фильтр по регулярным выражениям (Regex)&lt;/strong> в GSC.
Например, если вы пишете множество статей по темам от фронтенда до бэкенда и инфраструктуры, вы можете захотеть выделить только &amp;ldquo;статьи об ошибках и туториалы по Python и Pandas&amp;rdquo;, чтобы определить приоритеты для переписывания.&lt;/p>
&lt;p>Используя пользовательские фильтры по регулярным выражениям в GSC, вы можете сузить запросы по сложным условиям.&lt;/p>
&lt;p>&lt;strong>Примеры фильтрации технических ключевых слов:&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Исследование ошибок, связанных с Python: &lt;code>^(python|pandas|numpy|matplotlib).* (error|exception|bug|ошибка|не работает)&lt;/code>&lt;/li>
&lt;li>Построение инфраструктуры, связанной с AWS: &lt;code>(aws|amazon web services|ec2|s3|lambda).* (создание|настройка|туториал|tutorial|how to)&lt;/code>&lt;/li>
&lt;li>Обновление версий конкретных библиотек: &lt;code>(react|vue|angular) (v17|v18|v3) (migration|миграция|переход)&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>Если вы встраиваете это в запрос к GSC API, используйте &lt;code>dimensionFilterGroups&lt;/code> для применения условий регулярного выражения. Искусно используя эту фильтрацию, вы сможете точечно выделять высокоценные ключевые слова, направленные на решение проблем, которые разработчики &amp;ldquo;активно гуглят прямо сейчас&amp;rdquo;.&lt;/p>
&lt;hr>
&lt;h2 id="5-интеграция-данных-ga4-и-gsc-с-помощью-bigquerypandas">5. Интеграция данных GA4 и GSC с помощью BigQuery/Pandas
&lt;/h2>&lt;p>Одни только данные GSC показывают лишь &amp;ldquo;позицию в поиске и CTR&amp;rdquo;. Чтобы узнать, &amp;ldquo;насколько долго пользователи, попавшие на статью, фактически оставались на ней и достигли ли они конверсии (например, переход в репозиторий GitHub или подписка на рассылку)&amp;rdquo;, необходимо объединить (JOIN) их с данными &lt;strong>Google Analytics 4 (GA4)&lt;/strong>.&lt;/p>
&lt;p>Если вы храните экспортированные данные GA4 и данные массового экспорта GSC в BigQuery, вы можете использовать следующий SQL-запрос для их объединения и извлечения &amp;ldquo;статей с большим количеством показов и приемлемой позицией в поиске, но с высоким показателем отказов (bounce rate) или коротким временем вовлеченности&amp;rdquo;.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="line">&lt;span class="cl">&lt;span class="k">WITH&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">gsc_data&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">url&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">page_path&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">SUM&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">impressions&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">total_impressions&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">SUM&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">clicks&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">total_clicks&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">AVG&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sum_top_position&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">avg_position&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">`&lt;/span>&lt;span class="n">project&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">searchconsole&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">searchdata_url_impression&lt;/span>&lt;span class="o">`&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">data_date&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">BETWEEN&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;2026-08-01&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AND&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;2026-08-31&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">GROUP&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">BY&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">url&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">ga4_data&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">REGEXP_REPLACE&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">value&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">string_value&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">UNNEST&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">event_params&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">key&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;page_location&amp;#39;&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">r&lt;/span>&lt;span class="s1">&amp;#39;^https?://[^/]+&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">page_path&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">COUNT&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">DISTINCT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">user_pseudo_id&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">users&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">AVG&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">value&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">int_value&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">UNNEST&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">event_params&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">key&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;engagement_time_msec&amp;#39;&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">1000&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">avg_engagement_sec&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">`&lt;/span>&lt;span class="n">project&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">analytics_123456789&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">events_&lt;/span>&lt;span class="o">*`&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">event_name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;page_view&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">GROUP&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">BY&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">page_path&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">g&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">page_path&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">g&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">total_impressions&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">g&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">total_clicks&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">SAFE_DIVIDE&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">g&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">total_clicks&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">g&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">total_impressions&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ctr&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">g&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">avg_position&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">users&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">avg_engagement_sec&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">gsc_data&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">g&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">JOIN&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">ga4_data&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">ON&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">g&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">page_path&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">page_path&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">g&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">total_impressions&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">1000&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">AND&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">g&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">avg_position&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">BETWEEN&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AND&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">15&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">ORDER&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">BY&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">g&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">total_impressions&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DESC&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Используя эти результаты, можно классифицировать статьи для переписывания с помощью следующей матрицы:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>High Impression, Low CTR, High Engagement (Высокие показы, Низкий CTR, Высокая вовлеченность)&lt;/strong>:
Статьи, которыми читатели остаются довольны, если кликают на них в результатах поиска. &lt;strong>Исправление заголовков и мета-описаний&lt;/strong> должно быть главным приоритетом.&lt;/li>
&lt;li>&lt;strong>High CTR, Low Engagement (Высокий CTR, Низкая вовлеченность)&lt;/strong>:
Статьи, на которые кликают, но содержимое разочаровывает, и пользователи уходят. Требуется масштабное переписывание основного текста, например, &lt;strong>улучшение вступления, обновление кода до последних версий и повышение полноты информации (добавление H2/H3)&lt;/strong>.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="6-анализ-пробелов-в-контенте-с-использованием-nlp-и-tf-idf">6. Анализ пробелов в контенте с использованием NLP и TF-IDF
&lt;/h2>&lt;p>Как только статьи для переписывания определены, следующим шагом является анализ того, &amp;ldquo;какие конкретно заголовки (H2/H3) или ключевые слова следует добавить&amp;rdquo;. Вместо того чтобы полагаться на интуицию, мы будем использовать &lt;strong>TF-IDF (Term Frequency-Inverse Document Frequency) из области обработки естественного языка (NLP)&lt;/strong>.&lt;/p>
&lt;p>TF-IDF — это статистический показатель, используемый для оценки того, насколько важно слово в данном документе.&lt;/p>
$$
TF\text{-}IDF(t, d) = tf(t, d) \times \log\left(\frac{N}{df(t)}\right)
$$&lt;p>Где:&lt;/p>
&lt;ul>
&lt;li>$tf(t, d)$ — частота появления слова $t$ в документе $d$&lt;/li>
&lt;li>$N$ — общее количество документов&lt;/li>
&lt;li>$df(t)$ — количество документов, в которых встречается слово $t$&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>Подход:&lt;/strong>&lt;/p>
&lt;ol>
&lt;li>Получите текстовые данные 10 лучших статей (с сайтов конкурентов) по целевому ключевому слову с помощью веб-скрапинга и т. д.&lt;/li>
&lt;li>Подготовьте текстовые данные целевой статьи с вашего собственного сайта.&lt;/li>
&lt;li>Используя &lt;code>TfidfVectorizer&lt;/code> из библиотеки &lt;code>scikit-learn&lt;/code> на Python, извлеките ключевые слова (характерные слова), которые часто встречаются с высоким показателем в статьях конкурентов, но отсутствуют или имеют значительно более низкий показатель в статье на вашем собственном сайте.&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">sklearn.feature_extraction.text&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">TfidfVectorizer&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">pandas&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="nn">pd&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">numpy&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="nn">np&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># documents = [Текст вашего сайта, Текст статьи конкурента 1, Текст статьи конкурента 2, ...]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Предполагается список текстов, уже разделенных на слова с помощью морфологического анализатора (например, MeCab для японского языка)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">extract_missing_keywords&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">documents&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">vectorizer&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">TfidfVectorizer&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">max_df&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">0.9&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">min_df&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">tfidf_matrix&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">vectorizer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">fit_transform&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">documents&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">feature_names&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">vectorizer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get_feature_names_out&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Расчет среднего показателя TF-IDF для статей конкурентов (начиная с индекса 1)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">competitor_mean_tfidf&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">np&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">mean&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">tfidf_matrix&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">:]&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">toarray&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="n">axis&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Получение показателей TF-IDF для статьи на вашем сайте (индекс 0)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">my_article_tfidf&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">tfidf_matrix&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">toarray&lt;/span>&lt;span class="p">()[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Расчет разрыва для слов, важных у конкурентов, но отсутствующих (или редких) на вашем сайте&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">gap_scores&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">competitor_mean_tfidf&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">my_article_tfidf&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Извлечение верхних слов с наибольшим разрывом&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">df_gap&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">pd&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">DataFrame&lt;/span>&lt;span class="p">({&lt;/span>&lt;span class="s1">&amp;#39;keyword&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">feature_names&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;gap_score&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">gap_scores&lt;/span>&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">df_gap&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">df_gap&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">sort_values&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">by&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;gap_score&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ascending&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">False&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">df_gap&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">head&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">20&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Пример: missing_keywords = extract_missing_keywords(processed_docs)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># print(missing_keywords)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>С помощью этого анализа вы сможете количественно обнаружить &lt;strong>пробелы в темах (контентные пробелы)&lt;/strong>, такие как &amp;ldquo;на самом деле, топовые статьи упоминают «как развернуть в Docker-контейнере» и «построение CI/CD пайплайна», а в моей статье об этом ни слова&amp;rdquo;.&lt;/p>
&lt;p>Обнаруженные группы важных ключевых слов не следует просто разбрасывать по тексту; добавив их в виде осмысленных разделов с &lt;strong>заголовками H2 или H3 (теги Heading)&lt;/strong>, и написав подробные технические объяснения и фрагменты кода под этими заголовками, вы сможете значительно повысить оценку от Google.&lt;/p>
&lt;hr>
&lt;h2 id="7-конвейер-данных-и-цикл-непрерывного-улучшения">7. Конвейер данных и цикл непрерывного улучшения
&lt;/h2>&lt;p>Ключ к успеху в SEO — это не просто однократное выполнение процессов, описанных до сих пор, а их превращение в конвейер и постоянное выполнение. Ниже приведена общая архитектура и рабочий процесс (workflow) в виде блок-схемы Mermaid.&lt;/p>
&lt;pre class="mermaid">
flowchart TD
A[&amp;#34;Данные GSC API (Показы, Клики, Позиции)&amp;#34;] --&amp;gt; C[&amp;#34;BigQuery / Хранилище данных&amp;#34;]
B[&amp;#34;Экспорт данных GA4 (Просмотры страниц, Время вовлеченности)&amp;#34;] --&amp;gt; C
C --&amp;gt; D[&amp;#34;Python / Pandas Объединение и анализ данных&amp;#34;]
D --&amp;gt; E[&amp;#34;Определение статей с высокими показами / низким CTR&amp;#34;]
E --&amp;gt; F[&amp;#34;NLP Скрапинг конкурентов и извлечение ключевых слов TF-IDF&amp;#34;]
F --&amp;gt; G[&amp;#34;Оптимизация тегов H2/H3 и переписывание контента&amp;#34;]
G --&amp;gt; H[&amp;#34;Публикация обновленной статьи&amp;#34;]
H --&amp;gt; I[&amp;#34;Мониторинг изменений CTR (Ожидаемый против Фактического)&amp;#34;]
I --&amp;gt; |&amp;#34;Цикл обратной связи&amp;#34;| A
&lt;/pre>
&lt;p>Таким образом, систематизируя весь поток — от сбора данных из GSC и GA4 до выбора целей с помощью анализа, оптимизации контента с помощью NLP и мониторинга результатов — медиа-блог становится активом, который продолжает расти автоматически.&lt;/p>
&lt;hr>
&lt;h2 id="8-заключение-и-перспективы">8. Заключение и перспективы
&lt;/h2>&lt;p>Переписывание технических статей с использованием Google Search Console — это не просто исправление текста. Это продвинутая инженерия, которая использует данные и математические модели для предложения оптимальных решений &amp;ldquo;черному ящику&amp;rdquo; алгоритмов поисковых систем.&lt;/p>
&lt;p>Подведем итоги методов, описанных в этой статье:&lt;/p>
&lt;ol>
&lt;li>Вычислите разрыв между &lt;strong>ожидаемым CTR и фактическим CTR&lt;/strong> для выявления статей, на которые исправления окажут наибольшее влияние.&lt;/li>
&lt;li>Используйте &lt;strong>GSC API и Python&lt;/strong> для автоматического извлечения данных о производительности.&lt;/li>
&lt;li>Объедините эти данные с данными о вовлеченности из GA4 в &lt;strong>BigQuery&lt;/strong> и перепишите текст статей с высоким показателем отказов.&lt;/li>
&lt;li>Выявите пробелы в контенте с конкурентами посредством &lt;strong>анализа NLP с использованием TF-IDF&lt;/strong> и оптимизируйте заголовки (H2/H3).&lt;/li>
&lt;/ol>
&lt;p>Технологические тренды постоянно меняются. Чтобы точно отвечать на ошибки и проблемы, с которыми сейчас сталкиваются читатели, обязательно внедрите в свои повседневные операции стратегическое переписывание, опирающееся на данные.&lt;/p></description></item><item><title>Технический блог на Markdown: Настройки редактора, удваивающие скорость написания</title><link>http://kenji.blog/ru/p/markdown-editor-setup-for-tech-blogs/</link><pubDate>Sat, 12 Sep 2026 12:00:00 +0900</pubDate><guid>http://kenji.blog/ru/p/markdown-editor-setup-for-tech-blogs/</guid><description>&lt;img src="http://kenji.blog/p/markdown-editor-setup-for-tech-blogs/img/eyecatch.jpg" alt="Featured image of post Технический блог на Markdown: Настройки редактора, удваивающие скорость написания" />&lt;p>Для постоянного ведения технического блога оптимизация среды для написания является абсолютно необходимой. В этой статье мы подробно рассмотрим расширенные настройки редактора, которые радикально повысят скорость написания технических статей с использованием Markdown. Мы всесторонне обсудим максимальную кастомизацию Visual Studio Code (VS Code) и Neovim, использование сниппетов, внедрение инструмента проверки грамматики &lt;code>textlint&lt;/code>, автоматизацию через CI/CD-пайплайны, а также передовые методы написания с применением LLM, таких как GitHub Copilot.&lt;/p>
&lt;h2 id="1-математическая-модель-повышения-скорости-написания">1. Математическая модель повышения скорости написания
&lt;/h2>&lt;p>Давайте сначала смоделируем с помощью простой математической формулы, насколько оптимизация настроек редактора влияет на время написания. Пусть общее время ввода для написания одной статьи в блоге будет $T_{total}$.&lt;/p>
$$
T_{total} = T_{think} + T_{type} + T_{format} + T_{review}
$$&lt;p>Где $T_{think}$ — это время на обдумывание, $T_{type}$ — время на набор текста, $T_{format}$ — время на форматирование (например, Markdown), а $T_{review}$ — время на редактирование и вычитку.&lt;/p>
&lt;p>Сэкономленное время $T_{saved}$ за счет кастомизации редактора (например, внедрения сниппетов или настроек Linter&amp;rsquo;а) можно выразить следующим образом, используя количество появлений определенного паттерна $N$ (например, шорткодов Hugo или таблиц Markdown), время ручного ввода $t_{manual}$ и время при автоматизации через сниппеты $t_{snippet}$:&lt;/p>
$$
T_{saved} = \sum_{i=1}^{k} N_i \times (t_{manual, i} - t_{snippet, i}) + T_{review\_saved}
$$&lt;p>Кроме того, благодаря внедрению автоматических форматеров и инструментов Lint&amp;rsquo;инга, время, затрачиваемое на визуальную проверку человеком $T_{review}$, значительно сокращается. Максимизация этого значения $T_{saved}$ и является целью данной статьи.&lt;/p>
&lt;h2 id="2-ультимативные-настройки-visual-studio-code-vs-code">2. Ультимативные настройки Visual Studio Code (VS Code)
&lt;/h2>&lt;p>VS Code — один из самых популярных редакторов на сегодняшний день, обладающий мощной экосистемой расширений для написания в Markdown.&lt;/p>
&lt;h3 id="рекомендуемые-расширения">Рекомендуемые расширения
&lt;/h3>&lt;p>Для ускорения процесса написания мы настоятельно рекомендуем установить следующие расширения:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Markdown All in One&lt;/strong>: Предоставляет все основные функции, необходимые для написания в Markdown, такие как выделение жирным/курсивом с помощью горячих клавиш, автоматическое продолжение списков и автоматическая генерация оглавления (TOC).&lt;/li>
&lt;li>&lt;strong>markdownlint&lt;/strong>: В реальном времени предупреждает о синтаксических ошибках и нарушениях стиля в Markdown.&lt;/li>
&lt;li>&lt;strong>vscode-textlint&lt;/strong>: Применяет набор правил для японской технической документации, предотвращая несоответствия в терминологии и грамматические ошибки.&lt;/li>
&lt;/ol>
&lt;h3 id="настройка-пользовательских-сниппетов-для-hugo-markdownjson">Настройка пользовательских сниппетов для Hugo (&lt;code>markdown.json&lt;/code>)
&lt;/h3>&lt;p>Если для технического блога вы используете генераторы статических сайтов, такие как Hugo или Docusaurus, вам придется часто вводить Frontmatter или собственные шорткоды. Функция сниппетов в VS Code позволяет развернуть их в одно мгновение.&lt;/p>
&lt;p>Из палитры команд выберите &lt;code>Preferences: Configure User Snippets&lt;/code> и добавьте следующие настройки в &lt;code>markdown.json&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;Hugo Frontmatter&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;prefix&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;frontmatter&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;body&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;---&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;title: \&amp;#34;${1:Заголовок}\&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;slug: \&amp;#34;${2:slug-name}\&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;date: \&amp;#34;$CURRENT_YEAR-$CURRENT_MONTH-$CURRENT_DATE T$CURRENT_HOUR:$CURRENT_MINUTE:$CURRENT_SECOND+09:00\&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;image: \&amp;#34;img/eyecatch.jpg\&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;math: true&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;mermaid: true&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;categories: [\&amp;#34;${3:Category}\&amp;#34;]&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;tags: [\&amp;#34;${4:Tag1}\&amp;#34;, \&amp;#34;${5:Tag2}\&amp;#34;]&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;---&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;${0}&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;description&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Разворачивает YAML Frontmatter для Hugo&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;Hugo Figure Shortcode&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;prefix&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;hfig&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;body&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;{{&amp;lt; figure src=\&amp;#34;${1:image.jpg}\&amp;#34; title=\&amp;#34;${2:Заголовок изображения}\&amp;#34; &amp;gt;}}&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;description&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Шорткод Figure для Hugo&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;Markdown Table&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;prefix&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;mtable&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;body&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;| ${1:Header 1} | ${2:Header 2} | ${3:Header 3} |&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;| :--- | :---: | ---: |&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;| ${4:Row 1} | ${5:Data} | ${6:Data} |&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;| ${7:Row 2} | ${8:Data} | ${9:Data} |&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;$0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;description&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Создает Markdown таблицу на 3 колонки&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Благодаря этой настройке, достаточно ввести &lt;code>frontmatter&lt;/code> и нажать клавишу Tab, чтобы мгновенно развернуть YAML Frontmatter с текущим временем, что значительно увеличит начальную скорость написания.&lt;/p>
&lt;h3 id="поддержка-написания-с-использованием-github-copilot">Поддержка написания с использованием GitHub Copilot
&lt;/h3>&lt;p>Если в VS Code включен GitHub Copilot, автодополнение AI с учетом контекста работает и в Markdown. В частности, для технического блога AI может предвидеть и предлагать «структуру, которую следует объяснить далее» или «связанные блоки кода», что позволяет существенно сократить время на набор текста $T_{type}$.&lt;/p>
&lt;h2 id="3-экстремальная-кастомизация-в-neovim">3. Экстремальная кастомизация в Neovim
&lt;/h2>&lt;p>Графический интерфейс VS Code отличный, но для любителей терминалов и виммеров (Vim-пользователей) Neovim, позволяющий делать всё без отрыва рук от клавиатуры, является наиболее мощным выбором.&lt;/p>
&lt;h3 id="архитектура-lsp-в-neovim">Архитектура LSP в Neovim
&lt;/h3>&lt;p>Архитектура LSP (Language Server Protocol) и Linter в Neovim для среды Markdown выглядит следующим образом:&lt;/p>
&lt;pre class="mermaid">
classDiagram
class Neovim {
+&amp;#34;Текстовый буфер&amp;#34;
+&amp;#34;Привязки клавиш&amp;#34;
}
class nvim_lspconfig {
+&amp;#34;marksman (LSP для Markdown)&amp;#34;
}
class null_ls_or_none_ls {
+&amp;#34;markdownlint&amp;#34;
+&amp;#34;textlint&amp;#34;
}
class LuaSnip {
+&amp;#34;Динамические сниппеты&amp;#34;
}
class CMP {
+&amp;#34;Движок автодополнения&amp;#34;
}
Neovim --&amp;gt; nvim_lspconfig : &amp;#34;Обеспечивает семантические функции&amp;#34;
Neovim --&amp;gt; null_ls_or_none_ls : &amp;#34;Диагностика и форматирование&amp;#34;
Neovim --&amp;gt; CMP : &amp;#34;Запрос автодополнения&amp;#34;
CMP --&amp;gt; nvim_lspconfig : &amp;#34;Источник LSP&amp;#34;
CMP --&amp;gt; LuaSnip : &amp;#34;Источник сниппетов&amp;#34;
&lt;/pre>
&lt;h3 id="продвинутое-развертывание-сниппетов-с-помощью-luasnip">Продвинутое развертывание сниппетов с помощью LuaSnip
&lt;/h3>&lt;p>Гораздо мощнее JSON-сниппетов VS Code является плагин для Neovim — &lt;code>LuaSnip&lt;/code>. Используя логику языка Lua, можно динамически вычислять и разворачивать содержимое сниппетов.&lt;/p>
&lt;p>Ниже приведен пример настройки LuaSnip, которая динамически получает текущую дату и время и разворачивает Frontmatter для Hugo.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-lua" data-lang="lua">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">local&lt;/span> &lt;span class="n">ls&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">require&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;luasnip&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">local&lt;/span> &lt;span class="n">s&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ls.snippet&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">local&lt;/span> &lt;span class="n">t&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ls.text_node&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">local&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ls.insert_node&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">local&lt;/span> &lt;span class="n">f&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ls.function_node&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">-- Функция для получения текущего времени JST&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">local&lt;/span> &lt;span class="kr">function&lt;/span> &lt;span class="nf">get_current_date_jst&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">return&lt;/span> &lt;span class="n">os.date&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;!%Y-%m-%dT%H:%M:%S&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">..&lt;/span> &lt;span class="s2">&amp;#34;+09:00&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">end&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">ls.add_snippets&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;markdown&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">s&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;frontmatter&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">t&lt;/span>&lt;span class="p">({&lt;/span>&lt;span class="s2">&amp;#34;---&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;title: &lt;/span>&lt;span class="se">\&amp;#34;&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">}),&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;Title&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">t&lt;/span>&lt;span class="p">({&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\&amp;#34;&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;slug: &lt;/span>&lt;span class="se">\&amp;#34;&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">}),&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;slug-name&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">t&lt;/span>&lt;span class="p">({&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\&amp;#34;&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;date: &lt;/span>&lt;span class="se">\&amp;#34;&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">}),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">f&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kr">function&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="kr">return&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="n">get_current_date_jst&lt;/span>&lt;span class="p">()}&lt;/span> &lt;span class="kr">end&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{}),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">t&lt;/span>&lt;span class="p">({&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\&amp;#34;&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;image: &lt;/span>&lt;span class="se">\&amp;#34;&lt;/span>&lt;span class="s2">img/eyecatch.jpg&lt;/span>&lt;span class="se">\&amp;#34;&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;math: true&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;mermaid: true&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;categories: [&lt;/span>&lt;span class="se">\&amp;#34;&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">}),&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;Category&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">t&lt;/span>&lt;span class="p">({&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\&amp;#34;&lt;/span>&lt;span class="s2">]&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;tags: [&lt;/span>&lt;span class="se">\&amp;#34;&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">}),&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">4&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;Tag&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">t&lt;/span>&lt;span class="p">({&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\&amp;#34;&lt;/span>&lt;span class="s2">]&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;---&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">}),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">i&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">s&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;mtable&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">t&lt;/span>&lt;span class="p">({&lt;/span>&lt;span class="s2">&amp;#34;| &amp;#34;&lt;/span>&lt;span class="p">}),&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;Header 1&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">t&lt;/span>&lt;span class="p">({&lt;/span>&lt;span class="s2">&amp;#34; | &amp;#34;&lt;/span>&lt;span class="p">}),&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;Header 2&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">t&lt;/span>&lt;span class="p">({&lt;/span>&lt;span class="s2">&amp;#34; |&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;|---|---|&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;| &amp;#34;&lt;/span>&lt;span class="p">}),&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;Cell 1&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">t&lt;/span>&lt;span class="p">({&lt;/span>&lt;span class="s2">&amp;#34; | &amp;#34;&lt;/span>&lt;span class="p">}),&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">4&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;Cell 2&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">t&lt;/span>&lt;span class="p">({&lt;/span>&lt;span class="s2">&amp;#34; |&amp;#34;&lt;/span>&lt;span class="p">}),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Таким образом, опираясь на мощь языка программирования (Lua), можно создавать невероятные сниппеты, которые вставляют не просто фиксированные строки, но и возвращаемые значения функций, или динамически увеличивают/уменьшают количество колонок в таблице в зависимости от количества введенных символов.&lt;/p>
&lt;h2 id="4-статический-анализ-для-совмещения-качества-и-скорости-написания-textlint-и-регулярные-выражения">4. Статический анализ для совмещения качества и скорости написания (textlint и регулярные выражения)
&lt;/h2>&lt;p>Для обеспечения качества блога необходимо предотвращать опечатки и несоответствия в терминологии. Если делать это вручную, время $T_{review}$ катастрофически возрастает, поэтому мы внедряем статический анализ с помощью &lt;code>textlint&lt;/code>.&lt;/p>
&lt;h3 id="внедрение-textlint-и-наборы-правил-для-японского-языка">Внедрение textlint и наборы правил для японского языка
&lt;/h3>&lt;p>Установите textlint в среде Node.js.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">npm install -D textlint textlint-rule-preset-ja-technical-writing textlint-rule-prh textlint-filter-rule-comments
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Создайте &lt;code>.textlintrc.json&lt;/code> в корне проекта и настройте его следующим образом:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;filters&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;comments&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;rules&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;preset-ja-technical-writing&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;ja-no-mixed-period&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;periodMark&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;。&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;sentence-length&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;max&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">100&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;prh&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;rulePaths&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;./prh.yml&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Создайте &lt;code>prh.yml&lt;/code> и определите несоответствия в технических терминах. Например, можно стандартизировать написание «サーバー» (сервер) и «サーバ», «Javascript» и «JavaScript».&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">version&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">1&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">rules&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">expected&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;JavaScript&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">pattern&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Javascript&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">expected&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;サーバー&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">pattern&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;サーバ&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">expected&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;インターフェース&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">pattern&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;インターフェイス&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Благодаря этому при каждом наборе текста в редакторе будут выдаваться предупреждения о несоответствиях в реальном времени, что сведет время на вычитку почти к нулю.&lt;/p>
&lt;h3 id="пакетная-замена-и-структурные-паттерны-с-использованием-регулярных-выражений">Пакетная замена и структурные паттерны с использованием регулярных выражений
&lt;/h3>&lt;p>При переносе существующих статей в Markdown или копировании текста из внешних источников очень удобна пакетная замена с помощью регулярных выражений.&lt;/p>
&lt;p>Например, регулярное выражение для преобразования HTML-тегов &lt;code>&amp;lt;b&amp;gt;выделение&amp;lt;/b&amp;gt;&lt;/code> в Markdown &lt;code>**выделение**&lt;/code>:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Шаблон поиска&lt;/strong>: &lt;code>&amp;lt;b&amp;gt;(.*?)&amp;lt;/b&amp;gt;&lt;/code>&lt;/li>
&lt;li>&lt;strong>Шаблон замены&lt;/strong>: &lt;code>**$1**&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>Если нужно объединить лишние переносы строк в один:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Шаблон поиска&lt;/strong>: &lt;code>\n{3,}&lt;/code>&lt;/li>
&lt;li>&lt;strong>Шаблон замены&lt;/strong>: &lt;code>\n\n&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>Выполнив их с помощью функции поиска и замены VS Code (в режиме регулярных выражений) или команды &lt;code>%s&lt;/code> в Neovim (&lt;code>:%s/&amp;lt;b&amp;gt;\(.*?\)&amp;lt;\/b&amp;gt;/**\1**/g&lt;/code>), вы сможете мгновенно унифицировать формат.&lt;/p>
&lt;h3 id="автоматическая-проверка-с-помощью-cicd-пайплайна">Автоматическая проверка с помощью CI/CD пайплайна
&lt;/h3>&lt;p>Кроме того, используя GitHub Actions, мы построим CI пайплайн, в котором &lt;code>textlint&lt;/code> будет автоматически запускаться при отправке (push) статей в блог. Это позволит предотвратить деплой статей, нарушающих правила.&lt;/p>
&lt;pre class="mermaid">
flowchart TD
A[&amp;#34;Автор&amp;#34;] --&amp;gt;|Пуш изменений| B[&amp;#34;Репозиторий GitHub&amp;#34;]
B --&amp;gt; C{&amp;#34;Запуск GitHub Actions&amp;#34;}
C --&amp;gt; D[&amp;#34;Клонирование репозитория&amp;#34;]
D --&amp;gt; E[&amp;#34;Настройка Node.js&amp;#34;]
E --&amp;gt; F[&amp;#34;npm install&amp;#34;]
F --&amp;gt; G[&amp;#34;Запуск textlint&amp;#34;]
G --&amp;gt;|Успешно| H[&amp;#34;Сборка сайта Hugo&amp;#34;]
G --&amp;gt;|Ошибка| I[&amp;#34;Отчет об ошибке в PR/Коммит&amp;#34;]
H --&amp;gt; J[&amp;#34;Деплой на хостинг (Vercel/Netlify)&amp;#34;]
&lt;/pre>
&lt;h2 id="5-искусство-написания-markdown-в-эпоху-llm">5. Искусство написания Markdown в эпоху LLM
&lt;/h2>&lt;p>В современном написании технических блогов невозможно обойтись без использования LLM (Large Language Model). Использование встроенных в редактор инструментов ИИ еще больше удваивает скорость написания.&lt;/p>
&lt;h3 id="инженерия-промптов-внутри-редактора">Инженерия промптов внутри редактора
&lt;/h3>&lt;p>Используя GitHub Copilot Chat в VS Code или &lt;code>ChatGPT.nvim&lt;/code> и &lt;code>Copilot.vim&lt;/code> в Neovim, не покидая редактор, можно отправлять следующие промпты:&lt;/p>
&lt;blockquote>
&lt;p>«Создай структуру в виде иерархии Markdown для начинающих по следующим технологиям: Docker, Kubernetes, CI/CD»&lt;/p>
&lt;/blockquote>
&lt;p>После этого мгновенно генерируется разметка Markdown с заголовками и маркированными списками. Нам остается лишь добавить содержание на этот каркас.&lt;/p>
&lt;p>Кроме того, написание сложных диаграмм Mermaid или математических формул (LaTeX) также можно доверить ИИ, который сгенерирует точный синтаксис по вашим указаниям. Например, основы верстки формул и диаграмм в этой статье также были ускорены за счет совместного написания с LLM.&lt;/p>
&lt;h2 id="6-заключение">6. Заключение
&lt;/h2>&lt;p>В этой статье мы рассмотрели настройки редактора, которые удваивают скорость написания технических блогов на Markdown.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Осознание математической модели&lt;/strong>: Устранение повторяющихся задач для максимизации $T_{saved}$.&lt;/li>
&lt;li>&lt;strong>Использование VS Code&lt;/strong>: Сокращение ввода с помощью расширений и сниппетов в &lt;code>markdown.json&lt;/code>.&lt;/li>
&lt;li>&lt;strong>Экстремальная кастомизация Neovim&lt;/strong>: Динамические сниппеты с помощью &lt;code>LuaSnip&lt;/code> и полностью клавиатурное управление.&lt;/li>
&lt;li>&lt;strong>textlint и статический анализ&lt;/strong>: Интеграция CI/CD и локального Linter&amp;rsquo;а для сведения времени вычитки к нулю.&lt;/li>
&lt;li>&lt;strong>Интеграция LLM&lt;/strong>: Вывод структуры Markdown и кода для диаграмм непосредственно ИИ внутри редактора.&lt;/li>
&lt;/ol>
&lt;p>Внедрив эти настройки в свою рабочую среду, вы избавитесь от «рутины» при написании, а количество и качество ваших технических публикаций кардинально возрастут. Почему бы не начать с малого — например, с регистрации одного простого сниппета?&lt;/p></description></item><item><title>Что должны делать инженеры, чтобы увеличить ежемесячный трафик своего технического блога</title><link>http://kenji.blog/ru/p/tech-blog-growth-strategies-for-engineers/</link><pubDate>Sat, 12 Sep 2026 12:00:00 +0900</pubDate><guid>http://kenji.blog/ru/p/tech-blog-growth-strategies-for-engineers/</guid><description>&lt;img src="http://kenji.blog/p/tech-blog-growth-strategies-for-engineers/img/eyecatch.jpg" alt="Featured image of post Что должны делать инженеры, чтобы увеличить ежемесячный трафик своего технического блога" />&lt;h2 id="введение-гроуз-хакинг-технического-блога-доступный-только-инженерам">Введение: Гроуз-хакинг технического блога, доступный только инженерам
&lt;/h2>&lt;p>Многие инженеры-программисты создают технические блоги, но далеко не всем удается привлечь определенное количество трафика и поддерживать, а тем более увеличивать его в течение длительного времени. Написание качественных технических статей является обязательным условием, но эпоха «напиши хорошую статью, и её обязательно прочитают» уже прошла. Сегодня алгоритмы поисковых систем стали значительно сложнее, а скорость распространения информации в социальных сетях достигла небывалых высот.&lt;/p>
&lt;p>Однако у инженеров есть преимущество перед представителями других профессий. Оно заключается в способности «понимать системную архитектуру, комбинировать инструменты для автоматизации и программно анализировать данные». В этой статье мы не ограничимся лишь техниками написания текстов, а рассмотрим технический блог как «продукт» и подробно, с практическими примерами, разберем стратегии радикального увеличения ежемесячного трафика с помощью инженерных навыков.&lt;/p>
&lt;hr>
&lt;h2 id="1-seo-архитектура-технического-блога-для-инженеров">1. SEO-архитектура технического блога для инженеров
&lt;/h2>&lt;p>Базовая система блога (например, генератор статических сайтов) и структура HTML являются важнейшими факторами для правильной интерпретации контента поисковыми системами.&lt;/p>
&lt;h3 id="11-оптимизация-core-web-vitals">1.1 Оптимизация Core Web Vitals
&lt;/h3>&lt;p>Google использует Page Experience как фактор ранжирования, и в частности &lt;strong>Core Web Vitals (LCP, FID/INP, CLS)&lt;/strong> не стоит игнорировать даже в технических блогах.
В технических блогах часто используется множество блоков исходного кода, математических формул (MathJax / KaTeX) и иллюстраций. Они могут стать причиной задержки рендеринга страницы.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>LCP (Largest Contentful Paint)&lt;/strong>: Скорость загрузки основного контента на первом экране. Для главного изображения используйте форматы WebP или AVIF и предзагружайте его с атрибутом &lt;code>fetchpriority=&amp;quot;high&amp;quot;&lt;/code>. Огромные CSS и JS для подсветки синтаксиса должны загружаться асинхронно или только на тех страницах, где они действительно нужны.&lt;/li>
&lt;li>&lt;strong>CLS (Cumulative Layout Shift)&lt;/strong>: Смещение макета во время загрузки страницы. Резервирование области отображения для формул и изображений заранее с помощью CSS (например, &lt;code>aspect-ratio&lt;/code>) предотвратит скачки макета при последующей вставке DOM-элементов.&lt;/li>
&lt;li>&lt;strong>INP (Interaction to Next Paint)&lt;/strong>: Отзывчивость на действия пользователя. Тяжелый JavaScript (например, динамический полнотекстовый поиск на стороне клиента или выполнение громоздкого парсера Markdown) не должен выполняться в главном потоке — его необходимо выносить в Web Worker или генерировать как статический HTML на этапе сборки (SSG).&lt;/li>
&lt;/ul>
&lt;h3 id="12-внедрение-структурированных-данных-json-ld">1.2 Внедрение структурированных данных (JSON-LD)
&lt;/h3>&lt;p>Чтобы явно сообщить поисковым системам, что страница является «статьей», а также кто является её «автором», внедрите структурированные данные в формате JSON-LD. Использование таких схем, как &lt;code>TechArticle&lt;/code> и &lt;code>SoftwareSourceCode&lt;/code>, повышает шансы на появление в расширенных результатах (Rich Results) Google, что в свою очередь увеличивает CTR (кликабельность).&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">script&lt;/span> &lt;span class="na">type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;application/ld+json&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;@context&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;https://schema.org&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;@type&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;TechArticle&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;headline&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;Что должны делать инженеры, чтобы увеличить ежемесячный трафик своего технического блога&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;image&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;https://example.com/img/eyecatch.jpg&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;datePublished&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;2026-09-14T10:00:00+09:00&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;author&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;@type&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;Person&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;Kenji&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;url&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;https://example.com/about/&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;publisher&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;@type&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;Organization&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;Kenji&amp;#39;s Tech Blog&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;logo&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;@type&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;ImageObject&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;url&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;https://example.com/img/logo.png&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="13-семантический-html-и-оптимизация-структуры-документа">1.3 Семантический HTML и оптимизация структуры документа
&lt;/h3>&lt;p>Правильная вложенность заголовков (&lt;code>h1&lt;/code>–&lt;code>h6&lt;/code>) — это основы, но технический блог требует точного использования семантических тегов HTML5, таких как &lt;code>article&lt;/code>, &lt;code>section&lt;/code>, &lt;code>aside&lt;/code> и &lt;code>nav&lt;/code>. Кроме того, надлежащее использование тегов &lt;code>&amp;lt;code&amp;gt;&lt;/code> и &lt;code>&amp;lt;pre&amp;gt;&lt;/code> для исходного кода, &lt;code>&amp;lt;kbd&amp;gt;&lt;/code> для ввода с клавиатуры и &lt;code>&amp;lt;var&amp;gt;&lt;/code> для переменных позволяет предоставлять машиночитаемый HTML. Это также очень эффективная мера для индексации контента искусственным интеллектом (сбор обучающих данных для LLM или систем RAG).&lt;/p>
&lt;hr>
&lt;h2 id="2-психология-поискового-намерения-search-intent-и-стратегия-ключевых-слов">2. Психология поискового намерения (Search Intent) и стратегия ключевых слов
&lt;/h2>&lt;p>Чтобы максимизировать органический трафик из поисковых систем, необходимо точно понимать поисковое намерение — «почему пользователь ищет именно по этому ключевому слову». В технической сфере поисковые намерения можно условно разделить на две категории.&lt;/p>
&lt;h3 id="21-решение-ошибок-и-системное-обучение-и-обзоры">2.1 «Решение ошибок» и «Системное обучение и обзоры»
&lt;/h3>&lt;ol>
&lt;li>
&lt;p>&lt;strong>Намерение решить ошибку (Troubleshooting Intent)&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Примеры ключевых слов: &lt;code>Решение Docker &amp;quot;no space left on device&amp;quot;&lt;/code>, &lt;code>Причина Python IndexError list index out of range&lt;/code>&lt;/li>
&lt;li>Психология: Пользователь заблокирован ошибкой в процессе разработки и нуждается в быстром решении — команде или фрагменте кода.&lt;/li>
&lt;li>Стратегия: В самом начале статьи (на первом экране) представьте «заключение» (код или команду для решения проблемы). Пояснения, контекст и подробные механизмы размещайте ниже, в первую очередь удовлетворяя желание пользователя «быстро всё исправить». Это позволит снизить показатель отказов (Bounce Rate).&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Намерение системного обучения и обзоров (Learning &amp;amp; Review Intent)&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Примеры ключевых слов: &lt;code>Сравнение React vs Vue 2026&lt;/code>, &lt;code>Введение в асинхронное программирование Rust&lt;/code>, &lt;code>Проектирование сетевой архитектуры GCP&lt;/code>&lt;/li>
&lt;li>Психология: Пользователь хочет выбрать новый технологический стек или углубить понимание основ, и готов потратить время на чтение.&lt;/li>
&lt;li>Стратегия: Сделайте подробное оглавление (TOC), используйте множество иллюстраций и диаграмм архитектуры (например, Mermaid). Объективное сравнение преимуществ и недостатков, а также примеры использования в реальных рабочих задачах (Use Cases) помогут увеличить время пребывания на странице.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;h3 id="22-модель-экспоненциального-затухания-трафика-и-стратегия-длинного-хвоста-long-tail">2.2 Модель экспоненциального затухания трафика и стратегия длинного хвоста (Long Tail)
&lt;/h3>&lt;p>Трафик технических статей часто образует пик (всплеск) сразу после публикации за счет виральности в социальных сетях, после чего имеет тенденцию к экспоненциальному снижению. Этот трафик $V(t)$ можно аппроксимировать следующей математической моделью:&lt;/p>
$$ V(t) = V_0 e^{-\lambda t} + C $$&lt;p>Где:&lt;/p>
&lt;ul>
&lt;li>$V(t)$: Объем трафика в момент времени $t$&lt;/li>
&lt;li>$V_0$: Величина начального пика трафика, вызванного соцсетями и т.д. сразу после публикации&lt;/li>
&lt;li>$\lambda$: Константа затухания, связанная с устареванием контента и забыванием в соцсетях (зависит от скорости изменения технологических трендов)&lt;/li>
&lt;li>$C$: Стабильный органический поисковый трафик из поисковых систем (базовый трафик)&lt;/li>
&lt;/ul>
&lt;p>Ключ к долгосрочному росту трафика заключается не в погоне за временными всплесками ($V_0$), а в том, &lt;strong>как максимально увеличить константу $C$ (постоянный приток из поисковиков)&lt;/strong>. Охватывая большое количество «низкочастотных запросов» (Long Tail Keywords), таких как специфические редкие ошибки или способы интеграции определенных инструментов, где мало конкуренции, даже при небольшом объеме поиска, вы можете вырастить общую сумму $C$ до огромных размеров.&lt;/p>
&lt;hr>
&lt;h2 id="3-анализ-контента-на-основе-данных-с-использованием-google-search-console-api">3. Анализ контента на основе данных с использованием Google Search Console API
&lt;/h2>&lt;p>Чтобы построить стабильную базу трафика $C$, необходимо использовать данные Google Search Console (GSC) для объективного анализа того, «как вас оценивает Google». Однако ручная работа в веб-интерфейсе GSC имеет свои ограничения. Если вы инженер, автоматизируйте анализ с помощью GSC API и Python.&lt;/p>
&lt;h3 id="31-подход-к-автоматизации-с-помощью-gsc-api-и-python">3.1 Подход к автоматизации с помощью GSC API и Python
&lt;/h3>&lt;p>Создайте скрипт, который будет автоматически обнаруживать «статьи с упущенным потенциалом» — например, статьи, позиции которых со временем падают (Decaying Content), или статьи с высоким количеством показов (Impressions), но аномально низким показателем кликабельности (CTR).
Для этого используются &lt;code>google-api-python-client&lt;/code> и &lt;code>pandas&lt;/code>.&lt;/p>
&lt;h3 id="32-реализация-на-python-автоматическое-извлечение-контента-со-снижающимся-ctr">3.2 Реализация на Python: Автоматическое извлечение контента со снижающимся CTR
&lt;/h3>&lt;p>Ниже приведен пример скрипта, который получает данные об эффективности поиска за последние 30 дней через API и извлекает «ключевые слова и URL-адреса статей, где есть большой потенциал для улучшения заголовков и описаний» — с количеством показов более 1000 и CTR менее 2%.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;span class="lnt">59
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">pandas&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="nn">pd&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">google.oauth2&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">service_account&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">googleapiclient.discovery&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">build&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">datetime&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 1. Аутентификация и инициализация сервиса API&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">KEY_FILE_LOCATION&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;path/to/your-service-account-key.json&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">SCOPES&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;https://www.googleapis.com/auth/webmasters.readonly&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">SITE_URL&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;https://your-tech-blog.com/&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">credentials&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">service_account&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">Credentials&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">from_service_account_file&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">KEY_FILE_LOCATION&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">scopes&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">SCOPES&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">webmasters_service&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">build&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;searchconsole&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;v1&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">credentials&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">credentials&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 2. Вычисление периода запроса (последние 30 дней)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">today&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">datetime&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">date&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">today&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">end_date&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">today&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">datetime&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">timedelta&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">days&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">strftime&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;%Y-%m-&lt;/span>&lt;span class="si">%d&lt;/span>&lt;span class="s1">&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">start_date&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">today&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">datetime&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">timedelta&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">days&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">32&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">strftime&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;%Y-%m-&lt;/span>&lt;span class="si">%d&lt;/span>&lt;span class="s1">&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 3. Выполнение запроса к API&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">request&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;startDate&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">start_date&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;endDate&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">end_date&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;dimensions&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;query&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;page&amp;#39;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;rowLimit&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">5000&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">webmasters_service&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">searchanalytics&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">query&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">siteUrl&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">SITE_URL&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">body&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">request&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">execute&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 4. Обработка данных и фильтрация с использованием Pandas DataFrame&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="s1">&amp;#39;rows&amp;#39;&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">response&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">rows&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">response&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;rows&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">row&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">rows&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">data&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;Query&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">row&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;keys&amp;#39;&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;URL&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">row&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;keys&amp;#39;&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;Clicks&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">row&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;clicks&amp;#39;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;Impressions&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">row&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;impressions&amp;#39;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;CTR&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">row&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;ctr&amp;#39;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;Position&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">row&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;position&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">df&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">pd&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">DataFrame&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Условия фильтрации: Показов 1000 и более И CTR менее 2%&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">target_df&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">df&lt;/span>&lt;span class="p">[(&lt;/span>&lt;span class="n">df&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;Impressions&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">&amp;gt;=&lt;/span> &lt;span class="mi">1000&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">&amp;amp;&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">df&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;CTR&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="mf">0.02&lt;/span>&lt;span class="p">)]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Сортировка по возрастанию позиции (приоритет тем, которые высоко ранжируются, но по которым не кликают)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">target_df&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">target_df&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">sort_values&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">by&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;Position&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ascending&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;【Рекомендуемый список для улучшения заголовков/мета-описаний】&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">target_df&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">head&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">10&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Экспорт в CSV при необходимости&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># target_df.to_csv(&amp;#39;improve_candidates.csv&amp;#39;, index=False)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">else&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Данные не найдены.&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Запуская этот скрипт по расписанию через cron или GitHub Actions, вы сможете всегда на основе данных принимать решения о том, «заголовок какой статьи следует переписать». Важно опираться не на интуицию, а на непрерывное улучшение контента на основе данных (Continuous Content Improvement, аналог CI/CD).&lt;/p>
&lt;hr>
&lt;h2 id="4-управление-жизненным-циклом-статей-и-стратегия-переписывания-rewrite">4. Управление жизненным циклом статей и стратегия переписывания (Rewrite)
&lt;/h2>&lt;p>Создание технической статьи не заканчивается её публикацией. С развитием технологий (обновления версий фреймворков, устаревание API и т.д.) контент быстро теряет актуальность. Продолжение предоставления устаревшей информации не только вредит репутации блога, но и негативно сказывается на SEO-оценке.&lt;/p>
&lt;h3 id="41-управление-жизненным-циклом-контента-диаграмма-ганта">4.1 Управление жизненным циклом контента (Диаграмма Ганта)
&lt;/h3>&lt;p>Идеальный жизненный цикл управления контентом показан на диаграмме Ганта Mermaid.&lt;/p>
&lt;pre class="mermaid">
gantt
title Управление жизненным циклом контента на основе данных
dateFormat YYYY-MM-DD
axisFormat %m/%d
section &amp;#34;Фаза 1: Планирование и написание&amp;#34;
&amp;#34;Анализ ключевых слов и трендов&amp;#34; :a1, 2026-09-01, 3d
&amp;#34;Черновик и проверка кода&amp;#34; :a2, after a1, 5d
&amp;#34;Редактура и корректура&amp;#34; :a3, after a2, 2d
section &amp;#34;Фаза 2: Публикация и продвижение&amp;#34;
&amp;#34;Развертывание через CI/CD пайплайн&amp;#34; :p1, 2026-09-11, 1d
&amp;#34;Автоматическая публикация в соцсетях (X, LinkedIn, RSS)&amp;#34; :p2, 2026-09-11, 1d
&amp;#34;Распространение в сервисах закладок (Hatena и др.)&amp;#34; :p3, after p2, 3d
section &amp;#34;Фаза 3: Наблюдение и анализ&amp;#34;
&amp;#34;Период накопления данных GSC&amp;#34; :m1, 2026-09-14, 28d
&amp;#34;Оценка эффективности с помощью Python API&amp;#34;:m2, after m1, 2d
section &amp;#34;Фаза 4: Улучшение (Переписывание)&amp;#34;
&amp;#34;Исправление заголовков статей со снизившимся CTR&amp;#34; :r1, after m2, 3d
&amp;#34;Обновление кода до последней версии&amp;#34;:r2, after r1, 4d
&lt;/pre>
&lt;p>Таким образом, отношение к созданию статей как к проекту по разработке программного обеспечения и включение фазы эксплуатации и обслуживания (переписывания) после релиза в план — это секрет поддержания и увеличения трафика.&lt;/p>
&lt;h3 id="42-математическая-модель-рентабельности-инвестиций-roi-при-создании-контента">4.2 Математическая модель рентабельности инвестиций (ROI) при создании контента
&lt;/h3>&lt;p>Поскольку инженеры тратят свое драгоценное время на написание статей, им следует осознавать рентабельность своих инвестиций (ROI).
ROI для блога можно сформулировать следующим образом:&lt;/p>
$$ ROI = \frac{\sum_{t=1}^{T} \left( Rev_{ad}(t) + Val_{brand}(t) + Val_{skill}(t) \right) - Cost_{time}}{\text{Cost}_{time}} \times 100 \ (\%) $$&lt;ul>
&lt;li>$T$: Срок эффективной жизни статьи (период до её устаревания)&lt;/li>
&lt;li>$Rev_{ad}(t)$: Прямой доход от рекламы, партнерских программ (affiliate) и спонсорства&lt;/li>
&lt;li>$Val_{brand}(t)$: Денежный эквивалент положительного влияния на карьеру за счет демонстрации технических навыков (увеличение суммы оффера при смене работы, приглашения выступить с докладом и т.д.)&lt;/li>
&lt;li>$Val_{skill}(t)$: Ценность повышения собственных навыков за счет обучения и исследований, проведенных для написания статьи&lt;/li>
&lt;li>$Cost_{time}$: Время, затраченное на написание статьи, создание иллюстраций и проверку кода (в пересчете на собственную почасовую ставку)&lt;/li>
&lt;/ul>
&lt;p>Прекрасная особенность технических блогов заключается в том, что даже если $Rev_{ad}$ невелик, $Val_{brand}$ и $Val_{skill}$ имеют тенденцию быть чрезвычайно большими. В частности, качественные технические объяснения становятся вашим портфолио и демонстрируют огромную силу при поиске работы или получении дополнительных заказов (подработки).&lt;/p>
&lt;hr>
&lt;h2 id="5-дистрибуция-за-счет-интеграции-github-actions-и-внешних-инструментов-автоматизации">5. Дистрибуция за счет интеграции GitHub Actions и внешних инструментов автоматизации
&lt;/h2>&lt;p>После создания контента возникает проблема: как наиболее эффективно доставить его целевой аудитории (дистрибуция). Публикация ссылок в каждой социальной сети вручную каждый раз неэффективна и не свойственна инженерам.&lt;/p>
&lt;h3 id="51-архитектура-автоматизации-шаринга-в-социальных-сетях">5.1 Архитектура автоматизации шаринга в социальных сетях
&lt;/h3>&lt;p>Мы построим архитектуру, которая полностью автоматизирует процесс от момента слияния (merge) Markdown-файла в ветку main репозитория GitHub до сборки, развертывания и оповещения на множестве платформ.&lt;/p>
&lt;pre class="mermaid">
flowchart TD
A[&amp;#34;Разработчик (Git Push)&amp;#34;] --&amp;gt; B[&amp;#34;Репозиторий GitHub&amp;#34;]
B --&amp;gt;|Webhook| C[&amp;#34;GitHub Actions (CI/CD)&amp;#34;]
C --&amp;gt;|Сборка (Build)| D[&amp;#34;Генератор статических сайтов (Hugo/Gatsby)&amp;#34;]
D --&amp;gt;|Развертывание (Deploy)| E[&amp;#34;Хостинг (Vercel / Cloudflare Pages)&amp;#34;]
D --&amp;gt;|Генерация| F[&amp;#34;RSS-канал (index.xml)&amp;#34;]
F --&amp;gt;|Опрос (Polled by)| G[&amp;#34;Zapier / IFTTT / Make&amp;#34;]
G --&amp;gt;|Вызов API| H[&amp;#34;Автопубликация в X (Twitter)&amp;#34;]
G --&amp;gt;|Вызов API| I[&amp;#34;Публикация статьи в LinkedIn&amp;#34;]
G --&amp;gt;|Вызов API| J[&amp;#34;Webhook сообщества Discord / Slack&amp;#34;]
C --&amp;gt;|Скрипт Actions| K[&amp;#34;Кросс-постинг API в Qiita / Zenn&amp;#34;]
&lt;/pre>
&lt;h3 id="52-ключевые-моменты-создания-пайплайна-автоматизации">5.2 Ключевые моменты создания пайплайна автоматизации
&lt;/h3>&lt;ol>
&lt;li>
&lt;p>&lt;strong>Сборка и развертывание с помощью GitHub Actions&lt;/strong>
Если вы используете генератор статических сайтов, используйте GitHub Actions для автоматизации генерации HTML и развертывания на хостинге (Vercel, Netlify, Cloudflare Pages и т.д.). При этом, в качестве меры для Core Web Vitals, упомянутой ранее, эффективно внедрить в пайплайн сборки процесс оптимизации изображений (например, автоматическую конвертацию в WebP).&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Интеграция с социальными сетями на основе RSS-триггеров с использованием Zapier/IFTTT&lt;/strong>
Генератор сайтов при сборке создает актуальный RSS-канал (XML). Загрузив его в iPaaS, такие как Zapier или Make (ранее Integromat), можно настроить рабочий процесс (workflow): «Когда в RSS добавляется новый элемент, публиковать заголовок и URL в X (Twitter) и LinkedIn». Таким образом, уведомление подписчиков происходит автоматически в момент публикации статьи.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Кросс-постинг в Qiita/Zenn (использование тега Canonical)&lt;/strong>
Пока сила домена вашего собственного или корпоративного блога невелика, можно использовать привлекающую силу технических платформ, таких как Qiita или Zenn. Однако простой копипаст несет риск получения SEO-санкций за дублированный контент.
Эту проблему можно решить, установив тег &lt;strong>Canonical&lt;/strong> в метаданных статьи на Qiita или Zenn, указав URL оригинальной статьи в вашем блоге. Написав скрипт, который вызывает API различных платформ из GitHub Actions и автоматически генерирует статьи из Markdown, вы можете полностью автоматизировать мульти-канальную дистрибуцию.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="заключение-запуск-цикла-непрерывного-улучшения">Заключение: Запуск цикла непрерывного улучшения
&lt;/h2>&lt;p>Чтобы радикально увеличить ежемесячный трафик в техническом блоге, помимо самого процесса «написания», необходимы инженерные подходы, подобные тем, что представлены в этой статье.&lt;/p>
&lt;ol>
&lt;li>Построение надежной HTML- и сайт-архитектуры с учетом SEO&lt;/li>
&lt;li>Проектирование статей с пониманием поисковых намерений пользователей (решение ошибок против системного обучения)&lt;/li>
&lt;li>Анализ данных с использованием Google Search Console API и Python&lt;/li>
&lt;li>Управление жизненным циклом контента и переписывание с учетом ROI&lt;/li>
&lt;li>Полная автоматизация дистрибуции через интеграцию с CI/CD и Zapier&lt;/li>
&lt;/ol>
&lt;p>Если вы сможете собрать это воедино как систему, ваш технический блог станет мощнейшим активом (Asset), который будет сильно продвигать вашу карьеру. Инженеры, которые страдают от стагнации трафика, пожалуйста, начните применять «гроуз-хакинг блога» уже сегодня. Навыки программирования и проектирования архитектуры, отточенные в процессе разработки, должны стать вашим величайшим оружием и в управлении блогом.&lt;/p></description></item></channel></rss>