使用phantomjs无头浏览器生成pdf和网页快照说明文档

1,998 阅读4分钟

使用phantomjs无头浏览器生成pdf和网页快照说明文档

phantomjs是什么?

PhantomJS是一个无界面的,可脚本编程的WebKit浏览器引擎。它原生支持多种web 标准:DOM 操作,CSS选择器,JSON,Canvas 以及SVG。

phantomjs可以做什么?

无UI界面的网站测试
屏幕快照
页面操作自动化
网络监控

场景一:生成网页快照

安装phantomjs 官方建议使用2.0版本phantomjs-prebuilt
npm install phantomjs-prebuilt --save

//phantomjs生成图片代码
router.get('/', function (req, res, next) {
    var path = require('path')
    var childProcess = require('child_process')
    var phantomjs = require('phantomjs-prebuilt')
    var fs = require('fs')
    var binPath = phantomjs.path

    var childArgs = [
        path.join(__dirname, '../lib/rasterize.js'), //此文件为官方提供的示例文件,可生成图片或者pdf文件
        'http://www.baidu.com', //将要生成图片的网页地址
        './file.png'
    ]

    childProcess.execFile(binPath, childArgs, function (err, stdout, stderr) {
        // handle results
        if (err || stderr) {
            res.send(500, err || stderr);
            return;
        }

		res.sendFile(path.join(__dirname, './file.png'));
    })
})
//phantomjs将网页生成pdf代码
router.get('/', function (req, res, next) {
    var path = require('path')
    var childProcess = require('child_process')
    var phantomjs = require('phantomjs-prebuilt')
    var fs = require('fs')
    var binPath = phantomjs.path

	childProcess.execFile(binPath, [
			path.join(__dirname, '../lib/rasterize.js'),
			'http://www.baidu.com',
			'./file.pdf',
			'A4'
	], function (err, stdout, stderr) {
			// handle results
			if (err || stderr) {
					res.send(500, err || stderr);
					return;
			}

			res.sendFile(path.join(__dirname, './file.pdf'));
	})
})

phantomjs生成图片或者pdf代码

"use strict";
var page = require('webpage').create(),
    system = require('system'),
    address, output, size, pageWidth, pageHeight;


if (system.args.length < 3 || system.args.length > 5) {
    console.log('Usage: rasterize.js URL filename [paperwidth*paperheight|paperformat] [zoom]');
    console.log('  paper (pdf output) examples: "5in*7.5in", "10cm*20cm", "A4", "Letter"');
    console.log('  image (png/jpg output) examples: "1920px" entire page, window width 1920px');
    console.log('                                   "800px*600px" window, clipped to 800x600');
    phantom.exit(1);
} else {
    address = system.args[1];
    output = system.args[2];
    page.viewportSize = { width: 600, height: 600 };
    if (system.args.length > 3 && system.args[2].substr(-4) === ".pdf") {
        size = system.args[3].split('*');
        page.paperSize = size.length === 2 ? { width: size[0], height: size[1], margin: '0px' }
            : { format: system.args[3], orientation: 'portrait', margin: '0px' };

    } else if (system.args.length > 3 && system.args[3].substr(-2) === "px") {
        size = system.args[3].split('*');
        if (size.length === 2) {
            var pageWidth = parseInt(size[0], 10),
                pageHeight = parseInt(size[1], 10);
            page.viewportSize = { width: pageWidth, height: pageHeight };
            page.ddd = { top: 0, left: 0, width: pageWidth, height: pageHeight };
        } else {
            console.log("size:", system.args[3]);
            var pageWidth = parseInt(system.args[3], 10),
                pageHeight = parseInt(pageWidth * 3 / 4, 10); // it's as good an assumption as any
            console.log("pageHeight:", pageHeight);
            page.viewportSize = { width: pageWidth, height: pageHeight };
        }
    }
    if (system.args.length > 4) {
        page.zoomFactor = system.args[4];
    }
    page.settings.loadImages = true;
    page.settings.userAgent = 'Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.117 Safari/537.36';

    page.open(address, function (status) {
        if (status !== 'success') {
            console.log('Unable to load the address!');
            phantom.exit(1);
        } else {
            window.setTimeout(function () {
                page.render(output);
                phantom.exit();
            }, 200);
        }
    });
}

