nodejs抓取动态网页(前段时间前后端分离改造导致一个问题网站内容无法得到收录)

优采云 发布时间: 2022-01-18 01:01

  nodejs抓取动态网页(前段时间前后端分离改造导致一个问题网站内容无法得到收录)

  前段时间博客把vue的前后端分开,导致了一个问题,网站的内容无法获取到收录的搜索引擎。. . 这让我困扰了一段时间。由于工作繁忙,一直没有研究这个问题。最近在给老婆写爬虫脚本的时候,碰巧接触到了PhantomJS。突然意识到可以用PhantomJS来解决~

  PhantomJS 是一个可使用 JavaScript API 编写脚本的无头 WebKit。它具有 **fast ** 和 **native ** 对各种 Web 标准的支持:DOM 处理、CSS 选择器、JSON、Canvas 和 SVG。

  所以我用谷歌搜索,发现很多人都在使用这个解决方案。天哪,真的是太坑了。让我们来谈谈它。这种解决方案实际上是一种旁路机制。原理是通过Nginx配置搜索引擎的爬虫。请求被转发到节点服务器,完整的 HTML 由 PhantomJS 解析。废话不多说,直接上代码!

  准备一个 PhantomJS 任务脚本

  首先,我们需要一个名为 spider.js 的文件,用于 phantomjs 解析 网站。

  // spider.js

"use strict";

// 单个资源等待时间,避免资源加载后还需要加载其他资源

var resourceWait = 500;

var resourceWaitTimer;

// 最大等待时间

var maxWait = 5000;

var maxWaitTimer;

// 资源计数

var resourceCount = 0;

// PhantomJS WebPage模块

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

// NodeJS 系统模块

var system = require('system');

// 从CLI中获取第二个参数为目标URL

var url = system.args[1];

// 设置PhantomJS视窗大小

page.viewportSize = {

width: 1280,

height: 1014

};

// 获取镜像

var capture = function(errCode){

// 外部通过stdout获取页面内容

console.log(page.content);

// 清除计时器

clearTimeout(maxWaitTimer);

// 任务完成,正常退出

phantom.exit(errCode);

};

// 资源请求并计数

page.onResourceRequested = function(req){

resourceCount++;

clearTimeout(resourceWaitTimer);

};

// 资源加载完毕

page.onResourceReceived = function (res) {

// chunk模式的HTTP回包,会多次触发resourceReceived事件,需要判断资源是否已经end

if (res.stage !== 'end'){

return;

}

resourceCount--;

if (resourceCount === 0){

// 当页面中全部资源都加载完毕后,截取当前渲染出来的html

// 由于onResourceReceived在资源加载完毕就立即被调用了,我们需要给一些时间让JS跑解析任务

// 这里默认预留500毫秒

resourceWaitTimer = setTimeout(capture, resourceWait);

}

};

// 资源加载超时

page.onResourceTimeout = function(req){

resouceCount--;

};

// 资源加载失败

page.onResourceError = function(err){

resourceCount--;

};

// 打开页面

page.open(url, function (status) {

if (status !== 'success') {

phantom.exit(1);

} else {

// 当改页面的初始html返回成功后,开启定时器

// 当到达最大时间(默认5秒)的时候,截取那一时刻渲染出来的html

maxWaitTimer = setTimeout(function(){

capture(2);

}, maxWait);

}

});

  运行一下吧~

  $ phantomjs spider.js ''

  您可以在终端中看到呈现的 HTML 结构!惊人的!

  订购服务

  为了响应搜索引擎爬虫的请求,我们需要提供这个命令,并通过 node.js 创建一个简单的 web 服务。

  // server.js

// ExpressJS调用方式

var express = require('express');

var app = express();

// 引入NodeJS的子进程模块

var child_process = require('child_process');

app.get('*', function(req, res){

// 完整URL

var url = req.protocol + '://'+ req.hostname + req.originalUrl;

// 预渲染后的页面字符串容器

var content = '';

// 开启一个phantomjs子进程

var phantom = child_process.spawn('phantomjs', ['spider.js', url]);

// 设置stdout字符编码

phantom.stdout.setEncoding('utf8');

// *敏*感*词*phantomjs的stdout,并拼接起来

phantom.stdout.on('data', function(data){

content += data.toString();

});

// *敏*感*词*子进程退出事件

phantom.on('exit', function(code){

switch (code){

case 1:

console.log('加载失败');

res.send('加载失败');

break;

case 2:

console.log('加载超时: '+ url);

res.send(content);

break;

default:

res.send(content);

break;

}

});

});

app.listen(3000, function () {

console.log('Spider app listening on port 3000!');

});

  运行节点 server.js。此时,我们有一个预渲染的 Web 服务。接下来的工作就是将搜索引擎爬虫的请求转发给这个web服务,最后将渲染结果返回给爬虫。

  为了防止node进程挂起,可以使用nohup启动,nohup node server.js &。

  通过Nginx的配置,我们可以轻松解决这个问题。

  upstream spider_server {

server localhost:3000;

}

server {

listen 80;

server_name example.com;

location / {

proxy_set_header Host $host:$proxy_port;

proxy_set_header X-Real-IP $remote_addr;

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

if ($http_user_agent ~* "Baiduspider|twitterbot|facebookexternalhit|rogerbot|linkedinbot|embedly|quora link preview|showyoubot|outbrain|pinterest|slackbot|vkShare|W3C_Validator|bingbot|Sosospider|Sogou Pic Spider|Googlebot|360Spider") {

proxy_pass http://spider_server;

}

}

}

  你完成了!

  注:本文代码主要参考以下文章的分享,稍作修改后可用的版本,谢谢分享!

  参考链接:

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线