Перейти к содержанию
  • Лента
  • Категории
  • Последние
  • Метки
  • Популярные
  • Пользователи
  • Группы
Свернуть
exlends
Категории
  1. Главная
  2. Категории
  3. Языки программирования
  4. TypeScript
  5. Парсинг и SEO-анализ с Playwright: автоматизация для разработчиков

Парсинг и SEO-анализ с Playwright: автоматизация для разработчиков

Запланировано Прикреплена Закрыта Перенесена TypeScript
playwrightпарсингtypescript
1 Сообщения 1 Постеры 84 Просмотры
  • Сначала старые
  • Сначала новые
  • По количеству голосов
Ответить
  • Ответить, создав новую тему
Авторизуйтесь, чтобы ответить
Эта тема была удалена. Только пользователи с правом управления темами могут её видеть.
  • kirilljsxK Не в сети
    kirilljsxK Не в сети
    kirilljsx
    js
    написал в отредактировано kirilljsx
    #1

    Обложка: Как автоматизировать парсинг и SEO-анализ сайтов с Playwright и TypeScript: DIY-скрипт для full-stack разработчиков

    Ребят, сколько раз я видел, как разработчики вручную собирают данные с сайтов, копируют мета-теги, проверяют структуру просто задолбалось смотреть на эту рутину. Playwright + TypeScript решают эту проблему за пару часов кодинга. Это не просто инструмент для тестирования, а мощная апишка для автоматизации парсинга и SEO-анализа, которая работает быстро и надежно.

    Делюсь сегодня реальным подходом: как накидать DIY-скрипт, который будет стягивать данные с любого сайта, анализировать SEO-метрики, собирать заголовки и структуру страниц. Никакого сложного Scrapy или дорогих сервисов — просто Node.js, Playwright и немного TypeScript.

    Почему именно Playwright для парсинга

    Праграйт — это браузерная апишка, которая позволяет управлять Chromium, Firefox и WebKit прямо из кода. В отличие от простых HTTP-клиентов, он рендерит JavaScript, работает с динамическим контентом и легко имитирует действия юзера. Если сайт грузит данные через AJAX или использует React, Vue или Angular — Playwright справится с этим на раз.

    Ключевое преимущество: скорость разработки. За час ты напишешь парсер, который будет стягивать данные, анализировать HTML-структуру, собирать метатеги и генерировать отчеты. Для full-stack разработчика это значит, что ты не зависишь от отдельных парсеров или апишек — всё на одном стеке.

    Вот что умеет Playwright из коробки:

    • Навигация и ожидание: page.goto() переводит на нужный URL, waitForSelector дожидается загрузки элементов
    • Извлечение данных: page.$eval() для одного элемента, page.$$eval() для массива
    • Имитация действий: клики, ввод текста, скролл, перехват сетевых запросов
    • Скриншоты и PDF: для визуальной проверки и архивирования
    • Параллельная работа: можно запустить несколько браузеров одновременно

    Парсим страницы и собираем SEO-данные

    Начнём с базового скрипта, который открывает сайт, вытягивает основные SEO-метрики и сохраняет результаты в JSON. Это фундамент, на котором можно строить что угодно.

    Представим, что нам нужно проанализировать e-commerce сайт: собрать названия товаров, цены, описания, метатеги для SEO. Вот как это выглядит:

    import { chromium } from 'playwright';
    import * as fs from 'fs';
    
    interface SEOData {
      title: string;
      description: string;
      h1: string[];
      h2: string[];
      links: { text: string; href: string }[];
      images: { alt: string; src: string }[];
    }
    
    interface Product {
      name: string;
      price: string;
      description: string;
    }
    
    async function scrapeSEOData(url: string): Promise<SEOData> {
      const browser = await chromium.launch();
      const page = await browser.newPage();
      await page.goto(url);
    
      const seoData = await page.evaluate(() => {
        const title = document.querySelector('title')?.innerText || '';
        const description =
          document.querySelector('meta[name="description"]')?.getAttribute('content') || '';
        const h1 = Array.from(document.querySelectorAll('h1')).map(el => el.innerText);
        const h2 = Array.from(document.querySelectorAll('h2')).map(el => el.innerText);
        const links = Array.from(document.querySelectorAll('a')).map(el => ({
          text: el.innerText,
          href: el.getAttribute('href') || ''
        }));
        const images = Array.from(document.querySelectorAll('img')).map(el => ({
          alt: el.getAttribute('alt') || '',
          src: el.getAttribute('src') || ''
        }));
    
        return { title, description, h1, h2, links, images };
      });
    
      await browser.close();
      return seoData;
    }
    
    // Использование
    (async () => {
      const data = await scrapeSEOData('https://danube-web.shop/');
      fs.writeFileSync('seo-data.json', JSON.stringify(data, null, 2));
      console.log('SEO-данные сохранены в seo-data.json');
    })();
    

    Видишь, как просто? За несколько строк кода ты получил все основные SEO-метрики. Теперь усложним: давай собирать товары с e-commerce сайта.

    async function scrapeProducts(url: string): Promise<Product[]> {
      const browser = await chromium.launch({ headless: true });
      const page = await browser.newPage();
      await page.goto(url);
    
      // Ждём загрузки карточек товара
      await page.waitForSelector('.preview');
    
      const products = await page.$$eval('.preview', nodes =>
        nodes.map(node => ({
          name: node.querySelector('.preview-title')?.innerText || '',
          price: node.querySelector('.preview-price')?.innerText || '',
          description: node.querySelector('.preview-author')?.innerText || ''
        }))
      );
    
      await browser.close();
      return products;
    }
    

    Делюсь ещё одним лайфхаком: если нужно собрать данные со страницы, где много AJAX и динамической подгрузки, используй waitForSelector. Это гарантирует, что элементы уже в DOM, прежде чем ты попытаешься их вытянуть.

    Имитация действий пользователя и обход блокировок

    Не все данные лежат на поверхности. Иногда нужно нажать кнопку, заполнить форму, скролить страницу — и только потом собирать информацию. Playwright это делает как родное.

    Например, ты хочешь открыть поисковую форму, ввести запрос и собрать результаты. Вот как это выглядит в реальности:

    async function searchAndScrape(searchQuery: string): Promise<string[]> {
      const browser = await chromium.launch();
      const page = await browser.newPage();
      await page.goto('https://twitch.tv/directory/game/Art');
    
      // Находим и кликаем на кнопку поиска
      await page.click('button[aria-label="Search"]');
    
      // Вводим текст в поле поиска
      await page.fill('input[type="search"]', searchQuery);
    
      // Нажимаем Enter
      await page.press('input[type="search"]', 'Enter');
    
      // Ждём результаты
      await page.waitForSelector('.search-result');
    
      // Собираем данные
      const results = await page.$$eval('.search-result', nodes =>
        nodes.map(node => node.innerText)
      );
    
      await browser.close();
      return results;
    }
    

    Если сайт пытается заблокировать парсер (проверяет User-Agent, отправляет капчу), ты можешь перехватывать сетевые запросы и блокировать ненужные ресурсы. Это ускоряет парсинг в разы:

    await page.route('**/*', route => {
      const request = route.request();
    
      // Блокируем картинки, стили, шрифты
      if (
        request.resourceType() === 'image' ||
        request.resourceType() === 'stylesheet' ||
        request.resourceType() === 'font'
      ) {
        route.abort();
      } else {
        route.continue();
      }
    });
    
    await page.goto(url);
    

    В результате парсинг работает быстрее, и сервер видит, что ты не качаешь всё подряд — это снижает риск блокировки.

    TypeScript и параметризация тестов

    Если ты разрабатываешь для production, TypeScript — обязателен. Типы помогают избежать ошибок, дают автодополнение в IDE и делают код поддерживаемым. Плюс, с TypeScript ты можешь параметризовать тесты и парсеры, чтобы легко добавлять новые сценарии.

    Давай соберём всё вместе: создадим конфигурацию для парсинга разных сайтов, напишем типы для данных и реализуем параметризованные скрипты.

    interface ScrapeConfig {
      name: string;
      url: string;
      selectors: {
        productCard: string;
        title: string;
        price: string;
        image: string;
      };
    }
    
    const configs: ScrapeConfig[] = [
      {
        name: 'Danube Shop',
        url: 'https://danube-web.shop/',
        selectors: {
          productCard: '.preview',
          title: '.preview-title',
          price: '.preview-price',
          image: 'img'
        }
      },
      {
        name: 'Another Shop',
        url: 'https://example-shop.com/',
        selectors: {
          productCard: '.product',
          title: '.product-name',
          price: '.product-price',
          image: '.product-img'
        }
      }
    ];
    
    async function scrapeWithConfig(config: ScrapeConfig) {
      const browser = await chromium.launch();
      const page = await browser.newPage();
      await page.goto(config.url);
    
      await page.waitForSelector(config.selectors.productCard);
    
      const products = await page.$$eval(config.selectors.productCard, (nodes, selectors) => {
        return nodes.map(node => ({
          title: node.querySelector(selectors.title)?.innerText || '',
          price: node.querySelector(selectors.price)?.innerText || '',
          image: node.querySelector(selectors.image)?.getAttribute('src') || ''
        }));
      }, config.selectors);
    
      await browser.close();
      return products;
    }
    
    // Запускаем парсинг для всех конфигов
    (async () => {
      for (const config of configs) {
        console.log(`Парсим ${config.name}...`);
        const products = await scrapeWithConfig(config);
        fs.writeFileSync(`${config.name}.json`, JSON.stringify(products, null, 2));
        console.log(`Сохранено ${products.length} товаров`);
      }
    })();
    

    Видишь идею? Ты описываешь конфигурацию сайта один раз, а потом просто добавляешь новые сайты в массив. Скрипт сам разбирается, как их парсить. Это масштабируемо и удобно.

    Параллельный парсинг и оптимизация

    Если нужно собрать данные с сотни ссылок, парсить их по одной — пустая трата времени. Playwright легко запускает несколько браузеров параллельно. С помощью Promise.all() ты можешь обработать сразу несколько страниц.

    async function scrapeMultiplePages(urls: string[]): Promise<any[]> {
      const browser = await chromium.launch();
      const allData: any[] = [];
    
      // Батчим по 5 страниц одновременно
      const batchSize = 5;
      for (let i = 0; i < urls.length; i += batchSize) {
        const batch = urls.slice(i, i + batchSize);
        const promises = batch.map(async url => {
          const page = await browser.newPage();
          await page.goto(url);
          const data = await page.evaluate(() => ({
            title: document.querySelector('title')?.innerText || '',
            url: window.location.href
          }));
          await page.close();
          return data;
        });
    
        const batchResults = await Promise.all(promises);
        allData.push(...batchResults);
        console.log(`Обработано ${Math.min(i + batchSize, urls.length)} из ${urls.length}`);
      }
    
      await browser.close();
      return allData;
    }
    

    Это позволяет парсить в 100+ раз быстрее, чем последовательный обход. Батчируем по 5-10 страниц одновременно — и сервер не заподозрит, что его парсят.

    Автоматизация SEO-аудита и отчеты

    Делюсь ещё одной фишкой: автоматизированный SEO-аудит. Собираешь SEO-метрики, анализируешь их, генерируешь отчёт. Это может быть полезно для агентств, которые проверяют сайты клиентов.

    interface SEOAudit {
      url: string;
      hasTitle: boolean;
      hasDescription: boolean;
      h1Count: number;
      brokenLinks: string[];
      missingAltText: number;
      pageLoadTime: number;
    }
    
    async function auditSEO(url: string): Promise<SEOAudit> {
      const browser = await chromium.launch();
      const page = await browser.newPage();
      const startTime = Date.now();
      await page.goto(url);
      const pageLoadTime = Date.now() - startTime;
    
      const audit = await page.evaluate(() => {
        const hasTitle = !!document.querySelector('title')?.innerText;
        const hasDescription = !!document.querySelector('meta[name="description"]');
        const h1Count = document.querySelectorAll('h1').length;
        const links = Array.from(document.querySelectorAll('a'));
        const brokenLinks = links
          .filter((el: HTMLAnchorElement) => !el.href || el.href.startsWith('javascript:'))
          .map((el: HTMLAnchorElement) => el.href);
        const missingAltText = Array.from(document.querySelectorAll('img')).filter(
          (el: HTMLImageElement) => !el.alt
        ).length;
    
        return {
          hasTitle,
          hasDescription,
          h1Count,
          brokenLinks,
          missingAltText
        };
      });
    
      await browser.close();
    
      return {
        url,
        ...audit,
        pageLoadTime
      };
    }
    
    // Использование
    (async () => {
      const audit = await auditSEO('https://example.com');
      console.log('SEO-аудит:', audit);
      fs.writeFileSync('seo-audit.json', JSON.stringify(audit, null, 2));
    })();
    

    Такой скрипт за минуту проверит все основные SEO-метрики и даст тебе список проблем. Добавь в Cronjob — и получишь автоматический мониторинг SEO прямо в своей инфраструктуре.


    Когда Playwright это не лучший выбор

    Практичный совет: Playwright — мощный инструмент, но не универсальный. Если тебе нужно парсить тысячи страниц на непрерывной основе, headless браузер может быть медленным. В таких случаях имеет смысл рассмотреть гибридный подход.

    Когда Playwright оправдывает себя:

    • Работа с JavaScript-интенсивными сайтами (React, Vue, Angular)
    • Имитация действий пользователя (клики, скролл, ввод)
    • Относительно небольшой объём данных (до 10K страниц в день)
    • Нужна надежность и воспроизводимость
    • Ты уже используешь Node.js/TypeScript в проекте

    Когда стоит подумать о альтернативах:

    • Парсинг статических HTML-сайтов (используй cheerio, jsdom или Beautiful Soup)
    • Большой объём данных, нужна масштабируемость (Scrapy, Selenium Grid)
    • Работа с API (запрашивай данные напрямую, без браузера)
    • Сложная обработка данных (используй Python + pandas)

    Оптимальный подход: гибридный. Твой pipeline может выглядеть так: Playwright собирает динамические данные, параллельно простой HTTP-клиент стягивает статику, всё сохраняется в БД, потом Python обрабатывает и анализирует.

    И что мы сделали?

    Покрыли основы: от простого парсинга до параллельной обработки и SEO-аудита. Но в реальных проектах ты столкнёшься с куда более сложными сценариями.

    Дальше имеет смысл разобраться с обработкой ошибок (retry-логика, таймауты, graceful shutdown), кешированием результатов и интеграцией с очередями задач (BullMQ, Redis). Если парсишь большой объём — настрой логирование и мониторинг, чтобы видеть, что происходит на сервере. И главное: всегда проверяй robots.txt и условия использования сайта — парсинг может быть нарушением ToS.

    1 ответ Последний ответ
    0

    Здравствуйте! Похоже, вас заинтересовала эта беседа, но у вас ещё нет аккаунта.

    Надоело каждый раз пролистывать одни и те же посты? Зарегистрировав аккаунт, вы всегда будете возвращаться на ту же страницу, где были раньше, и сможете выбирать, получать ли уведомления о новых ответах (по электронной почте или в виде push-уведомлений). Вы также сможете сохранять закладки и ставить лайки постам, чтобы выразить свою благодарность другим участникам сообщества.

    С вашими комментариями этот пост мог бы стать ещё лучше 💗

    Зарегистрироваться Войти

    Категории

    • Главная
    • Новости
    • Фронтенд
    • Бекенд
    • Языки программирования

    Контакты

    • Сотрудничество
    • info@exlends.com

    © 2024 - 2026 ExLends, Inc. Все права защищены.

    Политика конфиденциальности
    • Войти

    • Нет учётной записи? Зарегистрироваться

    • Войдите или зарегистрируйтесь для поиска.
    • Первое сообщение
      Последнее сообщение
    0
    • Лента
    • Категории
    • Последние
    • Метки
    • Популярные
    • Пользователи
    • Группы