Интеграция Google PageSpeed Insights API для мониторинга скорости сайта

Наша компания занимается разработкой, поддержкой и обслуживанием сайтов любой сложности. От простых одностраничных сайтов до масштабных кластерных систем построенных на микро сервисах. Опыт разработчиков подтвержден сертификатами от вендоров.

Разработка и обслуживание любых видов сайтов:

Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Интеграция Google PageSpeed Insights API для мониторинга скорости сайта
Средняя
от 1 рабочего дня до 3 рабочих дней
Часто задаваемые вопросы

Наши компетенции:

Этапы разработки

Последние работы

  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    874
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    851

Интеграция Google PageSpeed Insights API для мониторинга скорости сайта

PageSpeed Insights (PSI) возвращает два типа данных: лабораторные (Lighthouse, симулированное окружение) и полевые (Chrome UX Report, реальные пользователи). Оба важны, но по-разному. Лабораторные показывают состояние прямо сейчас — после деплоя, после добавления нового скрипта. Полевые — реальный опыт пользователей за последние 28 дней. Мониторинг нужен для обоих.

Получение ключа API и базовый запрос

PSI API бесплатный. Ключ создаётся в Google Cloud Console → APIs & Services → Credentials. Без ключа лимит — 400 запросов в день, с ключом — 25 000.

import requests
from typing import Literal

PSI_API_URL = 'https://www.googleapis.com/pagespeedonline/v5/runPagespeed'

def fetch_psi(
    url: str,
    api_key: str,
    strategy: Literal['mobile', 'desktop'] = 'mobile',
) -> dict:
    params = {
        'url': url,
        'key': api_key,
        'strategy': strategy,
        'category': ['performance', 'seo', 'accessibility', 'best-practices'],
    }
    resp = requests.get(PSI_API_URL, params=params, timeout=60)
    resp.raise_for_status()
    return resp.json()

Извлечение Core Web Vitals

Структура ответа PSI многоуровневая. Полевые данные (CrUX) находятся в loadingExperience, лабораторные — в lighthouseResult.audits.

def extract_field_data(psi_response: dict) -> dict:
    exp = psi_response.get('loadingExperience', {})
    metrics = exp.get('metrics', {})

    def metric(key):
        m = metrics.get(key, {})
        return {
            'percentile': m.get('percentile'),
            'category': m.get('category'),  # FAST / AVERAGE / SLOW
        }

    return {
        'overall_category': exp.get('overall_category'),
        'lcp': metric('LARGEST_CONTENTFUL_PAINT_MS'),
        'fid': metric('FIRST_INPUT_DELAY_MS'),
        'cls': metric('CUMULATIVE_LAYOUT_SHIFT_SCORE'),
        'fcp': metric('FIRST_CONTENTFUL_PAINT_MS'),
        'inp': metric('INTERACTION_TO_NEXT_PAINT'),
        'ttfb': metric('EXPERIMENTAL_TIME_TO_FIRST_BYTE'),
    }

def extract_lab_data(psi_response: dict) -> dict:
    audits = psi_response.get('lighthouseResult', {}).get('audits', {})
    categories = psi_response.get('lighthouseResult', {}).get('categories', {})

    def audit_val(key, field='numericValue'):
        return audits.get(key, {}).get(field)

    return {
        'performance_score': categories.get('performance', {}).get('score'),
        'lcp_ms': audit_val('largest-contentful-paint'),
        'fcp_ms': audit_val('first-contentful-paint'),
        'tbt_ms': audit_val('total-blocking-time'),
        'cls': audit_val('cumulative-layout-shift'),
        'speed_index': audit_val('speed-index'),
        'tti_ms': audit_val('interactive'),
        'server_response_time_ms': audit_val('server-response-time'),
    }

Сохранение результатов

CREATE TABLE psi_results (
    id SERIAL PRIMARY KEY,
    url TEXT NOT NULL,
    strategy VARCHAR(10) NOT NULL,
    measured_at TIMESTAMP DEFAULT NOW(),

    -- Полевые данные (CrUX)
    field_overall_category TEXT,
    field_lcp_ms INTEGER,
    field_lcp_category TEXT,
    field_cls NUMERIC(6,4),
    field_cls_category TEXT,
    field_inp_ms INTEGER,
    field_inp_category TEXT,
    field_fcp_ms INTEGER,
    field_ttfb_ms INTEGER,

    -- Лабораторные данные (Lighthouse)
    lab_performance_score NUMERIC(4,2),
    lab_lcp_ms INTEGER,
    lab_fcp_ms INTEGER,
    lab_tbt_ms INTEGER,
    lab_cls NUMERIC(6,4),
    lab_tti_ms INTEGER,
    lab_speed_index INTEGER,
    lab_server_response_ms INTEGER
);

CREATE INDEX idx_psi_url_time ON psi_results(url, measured_at DESC);

Мониторинг списка страниц

Для сайта с несколькими приоритетными страницами (главная, топ-10 посадочных, корзина, карточка товара) запуск по расписанию:

import time
from datetime import datetime

PAGES_TO_MONITOR = [
    'https://example.com/',
    'https://example.com/catalog/',
    'https://example.com/product/bestseller/',
    'https://example.com/checkout/',
]

def run_monitoring(api_key: str, db_conn):
    results = []
    for url in PAGES_TO_MONITOR:
        for strategy in ('mobile', 'desktop'):
            try:
                data = fetch_psi(url, api_key, strategy)
                field = extract_field_data(data)
                lab = extract_lab_data(data)
                results.append({
                    'url': url,
                    'strategy': strategy,
                    'measured_at': datetime.utcnow(),
                    'field': field,
                    'lab': lab,
                })
                time.sleep(2)  # избегаем rate limit
            except Exception as e:
                print(f'Error for {url} ({strategy}): {e}')
    return results

Алерты при деградации

Отслеживаем падение Lighthouse score и переход CWV из «хорошо» в «требует улучшений»:

THRESHOLDS = {
    'lab_performance_score': 0.7,   # ниже 70 — алерт
    'lab_lcp_ms': 4000,             # > 4s
    'lab_tbt_ms': 600,              # > 600ms
    'field_lcp_category': 'SLOW',   # полевые данные стали медленными
}

def check_alerts(current: dict, thresholds: dict) -> list[str]:
    alerts = []
    lab = current.get('lab', {})
    field = current.get('field', {})

    if lab.get('performance_score', 1) < thresholds['lab_performance_score']:
        score = round(lab['performance_score'] * 100)
        alerts.append(f"Performance score упал до {score} (порог: {int(thresholds['lab_performance_score']*100)})")

    if lab.get('lab_lcp_ms', 0) > thresholds['lab_lcp_ms']:
        alerts.append(f"LCP = {lab['lab_lcp_ms']}ms (порог: {thresholds['lab_lcp_ms']}ms)")

    if field.get('lcp', {}).get('category') == 'SLOW':
        alerts.append(f"Полевой LCP стал SLOW (реальные пользователи)")

    return alerts

Сравнение с предыдущим замером

def compare_results(current: dict, previous: dict) -> dict:
    changes = {}
    lab_keys = ['lab_performance_score', 'lab_lcp_ms', 'lab_tbt_ms', 'lab_cls']
    for key in lab_keys:
        curr_val = current.get(key)
        prev_val = previous.get(key)
        if curr_val is not None and prev_val is not None:
            delta = curr_val - prev_val
            changes[key] = {
                'previous': prev_val,
                'current': curr_val,
                'delta': round(delta, 4),
                'direction': 'worse' if (
                    (key == 'lab_performance_score' and delta < 0) or
                    (key != 'lab_performance_score' and delta > 0)
                ) else 'better',
            }
    return changes

Важные ограничения

PSI API запускает Lighthouse в облаке Google, а не у вас. Результаты варьируются между запусками на 5–15% — это нормально. Для надёжных показателей лучше запускать 3 измерения и брать медиану. Также PSI не поддерживает авторизованные страницы — для мониторинга личного кабинета, корзины после логина нужен локальный Lighthouse через Node.js.

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

Запуск через cron

# Crontab: каждый день в 6:00
0 6 * * * /usr/bin/python3 /opt/monitoring/psi_monitor.py >> /var/log/psi_monitor.log 2>&1

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

Сроки

Скрипт сбора + хранение в PostgreSQL + алерты на email/Telegram — 1–2 рабочих дня. С Grafana-дашбордом, сравнением версий до/после деплоя, интеграцией в CI/CD pipeline — 3–4 дня.