如何pipe理PhantomJS实例的“池”

我正在计划一个webservice供我自己使用内部使用一个参数,一个URL,并返回表示从该URL parsing的 DOM的HTML。 通过解决我的意思是,Web服务将首先获得该网页,然后使用PhantomJS'呈现'页面,然后返回所有的DHTML,AJAX调用等执行后产生的源。 然而,基于每个请求(我现在正在做的)启动幻影的方式太慢了。 我宁愿有一个PhantomJS实例池,总是有一个服务于我的web服务的最新调用。

以前有没有做过这方面的工作? 我宁愿将这个web服务基于其他人的工作,而不是从头开始为自己写一个池pipe理器/ http代理服务器。

更多的上下文 :我已经列出了目前为止我见过的两个类似的项目,以及为什么我避免了每个项目,结果导致这个关于pipe理一个PhantomJS实例池的问题。

jsdom – 从我看到它有很好的function来执行页面上的脚本,但它不会尝试复制浏览器的行为,所以如果我使用它作为一个通用的“DOMparsing器”,最终会成为很多额外的编码来处理各种边界情况,事件调用等。我看到的第一个例子是手动调用我使用节点设置的testing应用程序的body标签的onload()函数。 这似乎是一个深刻的兔子洞的开始。

Selenium – 它只有很多更多的移动部件,因此设置一个池来pipe理长期浏览器实例将比使用PhantomJS更复杂。 我不需要它的任何macros录制/脚本的好处。 我只是想要一个web服务,就像获取网页和parsing它的DOM一样,就好像我正在用浏览器浏览这个URL(或者如果我可以让它忽略图像等甚至更快)。

我设置了一个PhantomJs云服务,它几乎做你在问什么。 我花了大约5个星期的工作。

您遇到的最大的问题是PhantomJs中已知的内存泄漏问题。 我的工作方式是每50次调用一次我的实例。

你遇到的第二大问题是每个页面的处理是非常CPU和内存密集型的,所以你只能运行每个CPU 4个左右的实例。

你碰到的第三大问题是PhantomJs在页面结束事件和redirect方面非常古怪。 你会被告知,你的网页是完成渲染之前,实际上。 有很多方法可以解决这个问题 ,但不幸的是没有任何“标准”。

你将不得不面对的第四个问题是nodejs和phantomjs之间的互操作,幸好有很多npm包处理这个问题可供select。

所以我知道我有偏见(正如我写的解决scheme,我要build议),但我build议你检查PhantomJsCloud.com是免费的光使用。

2015年1月更新:我碰到的另一个(第5个?)大问题是如何从pipe理器/负载平衡器发送请求/响应。 最初我使用的是PhantomJS内置的HTTP服务器,但是仍然遇到它的限制,特别是在最大响应大小方面。 我最终将本地文件系统的请求/响应写为通信线路。 花在实施这项服务上的总时间可能是20个人周,也许是1000个小时的工作。 *和FYI我正在做一个完整的重写下一个版本….(进行中)

asynchronousJavaScript库在Node中工作,并具有一个queuefunction,对于这种事情非常方便:

queue(worker, concurrency)

用指定的并发创build一个队列对象。 添加到队列中的任务将并行处理(达到并发限制)。 如果所有员工都在进行中,那么这个任务就会排队,直到有一个员工可用。 一旦工作人员完成任务,任务的callback被调用。

一些伪代码:

 function getSourceViaPhantomJs(url, callback) { var resultingHtml = someMagicPhantomJsStuff(url); callback(null, resultingHtml); } var q = async.queue(function (task, callback) { // delegate to a function that should call callback when it's done // with (err, resultingHtml) as parameters getSourceViaPhantomJs(task.url, callback); }, 5); // up to 5 PhantomJS calls at a time app.get('/some/url', function(req, res) { q.push({url: params['url_to_scrape']}, function (err, results) { res.end(results); }); }); 

在项目的自述文件中查看queue的整个文档 。

对于我的硕士论文,我开发了图书馆phantomjs-pool ,正是这个。 它允许提供映射到PhantomJS工作者的工作。 该库处理工作分配,通信,error handling,日志logging,重新启动和更多的东西。 该图书馆已成功用于抓取超过一百万页。

例:

以下代码执行Googlesearch的数字0到9,并将该页面的屏幕截图保存为googleX.png 。 四个网站被并行抓取(由于创build了四个工作人员)。 脚本通过node master.js启动。

master.js (在Node.js环境中运行)

 var Pool = require('phantomjs-pool').Pool; var pool = new Pool({ // create a pool numWorkers : 4, // with 4 workers jobCallback : jobCallback, workerFile : __dirname + '/worker.js', // location of the worker file phantomjsBinary : __dirname + '/path/to/phantomjs_binary' // either provide the location of the binary or install phantomjs or phantomjs2 (via npm) }); pool.start(); function jobCallback(job, worker, index) { // called to create a single job if (index < 10) { // index is count up for each job automatically job(index, function(err) { // create the job with index as data console.log('DONE: ' + index); // log that the job was done }); } else { job(null); // no more jobs } } 

worker.js (在PhantomJS环境中运行)

 var webpage = require('webpage'); module.exports = function(data, done, worker) { // data provided by the master var page = webpage.create(); // search for the given data (which contains the index number) and save a screenshot page.open('https://www.google.com/search?q=' + data, function() { page.render('google' + data + '.png'); done(); // signal that the job was executed }); }; 

作为@JasonS伟大的答案的替代scheme,你可以尝试我build立的PhearJS 。 PhearJS是一个在NodeJS中为PhantomJS实例编写的主pipe,通过HTTP提供一个API。 它可以从Github公开获得。

如果您使用的是nodejs,为什么不使用selenium-webdriver

  1. 运行一些phantomjs实例作为webdriver phantomjs --webdriver=port_number
  2. 为每个phantomjs实例创buildPhantomInstance

     function PhantomInstance(port) { this.port = port; } PhantomInstance.prototype.getDriver = function() { var self = this; var driver = new webdriver.Builder() .forBrowser('phantomjs') .usingServer('http://localhost:'+self.port) .build(); return driver; } 

    并把它们全部放在一个数组[phantomInstance1,phantomInstance2]

  3. 创build从数组中获得免费的phantomInstance的dispather.js

     var driver = phantomInstance.getDriver(); 

如果您使用的是nodejs,那么您可以使用https://github.com/sgentle/phantomjs-node ,这将允许您将任意数量的phantomjs进程连接到主要的NodeJS进程,因此可以使用async.js和很多节点的好东西。