Перейти к основному содержимому

JavaScript-рендеринг

JavaScript-рендеринг - это процесс выполнения JavaScript на странице для изменения её структуры или содержимого. Его также называют клиентским рендерингом, в противоположность серверному рендерингу. Некоторые современные сайты используют клиентский рендеринг, некоторые - серверный, а многие передовые веб-сайты комбинируют оба подхода.

Сайт Crawlee не использует JavaScript-рендеринг для отображения контента, поэтому давайте рассмотрим другой пример. Магазин Apify - это библиотека скраперов и автоматизаций, называемых акторами, которые можно использовать бесплатно. Этот сайт использует JavaScript-рендеринг для отображения списка акторов, что делает его отличным примером для демонстрации.

src/main.mjs
import { CheerioCrawler } from 'crawlee';

const crawler = new CheerioCrawler({
async requestHandler({ $, request }) {
// Извлекаем текстовое содержимое карточки актора
const actorText = $('.ActorStoreItem').text();
console.log(`АКТОР: ${actorText}`);
}
})

await crawler.run(['https://apify.com/store']);

При запуске кода вы увидите, что краулер не выведет содержимое карточки актора.

АКТОР:

Это происходит потому, что Магазин Apify использует клиентский JavaScript для рендеринга контента, а CheerioCrawler не может его выполнить, поэтому текст никогда не появляется в HTML страницы.

Вы можете проверить это с помощью Chrome DevTools. Если перейти на https://apify.com/store, кликнуть правой кнопкой мыши и выбрать Просмотр исходного кода страницы, поиск ActorStoreItem не даст результатов. Но если снова кликнуть правой кнопкой, выбрать Инспектировать и искать тот же ActorStoreItem, вы найдете множество совпадений.

Как это возможно? Потому что Просмотр исходного кода показывает оригинальный HTML до выполнения JavaScript - то, что получает CheerioCrawler. А Инспектировать показывает текущий HTML после выполнения JavaScript. Понимая это, неудивительно, что CheerioCrawler не может найти данные. Для этого нам нужен безголовый браузер.

Безголовые браузеры​

Чтобы получить содержимое .ActorStoreItem, необходимо использовать безголовый браузер. Можно выбрать одну из двух библиотек для управления браузером: Puppeteer или Playwright. Выбор прост: если вы знаете одну из них - используйте её. Если знаете обе или не знаете ни одной - выбирайте Playwright, так как в большинстве случаев он лучше.

Ожидание рендеринга элементов​

Вот примеры кода для обеих библиотек. Playwright немного приятнее в использовании, но обе библиотеки справятся с задачей. Главное отличие в том, что Playwright автоматически ждет появления элементов, а в Puppeteer нужно явно указывать ожидание.

src/main.mjs
import { PlaywrightCrawler } from 'crawlee';

const crawler = new PlaywrightCrawler({
async requestHandler({ page }) {
// page.locator указывает на элемент в DOM
// используя CSS-селектор, но он не обращается к нему ещё.
const actorCard = page.locator('.ActorStoreItem').first();
// При вызове одного из методов locator Playwright
// ожидает, пока элемент будет отрендерен, и затем обращается к нему.
const actorText = await actorCard.textContent();
console.log(`ACTOR: ${actorText}`);
},
});

await crawler.run(['https://apify.com/store']);

При запуске кода вы увидите неотформатированное содержимое первой карточки актора:

АКТОР: Web Scraperapify/web-scraperСканирует произвольные веб-сайты используя [...]

Мы не шутим​

Если вы не верите, что нужно ждать появления элементов, запустите следующий код без ожидания:

src/main.mjs
import { PlaywrightCrawler } from 'crawlee';

const crawler = new PlaywrightCrawler({
async requestHandler({ page }) {
// Сразу извлекаем текст со страницы, не дожидаясь
// появления элемента в DOM
const actorText = await page.$eval('.ActorStoreItem', (el) => {
return el.textContent;
});
console.log(`ACTOR: ${actorText}`);
},
});

await crawler.run(['https://apify.com/store']);

В обоих случаях запрос будет повторен несколько раз и в итоге завершится с ошибкой:

ОШИБКА [...] Error: не удалось найти элемент, соответствующий селектору ".ActorStoreItem"

Это происходит потому, что при попытке доступа к элементу в браузере он еще не отрендерен в DOM.

подсказка

Это руководство лишь затрагивает концепцию JavaScript-рендеринга и использования безголовых браузеров. Чтобы узнать больше, продолжите с курсом по Puppeteer и Playwright в Академии Apify. Он бесплатный и с открытым исходным кодом ❤️.