Работа с DOM в Puppeteer: как получить HTML код и извлечь различные теги (текст, картинки, ссылки)


Оглавление

1. Как пользоваться Puppeteer: установка и быстрый старт

2. Работа с DOM в Puppeteer: как получить HTML код и извлечь различные теги (текст, картинки, ссылки и прочее)

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 найти элемент по тексту, который содержит элемент

3. Продвинутая работа с DOM в Puppeteer: отключение JavaScript, загрузка HTML без захода на сайт, действие при ошибках, ожидание и прокрутка страницы

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

Документация:

В последующих примерах мы будем активно использовать метод 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 без захода на сайт, действие при ошибках, ожидание и прокрутка страницы


Рекомендуется Вам:

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *