Подтвердить что ты не робот

Загрузите веб-страницу SPA через AJAX

Я пытаюсь загрузить всю веб-страницу с помощью JavaScript, подключив URL-адрес. Тем не менее, веб-сайт построен как одностраничное приложение (SPA), которое использует JavaScript/backbone.js для динамической загрузки большей части содержимого после рендеринга начальный ответ.

Так, например, когда я направляюсь на следующий адрес:

https://connect.garmin.com/modern/activity/1915361012

И затем введите это в консоль (после загрузки страницы):

var $page = $("html")
console.log("%c✔: ", "color:green;", $page.find(".inline-edit-target.page-title-overflow").text().trim());
console.log("%c✔: ", "color:green;", $page.find("footer .details").text().trim());

Затем я получу динамически загруженный заголовок активности, а также статически загруженный нижний колонтитул страницы:

Working Screenshot


Однако, когда я пытаюсь загрузить веб-страницу с помощью вызова AJAX с помощью $.get() или .load(), я получаю только исходный ответ (то же, что и контент при просмотре источника):

view-source:https://connect.garmin.com/modern/activity/1915361012

Итак, если я использую один из следующих вызовов AJAX:

// jQuery.get()
var url = "https://connect.garmin.com/modern/activity/1915361012";
jQuery.get(url,function(data) {
    var $page = $("<div>").html(data)
    console.log("%c✖: ", "color:red;",   $page.find(".page-title").text().trim());
    console.log("%c✔: ", "color:green;", $page.find("footer .details").text().trim());
});

// jQuery.load()
var url = "https://connect.garmin.com/modern/activity/1915361012";
var $page = $("<div>")
$page.load(url, function(data) {
    console.log("%c✖: ", "color:red;",   $page.find(".page-title").text().trim()    );
    console.log("%c✔: ", "color:green;", $page.find("footer .details").text().trim());
});

Я по-прежнему получаю начальный нижний колонтитул, но не получаю никакого другого содержимого страницы:

Broken - Screenshot


Я пробовал решение здесь до eval() содержимого каждого тега script, но это не отображается достаточно надежный, чтобы загрузить страницу:

jQuery.get(url,function(data) {
    var $page = $("<div>").html(data)
    $page.find("script").each(function() {
        var scriptContent = $(this).html(); //Grab the content of this tag
        eval(scriptContent); //Execute the content
    });
    console.log("%c✖: ", "color:red;",   $page.find(".page-title").text().trim());
    console.log("%c✔: ", "color:green;", $page.find("footer .details").text().trim());
});

Q: Есть ли какие-либо опции для полной загрузки веб-страницы, которая будет подвержена JavaScript?

4b9b3361

Ответ 1

Вы никогда не сможете полностью воспроизвести себе, что делает страница (SPA).

Единственный способ, который я вижу, - использовать безгласный браузер, такой как PhantomJS или Headless Chrome, или Безголовый Firefox.

Я хотел попробовать Chrome без Chrome, поэтому посмотрим, что он может сделать с вашей страницей:

Быстрая проверка с использованием внутреннего REPL

Загрузите эту страницу с помощью Chrome Headless (вам понадобится Chrome 59 на Mac/Linux, Chrome 60 на Windows) и найдите заголовок страницы с JavaScript из REPL:

% chrome --headless --disable-gpu --repl https://connect.garmin.com/modern/activity/1915361012
[0830/171405.025582:INFO:headless_shell.cc(303)] Type a Javascript expression to evaluate or "quit" to exit.
>>> $('body').find('.page-title').text().trim() 
{"result":{"type":"string","value":"Daily Mile - Round 2 - Day 27"}}

NB: чтобы получить командную строку chrome, работающую на Mac, я сделал это заранее:

alias chrome="'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'"

Использование программно с Node и Puppeteer

Puppeteer - это библиотека Node (разработчиками Google Chrome), которая предоставляет API высокого уровня для управления безгласными Chrome over Протокол DevTools. Он также может быть сконфигурирован для использования полного (без головки) Chrome.

(Шаг 0: Установите Node и Yarn, если у вас их нет)

В новом каталоге:

yarn init
yarn add puppeteer

Создайте index.js следующим образом:

const puppeteer = require('puppeteer');
(async() => {
    const url = 'https://connect.garmin.com/modern/activity/1915361012';
    const browser = await puppeteer.launch();
    const page = await browser.newPage();
    // Go to URL and wait for page to load
    await page.goto(url, {waitUntil: 'networkidle'});
    // Wait for the results to show up
    await page.waitForSelector('.page-title');
    // Extract the results from the page
    const text = await page.evaluate(() => {
        const title = document.querySelector('.page-title');
        return title.innerText.trim();
    });
    console.log(`Found: ${text}`);
    browser.close();
})();

Результат:

$ node index.js 
Found: Daily Mile - Round 2 - Day 27

Ответ 2

Во-первых: избегайте eval - ваша политика безопасности содержимого должна блокировать ее, и она оставляет вас открытой для легких атак XSS. Скремблирование ботов определенно не запустит его.

Проблема, которую вы описываете, является общей для всех SPA - когда человек посещает их, появляется ваша оболочка приложения script, которая затем загружается в остальную часть контента - все хорошо. Когда бот посещает, они игнорируют скрипты и возвращают пустую оболочку.

Решение - это рендеринг на стороне сервера. Один из способов сделать это - если вы используете JS-рендеринг (например, React) и Node.js на сервере, вы можете довольно легко построить JS и обслуживать его статически.

Однако, если вы этого не сделаете, вам нужно будет запустить безгласный браузер на своем сервере, который выполнит все JS-пользователя, а затем подаст результат боту.

К счастью, еще кто-то уже выполнил всю работу здесь. Они поставили демо онлайн, чтобы попробовать с вашим сайтом:

Rendertron preview

Ответ 3

Я думаю, вы должны знать концепцию SPA, SPA - одностраничное приложение, это только статический html файл. при изменении маршрута страница будет динамически создавать или изменять узлы DOM для достижения эффекта страницы переключения с помощью Javascript.

Поэтому, если вы используете $.get(), сервер ответит статическим html файлом, который имеет стабильную страницу, поэтому вы не будете загружать то, что хотите.

Если вы хотите использовать $.get(), он имеет два способа: первый использует headless browser, например, headless chrome, phantomJS и т.д. Это поможет вам загрузить страницу, и вы можете получить DOM узлов загруженной страницы. Второй - SSR (Server Slide Render), если вы используете SSR, вы получите HTML-данные страницы напрямую с помощью $.get, так как данные HTML-ответа сервера соответствуют странице, когда запрашивая разные маршруты.

Ссылка:

SSR

кадр SRR vue: Nuxt.js

PhantomJS

Node API безголового Chrome