javascript - 获取可靠的网站屏幕截图? Phantomjs 和 Casperjs 在某些网站上都返回空屏幕截图

标签 javascript phantomjs screen-scraping casperjs

打开网页并截图。

仅使用 phantomjs:(这是一个简单的脚本,实际上它是其文档中使用的示例脚本。http://phantomjs.org/screen-capture.html

var page = require('webpage').create();
page.open('http://github.com/', function() {
  page.render('github.png');
  phantom.exit();
});

问题是,对于某些网站(例如 github)来说,有趣的是,它们以某种方式检测到并且不为 phantomjs 提供服务,并且没有渲染任何内容。结果是 github.png 是一个空白的白色 png 文件。

将 github 替换为:“google.com”,您将获得预期的漂亮(正确)屏幕截图。

起初我以为这是 Phantomjs 问题,所以我尝试通过 Casperjs 运行它:

casper.start('http://www.github.com/', function() {
    this.captureSelector('github.png', 'body');
});

casper.run();

但是我得到了与 Phantomjs 相同的行为。

所以我认为这很可能是用户代理问题。如:Github 嗅出 Phantomjs 并决定不显示该页面。因此,我将用户代理设置如下,但仍然不起作用。

var page = require('webpage').create();
page.settings.userAgent = 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2049.0 Safari/537.36';
page.open('http://github.com/', function() {
  page.render('github.png');
  phantom.exit();
});

然后我尝试解析该页面,显然有些网站(同样是 github)似乎没有发送任何内容。

使用 casperjs 我尝试打印标题。对于 google.com,我返回了 Google,但对于 github.com,我返回了 bupkis。示例代码:

var casper = require('casper').create();

casper.start('http://github.com/', function() {
    this.echo(this.getTitle());
});

casper.run();  

与上面相同,在纯 phantomjs 中也会产生相同的结果。

更新:

这可能是一个时间问题吗? github 真的 super 慢吗?我对此表示怀疑,但无论如何还是让我们测试一下..

var page = require('webpage').create();
page.open('http://github.com', function (status) {
    /* irrelevant */
   window.setTimeout(function () {
            page.render('github.png');
            phantom.exit();
        }, 3000);
});

结果仍然是bupkis。所以不,这不是时间问题。

  1. 像 github 这样的网站如何阻止 phantomjs?
  2. 我们如何可靠地截取所有网页的屏幕截图?要求速度快,而且 headless 。

最佳答案

经过一段时间的反复思考,我能够缩小问题的范围。显然 PhantomJS 使用默认的 ssl sslv3 这会导致 github 由于 ssl 握手错误而拒绝连接

phantomjs --debug=true github.js

显示输出:

. . .
2014-10-22T19:48:31 [DEBUG] WebPage - updateLoadingProgress: 10 
2014-10-22T19:48:32 [DEBUG] Network - Resource request error: 6 ( "SSL handshake failed" ) URL: "https://github.com/" 
2014-10-22T19:48:32 [DEBUG] WebPage - updateLoadingProgress: 100 

因此,我们可以得出结论,没有屏幕被截取,因为 github 拒绝连接。太棒了,很有道理。因此,让我们将 SSL 标志设置为 --ssl-protocol=any 并使用 --ignore-ssl-errors=true 忽略 ssl 错误

phantomjs --ignore-ssl-errors=true --ssl-protocol=any --debug=true github.js

非常成功!现在正在正确渲染并保存屏幕截图,但调试器向我们显示类型错误:

TypeError: 'undefined' is not a function (evaluating 'Array.prototype.forEach.call.bind(Array.prototype.forEach)')

  https://assets-cdn.github.com/assets/frameworks-dabc650f8a51dffd1d4376a3522cbda5536e4807e01d2a86ff7e60d8d6ee3029.js:29
  https://assets-cdn.github.com/assets/frameworks-dabc650f8a51dffd1d4376a3522cbda5536e4807e01d2a86ff7e60d8d6ee3029.js:29
2014-10-22T19:52:32 [DEBUG] WebPage - updateLoadingProgress: 72 
2014-10-22T19:52:32 [DEBUG] WebPage - updateLoadingProgress: 88 
ReferenceError: Can't find variable: $

  https://assets-cdn.github.com/assets/github-fa2f009761e3bc4750ed00845b9717b09646361cbbc3fa473ad64de9ca6ccf5b.js:1
  https://assets-cdn.github.com/assets/github-fa2f009761e3bc4750ed00845b9717b09646361cbbc3fa473ad64de9ca6ccf5b.js:1

我手动检查了 github 主页,只是为了看看是否存在 TypeError,但事实并非如此。

我的下一个猜测是资源加载速度不够快。Phantomjs 比高速子弹还要快!

因此,让我们尝试人为地减慢速度,看看是否可以消除 TypeError...

var page = require('webpage').create();
page.settings.userAgent = 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2049.0 Safari/537.36';
page.open('http://github.com', function (status) {
   window.setTimeout(function () {
            page.render('github.png');
            phantom.exit();
        }, 3000);
});

这不起作用...仔细检查图像后 - 很明显缺少一些元素。主要是一些图标和标志。

成功? 部分原因是我们现在至少得到了屏幕截图,而之前我们什么也没有得到。

工作完成了吗? 不完全是。需要确定导致 TypeError 的原因,因为它会阻止某些资源加载并扭曲图像。

附加

尝试使用 CasperJS 重新创建 --debug 与 PhantomJS 相比非常丑陋且难以遵循:

casper.start();
casper.userAgent('Mozilla/5.0 (Macintosh; Intel Mac OS X)');
casper.thenOpen('https://www.github.com/', function() {
    this.captureSelector('github.png', 'body');
});

casper.run();

控制台:

casperjs test --ssl-protocol=any --debug=true github.js

此外,图像缺少相同的图标,而且视觉上也扭曲。由于 CasperJs 依赖于 Phantomjs,因此我看不到使用它来完成此特定任务的值(value)。

如果您想补充我的答案,请分享您的发现。对完美的 PhantomJS 解决方案非常感兴趣

更新 #1:删除 TypeError

@ArtjomB 指出 Phantomjs 在本次更新 (1.9.7) 的当前版本中不支持 js bind。为此,他解释道:ArtjomB: PhantomJs Bind Issue Answer

The TypeError: 'undefined' is not a function refers to bind, because PhantomJS 1.x doesn't support it. PhantomJS 1.x uses an old fork of QtWebkit which is comparable to Chrome 13 or Safari 5. The newer PhantomJS 2 will use a newer engine which will support bind. For now you need to add a shim inside of the page.onInitialized event handler:

好的,很好,所以下面的代码将处理上面的 TypeError 。 (但功能不完整,详情见下文)

var page = require('webpage').create();
page.settings.userAgent = 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2049.0 Safari/537.36';
page.open('http://github.com', function (status) {
   window.setTimeout(function () {
            page.render('github.png');
            phantom.exit();
        }, 5000);
});
page.onInitialized = function(){
    page.evaluate(function(){
        var isFunction = function(o) {
          return typeof o == 'function';
        };

        var bind,
          slice = [].slice,
          proto = Function.prototype,
          featureMap;

        featureMap = {
          'function-bind': 'bind'
        };

        function has(feature) {
          var prop = featureMap[feature];
          return isFunction(proto[prop]);
        }

        // check for missing features
        if (!has('function-bind')) {
          // adapted from Mozilla Developer Network example at
          // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind
          bind = function bind(obj) {
            var args = slice.call(arguments, 1),
              self = this,
              nop = function() {
              },
              bound = function() {
                return self.apply(this instanceof nop ? this : (obj || {}), args.concat(slice.call(arguments)));
              };
            nop.prototype = this.prototype || {}; // Firefox cries sometimes if prototype is undefined
            bound.prototype = new nop();
            return bound;
          };
          proto.bind = bind;
        }
    });
}

现在,上面的代码将为我们提供与之前相同的屏幕截图,并且调试不会显示 TypeError,因此从表面上看,一切似乎都有效。已经取得了进展。

不幸的是,所有图像图标[ Logo 等]仍然无法正确加载。我们看到某种 3W 图标,不确定它来自哪里。

感谢@ArtjomB的帮助

enter image description here

关于javascript - 获取可靠的网站屏幕截图? Phantomjs 和 Casperjs 在某些网站上都返回空屏幕截图,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26517852/

相关文章:

jquery - 如何将 jQuery 与 phantomjs 一起使用

javascript - 显示包含对象和图标的数组

javascript - JavaScript 中的正则表达式用于搜索

javascript - 如何在for循环中等待每次迭代并返回响应作为nodeJS中的API响应

点击时的 JavaScript

python - 使用 Python 下载 URL 的 html - 但启用了 javascript

javascript - 如何通过 phantomJS 实时渲染 javascript?

macos - 为什么即使它在我的 PATH 上,Groovy 也找不到该程序?

python - 检查python urlopen是否加载完成

python - 尝试使用 Beautiful Soup 或 ElementTree 从链接的迭代列表中抓取信息