求助,为什么phantomjs 获取html无法获取到网页最终的数据

编程开发子分类下次自动登录
现在的位置:
& 综合 & 正文
数据抓取的艺术(一):Selenium+Phantomjs数据抓取环境配置
数据抓取是一门艺术,和其他软件不同,世界上不存在完美的、一致的、通用的抓取工具。为了不同的目的,需要定制不同的。不过,我们不必Start from Scratch,已经有许多的基本工具、基本方法和基础框架可供使用。不同的工具、不同的方法、不同的框架的特点也不同。了解这些工具、方法和框架是首要任务,接下来就需要明白它们的差异都在哪里、什么情境该用什么东东,最后才是析出规则、编写代码、运行来抓取数据。所以说,其实数据抓取的学习路线,不但很长而且很杂。
为了一个特定的目的,我需要爬取Google的搜索数,和其他情况不同:人家是特定关键词,一页一页地爬结果;我的是N多关键词,一次一个地搜索,只需要返回的搜索条数。事实上,一共有153个关键词,但每个关键词都需要和所有关键词握手组配成一对待检词组。于是,大家可以试想一下,一个153行、153列的大表格,每一个空白都等着填,这就将是153*153=23409次,也就是约04次,经测试每爬取一个共词页面的结果并存入Excel,需要花费4秒的时间。这意味着,以单人单线程的方式需要0=13个小时,才能跑完。
这些内容我还会在后续博文中详述,现在先介绍上述目的情境下我使用的技术框架及其安装配置过程。
一、技术架构
[Python2.7 + Pip + Selenium + Phantomjs]
Selenium+Phantomjs,最初这对兄弟不是一家的,后来发现二者志趣相投、互有好感,于是结拜为兄弟,住进了Selenium家里。(这种说法有待商榷)
看看介绍:
Selenium是一个用于Web应用程序测试的工具。Selenium测试直接运行在浏览器中,就像真正的用户在操作一样。支持的浏览器包括IE、Mozilla Firefox、Chrome等。
Phantom JS是一个服务器端的 JavaScript API 的 WebKit。其支持各种Web标准: DOM 处理, CSS 选择器, JSON, Canvas, 和 SVG。
二、环境搭建
(1)安装Python从略,我用的版本是2.7.4(WinXP和Win7的32位平台)。
(2)由于发现Pip比easy_install优秀,我便使用我已经安装的easy_install来安装pip。
easy_install pip
如图所示:
(3)安装Phantomjs。
到Phantomjs的官方网站http://phantomjs.org/download.html,下载“Download
MB) ”。随后打开这个压缩包,将phantomjs.exe这一个文件解压到系统路径所能找到的地方,由于之前我已经将“C:\Python27\Scripts”目录添加入PATH之中,所以我就直接解压到这个目录。如图所示:
至此,就已经在Win的环境下配置好了环境。
随意新建一个文件,并加入如下代码:
from selenium
import webdriver
driver = webdriver.PhantomJS()
driver.get('')
data = driver.find_element_by_id('cp').text
print data
Check Out,看看是不是这个结果:
其实,我一直不喜欢弹出来的DOS黑框,觉得这东东太影响视觉而且可能更耗时,但是在我看了官方DOC后:
我发现,是不能直接藏匿DOS弹框的。So,只好这般啦。
OK,it's time to "Enjoy yourself"......
也请参看下文:《》
&&&&推荐文章:
【上篇】【下篇】Posts - 91,
Articles - 0,
Comments - 391
09:30 by Justany_WhiteSnow, ... 阅读,
利用PhantomJS做网页截图经济适用,但其API较少,做其他功能就比较吃力了。例如,其自带的 Mongoose最高只能同时支持10个请求,指望他能独立成为一个服务是不怎么实际的。所以这里需要另一个语言来支撑服务,这里选用NodeJS来完成。
对源代码感兴趣的朋友可以在Github获取:
安装PhantomJS
首先,去下载对应平台的版本,或者下载源代码自行编译。然后将PhantomJS配置进环境变量,输入
$ phantomjs
如果有反应,那么就可以进行下一步了。
利用PhantomJS进行简单截图
var webpage = require('webpage')
, page = webpage.create();
page.viewportSize = { width: 1024, height: 800 };
page.clipRect = { top: 0, left: 0, width: 1024, height: 800 };
page.settings = {
javascriptEnabled: false,
loadImages: true,
userAgent: 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.31 (KHTML, like Gecko) PhantomJS/19.0'
page.open('', function (status) {
if (status === 'fail') {
console.log('open page fail!');
page.render('./snapshot/test.png');
// release the memory
page.close();
这里我们设置了窗口大小为1024 * 800:
page.viewportSize = { width: 1024, height: 800 };
截取从(0, 0)为起点的1024 * 800大小的图像:
page.clipRect = { top: 0, left: 0, width: 1024, height: 800 };
禁止Javascript,允许图片载入,并将userAgent改为"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.31 (KHTML, like Gecko) PhantomJS/19.0":
page.settings = {
javascriptEnabled: false,
loadImages: true,
userAgent: 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.31 (KHTML, like Gecko) PhantomJS/19.0'
然后利用page.open打开页面,最后截图输出到./snapshot/test.png中:
page.render('./snapshot/test.png');
NodeJS与PhantomJS通讯
我们先来看看PhantomJS能做什么通讯。
命令行传参
phantomjs snapshot.js
命令行传参只能在PhantomJS开启时进行传参,在运行过程中就无能为力了。
标准输出能从PhantomJS向NodeJS输出数据,但却没法从NodeJS传数据给PhantomJS。
不过测试中,标准输出是这几种方式传输最快的,在大量数据传输中应当考虑。
PhantomJS向NodeJS服务发出HTTP请求,然后NodeJS返回相应的数据。
这种方式很简单,但是请求只能由PhantomJS发出。
值得注意的是PhantomJS 1.9.0支持Websocket了,不过可惜是hixie-76 Websocket,不过毕竟还是提供了一种NodeJS主动向PhantomJS通讯的方案了。
测试中,我们发现PhantomJS连上本地的Websocket服务居然需要1秒左右,暂时不考虑这种方法吧。
phantomjs-node
phantomjs-node成功将PhantomJS作为NodeJS的一个模块来使用,但我们看看作者的原理解释:
I will answer that question with a question. How do you communicate with a process that doesn't support shared memory, sockets, FIFOs, or standard input?
Well, there's one thing PhantomJS does support, and that's opening webpages. In fact, it's really good at opening web pages. So we communicate with PhantomJS by spinning up an instance of ExpressJS, opening Phantom in a subprocess, and pointing it at a special webpage that turns socket.io messages into&alert()calls. Those&alert()&calls are picked up by Phantom and there you go!
The communication itself happens via James Halliday's fantastic&&library, which fortunately works well enough when combined with&&to run straight out of PhantomJS's pidgin Javascript environment.
实际上phantomjs-node使用的也是HTTP或者Websocket来进行通讯,不过其依赖庞大,我们只想做一个简单的东西,暂时还是不考虑这个东东吧。
让我们开始吧
我们在第一版中选用HTTP进行实现。
首先利用cluster进行简单的进程守护(index.js):
module.exports = (function () {
"use strict"
var cluster = require('cluster')
, fs = require('fs');
if(!fs.existsSync('./snapshot')) {
fs.mkdirSync('./snapshot');
if (cluster.isMaster) {
cluster.fork();
cluster.on('exit', function (worker) {
console.log('Worker' + worker.id + ' died :(');
process.nextTick(function () {
cluster.fork();
require('./extract.js');
然后利用connect做我们的对外API(extract.js):
module.exports = (function () {
"use strict"
var connect = require('connect')
, fs = require('fs')
, spawn = require('child_process').spawn
, jobMan = require('./lib/jobMan.js')
, bridge = require('./lib/bridge.js')
, pkg = JSON.parse(fs.readFileSync('./package.json'));
var app = connect()
.use(connect.logger('dev'))
.use('/snapshot', connect.static(__dirname + '/snapshot', { maxAge: pkg.maxAge }))
.use(connect.bodyParser())
.use('/bridge', bridge)
.use('/api', function (req, res, next) {
if (req.method !== "POST" || !req.body.campaignId) return next();
if (!req.body.urls || !req.body.urls.length) return jobMan.watch(req.body.campaignId, req, res, next);
var campaignId = req.body.campaignId
, imagesPath = './snapshot/' + campaignId + '/'
, urls = []
function _deal(id, url, imagePath) {
// just push into urls list
urls.push({
imagePath: imagePath
for (var i = req.body.urls. i--;) {
url = req.body.urls[i];
imagePath = imagesPath + i + '.png';
_deal(i, url, imagePath);
jobMan.register(campaignId, urls, req, res, next);
var snapshot = spawn('phantomjs', ['snapshot.js', campaignId]);
snapshot.stdout.on('data', function (data) {
console.log('stdout: ' + data);
snapshot.stderr.on('data', function (data) {
console.log('stderr: ' + data);
snapshot.on('close', function (code) {
console.log('snapshot exited with code ' + code);
.use(connect.static(__dirname + '/html', { maxAge: pkg.maxAge }))
.listen(pkg.port, function () { console.log('listen: ' + 'http://localhost:' + pkg.port); });
这里我们引用了两个模块bridge和jobMan。
其中bridge是HTTP通讯桥梁,jobMan是工作管理器。我们通过campaignId来对应一个job,然后将job和response委托给jobMan管理。然后启动PhantomJS进行处理。
通讯桥梁负责接受或者返回job的相关信息,并交给jobMan(bridge.js):
module.exports = (function () {
"use strict"
var jobMan = require('./jobMan.js')
, fs = require('fs')
, pkg = JSON.parse(fs.readFileSync('./package.json'));
return function (req, res, next) {
if (req.headers.secret !== pkg.secret) return next();
// Snapshot APP can post url information
if (req.method === "POST") {
var body = JSON.parse(JSON.stringify(req.body));
jobMan.fire(body);
res.end('');
// Snapshot APP can get the urls should extract
var urls = jobMan.getUrls(req.url.match(/campaignId=([^&]*)(\s|&|$)/)[1]);
res.writeHead(200, {'Content-Type': 'application/json'});
res.statuCode = 200;
res.end(JSON.stringify({ urls: urls }));
如果request method为POST,则我们认为PhantomJS正在给我们推送job的相关信息。而为GET时,则认为其要获取job的信息。
jobMan负责管理job,并发送目前得到的job信息通过response返回给client(jobMan.js):
module.exports = (function () {
"use strict"
var fs = require('fs')
, fetch = require('./fetch.js')
, _jobs = {};
function _send(campaignId){
var job = _jobs[campaignId];
if (!job) return;
if (job.waiting) {
job.waiting = false;
clearTimeout(job.timeout);
var finished = (job.urlsNum === job.finishNum)
, data = {
campaignId: campaignId,
urls: job.urls,
finished: finished
job.urls = [];
var res = job.
if (finished) {
_jobs[campaignId] = null;
delete _jobs[campaignId]
res.writeHead(200, {'Content-Type': 'application/json'});
res.statuCode = 200;
res.end(JSON.stringify(data));
function register(campaignId, urls, req, res, next) {
_jobs[campaignId] = {
urlsNum: urls.length,
finishNum: 0,
cacheUrls: urls,
res: null,
waiting: false,
timeout: null
watch(campaignId, req, res, next);
function watch(campaignId, req, res, next) {
_jobs[campaignId].res =
// 20s timeout
_jobs[campaignId].timeout = setTimeout(function () {
_send(campaignId);
}, 20000);
function fire(opts) {
var campaignId = opts.campaignId
, job = _jobs[campaignId]
, fetchObj = fetch(opts.html);
if (job) {
if (+opts.status && fetchObj.title) {
job.urls.push({
id: opts.id,
url: opts.url,
image: opts.image,
title: fetchObj.title,
description: fetchObj.description,
status: +opts.status
job.urls.push({
id: opts.id,
url: opts.url,
status: +opts.status
if (!job.waiting) {
job.waiting = true;
setTimeout(function () {
_send(campaignId);
job.finishNum ++;
console.log('job can not found!');
function getUrls(campaignId) {
var job = _jobs[campaignId];
if (job) return job.cacheU
register: register,
watch: watch,
fire: fire,
getUrls: getUrls
这里我们用到fetch对html进行抓取其title和description,fetch实现比较简单(fetch.js):
module.exports = (function () {
"use strict"
return function (html) {
if (!html) return { title: false, description: false };
var title = html.match(/\&title\&(.*?)\&\/title\&/)
, meta = html.match(/\&meta\s(.*?)\/?\&/g)
if (meta) {
for (var i = meta. i--;) {
if(meta[i].indexOf('name="description"') & -1 || meta[i].indexOf('name="Description"') & -1){
description = meta[i].match(/content\=\"(.*?)\"/)[1];
(title && title[1] !== '') ? (title = title[1]) : (title = 'No Title');
description || (description = 'No Description');
title: title,
description: description
最后是PhantomJS运行的源代码,其启动后通过HTTP向bridge获取job信息,然后每完成job的其中一个url就通过HTTP返回给bridge(snapshot.js):
var webpage = require('webpage')
, args = require('system').args
, fs = require('fs')
, campaignId = args[1]
, pkg = JSON.parse(fs.read('./package.json'));
function snapshot(id, url, imagePath) {
var page = webpage.create()
page.viewportSize = { width: 1024, height: 800 };
page.clipRect = { top: 0, left: 0, width: 1024, height: 800 };
page.settings = {
javascriptEnabled: false,
loadImages: true,
userAgent: 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.31 (KHTML, like Gecko) PhantomJS/1.9.0'
page.open(url, function (status) {
if (status === 'fail') {
'campaignId=',
campaignId,
encodeURIComponent(url),
'&status=',
].join('');
postPage.open('http://localhost:' + pkg.port + '/bridge', 'POST', data, function () {});
page.render(imagePath);
var html = page.
// callback NodeJS
'campaignId=',
campaignId,
encodeURIComponent(html),
encodeURIComponent(url),
'&image=',
encodeURIComponent(imagePath),
'&status=',
].join('');
postMan.post(data);
// release the memory
page.close();
var postMan = {
postPage: null,
posting: false,
datas: [],
currentNum: 0,
init: function (snapshot) {
var postPage = webpage.create();
postPage.customHeaders = {
'secret': pkg.secret
postPage.open('http://localhost:' + pkg.port + '/bridge?campaignId=' + campaignId, function () {
var urls = JSON.parse(postPage.plainText).urls
this.len = urls.
if (this.len) {
for (var i = this. i--;) {
url = urls[i];
snapshot(url.id, url.url, url.imagePath);
this.postPage = postP
post: function (data) {
this.datas.push(data);
if (!this.posting) {
this.posting = true;
this.fire();
fire: function () {
if (this.datas.length) {
var data = this.datas.shift()
, that = this;
this.postPage.open('http://localhost:' + pkg.port + '/bridge', 'POST', data, function () {
that.fire();
// kill child process
setTimeout(function () {
if (++this.currentNum === this.len) {
that.postPage.close();
phantom.exit();
this.posting = false;
postMan.init(snapshot);用phantomjs 进行网页整页截屏
用phantomjs 进行网页整页截屏
phantomjs 是一个基于js的webkit内核无头浏览器 也就是没有显示界面的浏览器,这样访问网页就省去了浏览器的界面绘制所消耗的系统资源,比较适合用于网络测试等应用 。利用这个工具,我们可以轻松的搭建一个接口用于获取我们想要的url的整页截屏。收起 本资料共包含以下附件: 用phantomjs 进行网页整页截屏.doc
(本资料免费下载)
本资料由网友发布,仅代表网友个人经验或观点,不代表本网站立场和观点。如果本资料侵犯了您的知识产权,请与我们取得联系,我们会及时处理。
&&&主编推荐
&&&&&&&&&&&&&&&
希赛网 版权所有 & &&求助,为什么phantomjs 无法获取到网页最终的数据_百度知道
求助,为什么phantomjs 无法获取到网页最终的数据
提问者采纳
,用来进行网页抓取是再好不过了;}之后只需要让js代码在phantomjs里跑起来即可~var page = require('
var c = document://www。比如我们要批量抓取网页 “历史上的今天” 的内容;i++){d=d+c[i].com/" target="_blank"><a href="http. }); i++){
d=d+c[i];).list li a&#39;).exit();
phantom.title+&#39;i&webpage&#39;success&#39;)var l = c。网站对dom结构的观察发现hantomjs因为是无头浏览器可以跑&#92;&#92;;for(var i =0.com/&#39;;l,在dos里面执行一下.evaluate(function () {
var d= &#39, function (status) { &#47。因此我们利用高级选择器构建dom片段var d= &#39;FAIL to load the address&#39.log(&#39.list li a的title值即可.)
var l =i&n&#39;&#39;
console.list li a&#39;打开页面
if (最终我们另存为
for(var i =0!== &#39.create();var c = document.n&#39;) {
console://www.title+&#39.querySelectorAll(&#39;l.querySelectorAll(&#39.log(page,我们只需要取到 ;&#47.&#39.open(&#39,所以同样可以跑dom节点
来自团队:
其他类似问题
为您推荐:
等待您来回答
下载知道APP
随时随地咨询
出门在外也不愁

我要回帖

更多关于 phantomjs 获取html 的文章

 

随机推荐