资料

webpage模块

webpage模块是PhantomJS的核心模块,用于网页操作。

var webPage = require('webpage');
var page = webPage.create();

上面代码表示加载PhantomJS的webpage模块,并创建一个实例。

下面是webpage实例的属性和方法介绍。

open()

open方法用于打开具体的网页。

var page = require('webpage').create();

page.open('http://slashdot.org', function (s) {
  console.log(s);
  phantom.exit();
});

上面代码中,open()方法,用于打开具体的网页。它接受两个参数。第一个参数是网页的网址,这里打开的是著名新闻网站Slashdot,第二个参数是回调函数,网页打开后该函数将会运行,它的参数是一个表示状态的字符串,如果打开成功就是success,否则就是fail。

注意,只要接收到服务器返回的结果,PhantomJS就会报告网页打开成功,而不管服务器是否返回404或500错误。

open方法默认使用GET方法,与服务器通信,但是也可以使用其他方法。

var webPage = require('webpage');
var page = webPage.create();
var postBody = 'user=username&password=password';

page.open('http://www.google.com/', 'POST', postBody, function(status) {
  console.log('Status: ' + status);
  // Do other things here...
});

上面代码中,使用POST方法向服务器发送数据。open方法的第二个参数用来指定HTTP方法,第三个参数用来指定该方法所要使用的数据。

open方法还允许提供配置对象,对HTTP请求进行更详细的配置。

var webPage = require('webpage');
var page = webPage.create();
var settings = {
  operation: "POST",
  encoding: "utf8",
  headers: {
    "Content-Type": "application/json"
  },
  data: JSON.stringify({
    some: "data",
    another: ["custom", "data"]
  })
};

page.open('http://your.custom.api', settings, function(status) {
  console.log('Status: ' + status);
  // Do other things here...
});

evaluate()

evaluate方法用于打开网页以后,在页面中执行JavaScript代码。

var page = require('webpage').create();

page.open(url, function(status) {
  var title = page.evaluate(function() {
    return document.title;
  });
  console.log('Page title is ' + title);
  phantom.exit();
});

网页内部的console语句,以及evaluate方法内部的console语句,默认不会显示在命令行。这时可以采用onConsoleMessage回调函数,上面的例子可以改写如下。

var page = require('webpage').create();

page.onConsoleMessage = function(msg) {
  console.log('Page title is ' + msg);
};

page.open(url, function(status) {
  page.evaluate(function() {
    console.log(document.title);
  });
  phantom.exit();
});

上面代码中,evaluate方法内部有console语句,默认不会输出在命令行。这时,可以用onConsoleMessage方法监听这个事件,进行处理。

includeJs()

includeJs方法用于页面加载外部脚本,加载结束后就调用指定的回调函数。

var page = require('webpage').create();
page.open('http://www.sample.com', function() {
  page.includeJs("http://path/to/jquery.min.js", function() {
    page.evaluate(function() {
      $("button").click();
    });
    phantom.exit()
  });
});

上面的例子在页面中注入jQuery脚本,然后点击所有的按钮。需要注意的是,由于是异步加载,所以phantom.exit()语句要放在page.includeJs()方法的回调函数之中,否则页面会过早退出。

render()

render方法用于将网页保存成图片,参数就是指定的文件名。该方法根据后缀名,将网页保存成不同的格式,目前支持PNG、GIF、JPEG和PDF。

var webPage = require('webpage');
var page = webPage.create();

page.viewportSize = { width: 1920, height: 1080 };
page.open("http://www.google.com", function start(status) {
  page.render('google_home.jpeg', {format: 'jpeg', quality: '100'});
  phantom.exit();
});

