javascript phantomjs screen-scraping casperjs

javascript - Tomando capturas de pantalla confiables de los sitios web? Phantomjs y Casperjs devuelven capturas de pantalla vacĂ­as en algunos sitios web



screen-scraping (1)

Abra una página web y tome una captura de pantalla.

Usando SOLO phantomjs: (este es un script simple, de hecho es el script de ejemplo utilizado en sus documentos. http://phantomjs.org/screen-capture.html

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

El problema es que para algunos sitios web (como github) lo suficientemente divertidos están detectando y no sirviendo phantomjs y nada se está procesando. El resultado es github.png es un archivo png blanco en blanco.

Reemplace github con "google.com" y obtendrá una buena captura de pantalla (adecuada) como se pretende.

Al principio pensé que esto era un problema de Phantomjs, así que intenté ejecutarlo en Casperjs con:

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

Pero tengo el mismo comportamiento que con Phantomjs.

Así que pensé bien, esto es muy probable que sea un problema de agente de usuario. Como en: Github olfatea Phantomjs y decide no mostrar la página. Así que configuré el agente de usuario como abajo, pero eso todavía no funcionó.

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(); });

Entonces intenté analizar la página y aparentemente algunos sitios (de nuevo como github) no parecen enviar nada por el cable.

Usando casperjs intenté imprimir el título. Y para google.com recuperé Google pero para github.com recuperé bupkis. Código de ejemplo:

var casper = require(''casper'').create(); casper.start(''http://github.com/'', function() { this.echo(this.getTitle()); }); casper.run();

Lo mismo que lo anterior también produce el mismo resultado puramente fantasmático.

Actualizar:

¿Podría ser esto un problema de tiempo? ¿Github es super lento? Lo dudo pero deja de probar de todos modos ..

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

Y el resultado sigue siendo bupkis. Así que no, no es un problema de tiempo.

  1. ¿Cómo son algunos sitios como github bloqueando phantomjs?
  2. ¿Cómo podemos hacer capturas de pantalla de forma fiable TODAS las páginas web? Se requiere que sea rápido, y sin cabeza.

Después de rebotar esto por algún tiempo pude reducir el problema. Al parecer, PhantomJS utiliza un ssl predeterminado de sslv3 que hace que github rechace la conexión debido a un mal saludo ssl

phantomjs --debug=true github.js

Muestra la salida de:

. . . 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

Entonces, de esto podemos concluir que no se tomó ninguna pantalla porque github estaba rechazando la conexión. Genial que tiene perfecto sentido. Así que vamos a establecer el indicador SSL en --ssl-protocol=any y también ignoramos los errores ssl con --ignore-ssl-errors=true

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

¡Gran éxito! Ahora se está representando y guardando correctamente una captura de pantalla, pero el depurador nos muestra un TypeError:

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

Revisé la página de inicio de github manualmente solo para ver si existía un TypeError y NO.

Mi siguiente conjetura es que los activos no se están cargando lo suficientemente rápido ... ¡ Phantomjs es más rápido que una bala de velocidad!

Así que intentemos ralentizarlo artificialmente y ver si podemos deshacernos de ese 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); });

Eso no funcionó ... Después de una inspección más cercana de la imagen, está claro que faltan algunos elementos. Principalmente algunos iconos y el logo.

¿Éxito? Parcialmente porque ahora al menos estamos obteniendo una captura de pantalla donde antes, no estábamos obteniendo nada.

¿Trabajo hecho? No exactamente. Debe determinar qué está causando ese error de tipo porque evita que algunos activos carguen y distorsionen la imagen.

Adicional

El intento de recrear con CasperJS --debug es muy feo y difícil de seguir en comparación con 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();

consola:

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

Además, a la imagen le faltan los mismos iconos, pero también está distorsionada visualmente. Dado que CasperJs confía en Phantomjs, no veo el valor de usarlo para esta tarea específica.

Si desea agregar algo a mi respuesta, comparta sus hallazgos. Muy interesado en una solución PhantomJS impecable.

Actualización # 1: Eliminando el TypeError

@ArtjomB señala que Phantomjs no admite js bind en su versión actual a partir de esta actualización (1.9.7). Por esta razón, él explica: ArtjomB: PhantomJs Bind Issue Answer

TypeError: ''undefined'' no es una función que se refiere a bind, porque PhantomJS 1.x no lo admite. PhantomJS 1.x usa una bifurcación antigua de QtWebkit que es comparable a Chrome 13 o Safari 5. El PhantomJS 2 más nuevo usará un motor más nuevo que admitirá el enlace. Por ahora necesita agregar un calzo dentro del controlador de eventos page.onInitialized:

Ok genial, por lo que el siguiente código se encargará de nuestro TypeError desde arriba. (Pero no completamente funcional, ver más abajo para más detalles)

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; } }); }

Ahora, el código anterior nos dará una captura de pantalla igual a la que teníamos antes de que Y la depuración no muestre un TypeError así que desde la superficie, todo parece funcionar. Se ha progresado.

Desafortunadamente, todos los íconos de imagen [logotipo, etc.] todavía no se están cargando correctamente. Vemos algún tipo de icono de 3W que no está seguro de dónde es eso.

Gracias por la ayuda @ArtjomB