Работа с DOM в Puppeteer: как получить HTML код и извлечь различные теги (текст, картинки, ссылки)
Оглавление
1. Как пользоваться Puppeteer: установка и быстрый старт
2.1 Как в Puppeteer получить HTML веб-страницы
2.2 Как в Puppeteer получить HTML веб-страницы и сохранить его в файл
2.3 Как в Puppeteer извлечь все определённые элементы (теги) из HTML (DOM)
2.4 Как в Puppeteer извлечь все теги «p» (абзацы текста)
2.5 Как в Puppeteer извлечь все теги «a» (ссылки)
2.6 Как в Puppeteer извлечь все ссылки на картинки
2.7 Как в Puppeteer извлечь заголовок страницы
2.8 Как использовать методы Page.$eval() и Page.$$eval() для выбора элементов
2.9 Как в Puppeteer найти все элементы по имени класса
2.10 Как в Puppeteer найти элемент по id
2.11 Как в Puppeteer найти элемент по тексту, который содержит элемент
4.
2.1 Как в Puppeteer получить HTML веб-страницы
Думаю, главной целью использование Puppeteer является получение HTML и DOM страницы, а не скриншотов. Начнём с получения HTML веб-страницы.
Создайте файл html.js со следующим содержимым:
const puppeteer = require('puppeteer'); async function run() { const browser = await puppeteer.launch(); const page = await browser.newPage(); const customUserAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36'; await page.setViewport({width: 1440, height: 3440}); await page.setUserAgent(customUserAgent); await page.goto('https://w-e-b.site/?act=client-tls-fingerprinting'); const html = await page.content(); console.log(html); browser.close(); } run();
Запустите файл следующим образом:
node html
2.2 Как в Puppeteer получить HTML веб-страницы и сохранить его в файл
Думаю, HTML код вы уже увидели, но я уверен, что это не то, что вы ожидали — весь HTML код выведен в окно консоли. Чтобы сохранить в файл HTML код в Puppeteer, используйте следующую конструкцию:
node html > page.htm
Вы также можете сохранить полученный с помощью Puppeteer HTML код (DOM) в файл не используя перенаправление вывода. Для этого создайте файл html-to-file.js со следующим содержимым:
const puppeteer = require('puppeteer'); async function run() { const browser = await puppeteer.launch(); const page = await browser.newPage(); const customUserAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36'; await page.setViewport({width: 1440, height: 3440}); await page.setUserAgent(customUserAgent); await page.goto('https://w-e-b.site/?act=client-tls-fingerprinting'); const html = await page.content(); const fs = require('fs'); fs.writeFile("test.htm", html, function(err) { if(err) { return console.log(err); } console.log("The file was saved!"); }); browser.close(); } run();
Запустите файл следующим образом:
node html-to-file
В результате запуска этой программы полученный с помощью Puppeteer исходный код страницы будет сохранён в файл test.htm.
2.3 Как в Puppeteer извлечь все определённые элементы (теги) из HTML (DOM)
В Puppeteer вы можете использовать селекторы JavaScript, которые позволяют однозначно идентифицировать различные элементы страницы и извлечь их содержимое, либо взаимодействовать с ними — например, заполнять текстовые поля и нажимать на кнопки. Мы будем это рассматривать позже, а пока рассмотрим примеры извлечения определённых тегов — в качестве затравки.
В следующих примерах будет меняться селектор, используемый в методе querySelectorAll. Также мы можем выбирать, какие именно свойства мы хотим получить. Пример популярных свойств, которые мы будем использовать в данной статье:
innerText innerHTML src href
Документация:
- https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll
- https://developer.mozilla.org/en-US/docs/Web/HTML/Element/
- https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/innerText
- https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML
В последующих примерах мы будем активно использовать метод Page.evaluate(), который выполняет указанную функцию в контексте страницы и возвращает результат.
Внутри метода Page.evaluate() мы будем использовать «обычный» JavaScript, то есть без использования библиотеки Puppeteer.
Документация:
2.4 Как в Puppeteer извлечь все теги «p» (абзацы текста)
Создадим файл extract-p.js со следующим содержимым:
const puppeteer = require('puppeteer'); async function run() { const browser = await puppeteer.launch(); const page = await browser.newPage(); const customUserAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36'; await page.setViewport({width: 1440, height: 3440}); await page.setUserAgent(customUserAgent); await page.goto('https://hackware.ru/?p=19139'); const tags = await page.evaluate(() => { return Array.from(document.querySelectorAll('p')) .map(heading => heading.innerText); }); for (const tag of tags) { console.log(tag); } browser.close(); } run();
Запустим:
node extract-p
Цель работы этого скрипта — извлечь все теги «p» со страницы https://hackware.ru/?p=19139
Примечание, если вы знакомы с программированием, но не программируете на JavaScript, то вас может смутить следующий фрагмент кода:
return Array.from(document.querySelectorAll('p')) .map(heading => heading.innerText);
После «return» код не должен выполняться и, казалось бы, конструкция не имеет смысла. Всё дело в особенностях того, как JavaScript обрабатывает пробелы (включая символ новой строки), а именно то, что JavaScript игнорирует пробелы. То есть приведённая выше строка в более привычном виде выглядит так:
return Array.from(document.querySelectorAll('p')).map(heading => heading.innerText);
Но поскольку нередко в JavaScript последовательно применяется несколько методов, а пробелы игнорируются, то довольно часто используют приведённую выше запись для улучшения читаемости кода.
Чтобы предыдущий пример был более осмысленным, уточним селектор:
const puppeteer = require('puppeteer'); async function run() { const browser = await puppeteer.launch(); const page = await browser.newPage(); const customUserAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36'; await page.setViewport({width: 1440, height: 3440}); await page.setUserAgent(customUserAgent); await page.goto('https://hackware.ru/?p=19139'); const tags = await page.evaluate(() => { return Array.from(document.querySelectorAll('div > div.entrytext > p')) .map(heading => heading.innerText); }); for (const tag of tags) { console.log(tag); } browser.close(); } run();
Теперь будут найдены только абзацы текста внутри статьи, без текста в сайдбарах, футерах и прочих не интересных нам областях.
Ниже мы рассмотрим, как получить строки селекторов и вам станет понятно, откуда взялась строка:
div > div.entrytext > p
2.5 Как в Puppeteer извлечь все теги «a» (ссылки)
Создадим файл extract-art-a.js со следующим содержимым:
const puppeteer = require('puppeteer'); async function run() { const browser = await puppeteer.launch(); const page = await browser.newPage(); const customUserAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36'; await page.setViewport({width: 1440, height: 3440}); await page.setUserAgent(customUserAgent); await page.goto('https://hackware.ru/?p=19139'); const tags = await page.evaluate(() => { return Array.from(document.querySelectorAll('a')) .map(heading => heading.href); }); for (const tag of tags) { console.log(tag); } browser.close(); } run();
Этот код извлечёт все ссылки с указанной страницы. Чтобы извлечь только ссылки из статьи, нужно заменить селектор на следующую строку (это селектор для указанной в примере страницы):
div > div.entrytext > p > a
Также обратите внимание на строку:
.map(heading => heading.href);
Для ссылок нас интересует такое свойство как «href».
Следующий пример покажет только внешние ссылки (ссылки, которые ведут на другой домен, отличный от того, где размещена анализируемая страница):
const puppeteer = require('puppeteer'); async function run() { const browser = await puppeteer.launch(); const page = await browser.newPage(); const customUserAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36'; await page.setUserAgent(customUserAgent); await page.goto('https://hackware.ru/?p=19139'); const tags = await page.evaluate(() => { return Array.from(document.querySelectorAll('div > div.entrytext > p > a')) .map(heading => heading.href); }); const hostname = await page.evaluate(() => { return window.location.hostname; }); const re = new RegExp(hostname); for (const tag of tags) { if (!re.test(tag) && tag) { console.log(tag); } } browser.close(); } run();
2.6 Как в Puppeteer извлечь все ссылки на картинки
Создадим файл extract-art-img.js со следующим содержимым:
const puppeteer = require('puppeteer'); async function run() { const browser = await puppeteer.launch(); const page = await browser.newPage(); const customUserAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36'; await page.setViewport({width: 1440, height: 3440}); await page.setUserAgent(customUserAgent); await page.goto('https://hackware.ru/?p=19139'); const tags = await page.evaluate(() => { return Array.from(document.querySelectorAll('img')) .map(heading => heading.src); }); for (const tag of tags) { console.log(tag); } browser.close(); } run();
Этот код покажет все ссылки на картинки на указанной страницы. Чтобы показать только ссылки на картинки из статьи, нужно использовать следующий селектор:
div > div.entrytext > p > a > img
Обратите внимание, что для изображений нас интересует такое свойство как «src».
2.7 Как в Puppeteer извлечь заголовок страницы
Создадим файл extract-art-title.js со следующим содержимым:
const puppeteer = require('puppeteer'); async function run() { const browser = await puppeteer.launch(); const page = await browser.newPage(); const customUserAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36'; await page.setViewport({width: 1440, height: 3440}); await page.setUserAgent(customUserAgent); await page.goto('https://hackware.ru/?p=19139'); const tags = await page.evaluate(() => { return Array.from(document.querySelectorAll('title')) .map(heading => heading.innerText); }); console.log(tags[0]); browser.close(); } run();
Обратите внимание, что вместо того, чтобы перебирать весь массив тегов, мы обратились к элементу массива напрямую:
console.log(tags[0]);
Кстати, для заголовков в библиотеке Puppeteer имеется специальный метод и вы можете его использовать следующим образом:
const puppeteer = require('puppeteer'); async function run() { const browser = await puppeteer.launch(); const page = await browser.newPage(); const customUserAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36'; await page.setUserAgent(customUserAgent); await page.goto('https://hackware.ru/?p=19214'); const title = await page.title(); console.log(title); browser.close(); } run();
Документация:
2.8 Как использовать методы Page.$eval() и Page.$$eval() для выбора элементов
Выше мы рассматривали варианты использования метода page.evaluate(). Главное предназначение этого метода — выполнять код JavaScript в контексте открытой веб-страницы. То есть это универсальный метод, который используется для разных целей. Мы его использовали для выполнения «обычного» JavaScript (без методов Puppeteer), который выбирал элементы по определённому селектору.
Но в для этих целей (выбор элементов по определённому селектору) имеются специальные методы Puppeteer:
- Page.$eval() – этот метод находит первый элемент на странице, соответствующий селектору, и передаёт результат в качестве первого аргумента pageFunction. Этот метод подходит для поиска по id элемента, либо если вам нужен только 1 (первый) элемент.
- Page.$$eval() – этот метод возвращает все элементы, соответствующие селектору, и передаёт полученный массив в качестве первого аргумента pageFunction. Этот метод подходит для поиска по имени класса, а также по имени тега.
Рассмотрим использование метода Page.$$eval() для поиска абзацев текста. Также показанный ниже код выведет общее количество найденных абзацев:
const puppeteer = require('puppeteer'); async function run() { const browser = await puppeteer.launch(); const page = await browser.newPage(); const customUserAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36'; await page.setViewport({width: 1440, height: 3440}); await page.setUserAgent(customUserAgent); await page.goto('https://hackware.ru/?p=19214'); const tags = await page.$$eval('div > div.entrytext > p', tag => { return tag.map(tag => tag.innerText); }); for (const tag of tags) { console.log(tag); } const count = await page.$$eval('div > div.entrytext > p', tag => tag.length); console.log('Total: ' + count); browser.close(); } run();
Документация:
2.9 Как в Puppeteer найти все элементы по имени класса
В последующих частях мы будем более подробно знакомиться с селекторами, поскольку с их использованием будем выполнять такие действия как ввод данных в текстовые поля и форму, выполнять поиск по сайту, нажимать кнопки, получать значение определённого элемента и прочее.
Сейчас же мы лишь поверхностно рассмотрим некоторые случаи использования селекторов. К примеру, следующий код найдёт все элементы, классом которых является «code»:
const puppeteer = require('puppeteer'); async function run() { const browser = await puppeteer.launch(); const page = await browser.newPage(); const customUserAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36'; await page.setViewport({width: 1440, height: 3440}); await page.setUserAgent(customUserAgent); await page.goto('https://hackware.ru/?p=19214'); const tags = await page.$$eval('.code', tag => { return tag.map(tag => tag.innerText); }); for (const tag of tags) { console.log(tag); console.log('============================================'); } const count = await page.$$eval('.code', tag => tag.length); console.log('Total: ' + count); browser.close(); } run();
2.10 Как в Puppeteer найти элемент по id
Для поиска элемента по его id мы можем использовать Page.$eval() вместо Page.$$eval(). Следующий код выведет текст элемента с id «creditline»:
const puppeteer = require('puppeteer'); async function run() { const browser = await puppeteer.launch(); const page = await browser.newPage(); const customUserAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36'; await page.setViewport({width: 1440, height: 3440}); await page.setUserAgent(customUserAgent); await page.goto('https://hackware.ru/?p=19214'); const tag = await page.$eval('#creditline', el => el.innerText) console.log(tag); browser.close(); } run();
Вы для элементов разного типа вы можете получать различные свойства:
const searchValue = await page.$eval('#search', el => el.value); const preloadHref = await page.$eval('link[rel=preload]', el => el.href); const html = await page.$eval('.main-container', el => el.outerHTML);
2.11 Как в Puppeteer найти элемент по тексту, который содержит элемент
Можно искать элементы по самым разным селекторам. Давайте рассмотрим пример, как найти все элементы содержащие определённый текст. В этом примере мы будем искать теги «p» содержащие строку «код»:
const puppeteer = require('puppeteer'); async function run() { const browser = await puppeteer.launch(); const page = await browser.newPage(); const customUserAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36'; await page.setViewport({width: 1440, height: 3440}); await page.setUserAgent(customUserAgent); await page.goto('https://hackware.ru/?p=19214'); const tags = await page.$$eval('div > div.entrytext > p', tags => { var arr = []; for (const tag of tags) { if (tag.innerText.includes('код')) { arr.push(tag); } } return arr.map(arr => arr.innerText); }); for (const tag of tags) { console.log(tag); } console.log('Total: ' + tags.length); browser.close(); } run();
Поиск можно выполнять и по содержимому других тегов, например, по надписям на кнопках и прочее. Конечно же, при этом можно выполнять различные действия, например, в следующим примере ищется кнопка по надписи на ней и затем эмулируется клик по этой кнопке:
const puppeteer = require('puppeteer'); async function run() { const browser = await puppeteer.launch(); const page = await browser.newPage(); const customUserAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36'; await page.setViewport({width: 1440, height: 3440}); await page.setUserAgent(customUserAgent); await page.goto('https://hackware.ru/?p=19214'); await page.$$eval('button', buttons => { for (const button of buttons) { if (button.textContent === 'Поиск') { button.click(); break; // Clicking the first matching button and exiting the loop } } }); browser.close(); } run();
Смотрите продолжение в следующей части: Продвинутая работа с DOM в Puppeteer: отключение JavaScript, загрузка HTML без захода на сайт, действие при ошибках, ожидание и прокрутка страницы
Связанные статьи:
- Как пользоваться Puppeteer: установка и быстрый старт (100%)
- Продвинутая работа с DOM в Puppeteer: отключение JavaScript, загрузка HTML без захода на сайт, действие при ошибках, ожидание и прокрутка страницы (100%)
- OpenSSL: принципы работы, создание сертификатов, аудит (61.4%)
- HTTP протокол (61.4%)
- Как установить настоящий Firefox в Kali Linux (56.4%)
- Установка и запуск Burp Suite (RANDOM - 50%)