该方法还可以接受一个配置对象,format字段用于指定图片格式,quality字段用于指定图片质量,最小为0,最大为100。

viewportSize,zoomFactor

viewportSize属性指定浏览器视口的大小,即网页加载的初始浏览器窗口大小。

var webPage = require('webpage');
var page = webPage.create();

page.viewportSize = {
  width: 480,
  height: 800
};

viewportSize的Height字段必须指定,不可省略。

zoomFactor属性用来指定渲染时(render方法和renderBase64方法)页面的放大系数,默认是1(即100%)。

var webPage = require('webpage');
var page = webPage.create();

page.zoomFactor = 0.25;
page.render('capture.png');

onResourceRequested

onResourceRequested属性用来指定一个回调函数,当页面请求一个资源时,会触发这个回调函数。它的第一个参数是HTTP请求的元数据对象,第二个参数是发出的网络请求对象。

HTTP请求包括以下字段。

字段 说明
id 所请求资源的编号
method 使用的HTTP方法
url 所请求的资源 URL
time 一个包含请求时间的Date对象
headers HTTP头信息数组

网络请求对象包含以下方法

方法 说明
abort() 终止当前的网络请求,这会导致调用onResourceError回调函数。
changeUrl(newUrl) 改变当前网络请求的URL。
setHeader(key, value) 设置HTTP头信息。
var webPage = require('webpage');
var page = webPage.create();

page.onResourceRequested = function(requestData, networkRequest) {
  console.log('Request (#' + requestData.id + '): ' + JSON.stringify(requestData));
};

onResourceReceived

onResourceReceived属性用于指定一个回调函数,当网页收到所请求的资源时,就会执行该回调函数。它的参数就是服务器发来的HTTP回应的元数据对象,包括以下字段。

字段 说明
id 所请求的资源编号
url 所请求的资源的URL r- time:包含HTTP回应时间的Date对象
headers HTTP头信息数组
bodySize 解压缩后的收到的内容大小
contentType 接到的内容种类
redirectURL 重定向URL(如果有的话)
stage 对于多数据块的HTTP回应,头一个数据块为start,最后一个数据块为end。
status HTTP状态码,成功时为200。
statusText HTTP状态信息,比如OK。

如果HTTP回应非常大,分成多个数据块发送,onResourceReceived会在收到每个数据块时触发回调函数。

var webPage = require('webpage');
var page = webPage.create();

page.onResourceReceived = function(response) {
  console.log('Response (#' + response.id + ', stage "' + response.stage + '"): ' + JSON.stringify(response));
};

system模块

system模块可以加载操作系统变量,system.args就是参数数组。

var page = require('webpage').create(),
    system = require('system'),
    t, address;

// 如果命令行没有给出网址
if (system.args.length === 1) {
    console.log('Usage: page.js <some URL>');
    phantom.exit();
}

t = Date.now();
address = system.args[1];
page.open(address, function (status) {
    if (status !== 'success') {
        console.log('FAIL to load the address');
    } else {
        t = Date.now() - t;
        console.log('Loading time ' + t + ' ms');
    }
    phantom.exit();
});

使用方法如下:

$ phantomjs page.js http://www.google.com

职场加油站点滴分享,希望可以帮助到正在困惑的朋友!

    phantomjs selenium 如何动态修改代理?
    刚好碰见同样的需求,网上没有搜索到答案,粗略浏览了一下selenium对phantomjs的实现
    phantomJS和selenium如何爬取ajax跨域请求的网页?
    2、JSONP可以通过network查看JS找到,可以把获取到的内容进行反混淆解密,试试看是不是
    PhantomJS是否可以伪装成其他浏览器?
    在 page 对象的设置项里改变 userAgent 的值,代码如下(模拟 IE6.1 )var
    关于Selenium和PhantomJS抓取网页的问题?
    因为京东的评论内容是需要一次点击事件才能加载的a_list = browser.find_ele
    Phantomjs性能优化
    来自博客:Phantomjs性能优化 写过爬虫的朋友应该都用过一个无头浏览器–phantomjs