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

Я пытаюсь получить всю веб-страницу с помощью JavaScript, подключив URL-адрес. Однако веб-сайт построен как одностраничное приложение (SPA), которое использует JavaScript / костяк.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):

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


Я пробовал решение здесь to 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?

3 ответов


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

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

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

быстрая проверка с помощью внутреннего REPL

загрузите эту страницу с Chrome безголовым (вам понадобится 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

Кукловод - это библиотека узлов (разработчиками Google Chrome), которая предоставляет API высокого уровня для управления безголовым Chrome по протоколу DevTools. Его можно также установить для использования полного (безголовый) хром.

(Шаг 0 : Установка узел & пряжа если у вас их нет)

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

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

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

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

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

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

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


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

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

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

ссылки:

ССР

рамка SRR vue: Nuxt.js

PhantomJS

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