通过AJAX加载SPA网页

时间:2017-08-14 17:30:22

标签: javascript jquery ajax single-page-application jquery-load

我正在尝试通过插入URL来使用JavaScript获取整个网页。但是,该网站是作为单页应用程序(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

然而,当我尝试通过与$.get().load()的AJAX调用加载网页时,我只会收到初始响应(与内容相同)当超过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

我已经尝试了solution hereeval()每个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());
});

问:完全加载可通过JavaScript报废的网页的任何选项?

3 个答案:

答案 0 :(得分:3)

您永远无法完全复制任意(SPA)页面的内容。

我看到的唯一方法是使用无头浏览器,例如PhantomJSHeadless ChromeHeadless Firefox

我想尝试无头Chrome,所以让我们看看它对你的网页有什么作用:

使用内部REPL快速检查

使用Chrome Headless加载该页面(您在Mac / Linux上需要Chrome 59,在Windows上需要Chrome 60),并使用REPL中的JavaScript查找页面标题:

% 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"}}

注意:要让chrome命令行在Mac上运行,我事先就这样做了:

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

以编程方式使用Node&amp;木偶人

  

Puppeteer是一个节点库(由Google Chrome开发人员提供),它提供了一个高级API,可通过DevTools协议控制无头Chrome。它也可以配置为使用完整(非无头)Chrome。

(步骤0:安装Node&amp; 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

答案 1 :(得分:1)

首先关闭:避免eval - 您的内容安全策略应该阻止它,让您容易受到轻松的XSS攻击。刮刮胶机肯定不会运行它。

您所描述的问题在所有SPA中都很常见 - 当一个人访问时,他们会获得您的应用程序shell脚本,然后加载其余内容 - 这一切都很好。当机器人访问时,他们会忽略脚本并返回空壳。

解决方案是服务器端渲染。一种方法是,如果您在服务器上使用JS渲染器(比如React)和Node.js,您可以相当轻松地构建JS并静态地提供它。

但是,如果您不是,那么您需要在服务器上运行无头浏览器,执行用户所需的所有JS,然后将结果提供给机器人。

幸运的是其他人已经done all the work here了。他们已经在线进行了演示,您可以try out with your site

Rendertron preview

答案 2 :(得分:0)

我认为你应该知道SPA的概念, SPA是单页应用程序,它只是静态的html文件。当路由变换时,页面将动态创建或修改DOM个节点,以通过使用Javascript实现切换页面的效果。

因此,如果您使用$.get(),服务器将响应具有稳定页面的静态html文件,因此您无法加载所需内容。

如果您想使用$.get(),它有两种方式,第一种方式是使用headless browser,例如,headless chromephantomJS等。它会帮助您加载页面,您可以获取已加载页面的dom个节点。第二个是SSRServer Slide Render),如果您使用SSR,您将获得页面的HTML数据直接由$.get,因为服务器响应请求不同路由时对应页面的HTML数据。

参考:

SSR

vue的SRR框架:Nuxt.js

PhantomJS

Node API of Headless Chrome