文档章节

用PhantomJS来给AJAX站点做SEO优化

壹峰
 壹峰
发布于 2017/02/04 23:17
字数 1302
阅读 24
收藏 0

转 https://www.mxgw.info/t/phantomjs-prerender-for-seo.html

 

腾讯问卷所有动态内容,全部由Ajax接口提供。

众所周知,大部分的搜索引擎爬虫都不会执行JS,也就是说,如果页面内容由Ajax返回的话,搜索引擎是爬取不到部分内容的,也就无从做SEO了。

先来看看效果

QQ20160321-1

去年一整年,搜索引擎收录都少得可怜。

更致命的是,被收录的页面,其搜索引擎里面显示的标题是最原始的html标题,权重如此高的地方,却被收录了一个没什么用的标题。

在去年年底完成实施了预渲染服务后,收录量蹭蹭蹭的起来了,并且收录的标题也都全部正常了。

而这所有的一切,除了Nginx接入层的配置是需要改动业务代码外,其他全部都是旁路机制。也就是说,自己做一套,可以给所有同类型业务共用,同时不会影响现有业务的任何代码任何流程。

 

PhantomJS来解围

Ajax无法做SEO这个问题,困扰了我很久,后来发现PhantomJS这东西能在服务端解析HTML,瞬间这个问题不再是问题。

PhantomJS is a headless WebKit scriptable with a JavaScript API. It has fast andnativesupport for various web standards: DOM handling, CSS selector, JSON, Canvas, and SVG.

准备一个PhantomJS任务脚本

这里我命名为spider.js。

  /*global phantom*/
  "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);
   
  }
   
  });
view raw spider.js hosted with  by  GitHub

通过PhantomJS命令直接执行即可在终端中看到渲染后的html结构

 

命令服务化

什么意思呢,因为上面是一个命令,没法很好的响应搜索引擎爬虫的请求,估我们要把他服务化。

PhantomJS自带一个Web Server Module,但总是不稳定,如前面文章所说时不时会假死。

我们就通过Node给他起一个简单的Web服务。

  // 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;
  }
  });
   
  });
view raw express_spider.js hosted with  by  GitHub

旁路服务

我们现在已经有了一个能跑预渲染的Web服务了,剩下就是要将搜索引擎爬虫的流量导入到这个预渲染的服务中,同时把结果再返回给搜索引擎爬虫。

我们使用Nginx这个接入层利器即可轻松解决这个问题。

  # 定义一个Nginx的upstream为spider_server
  upstream spider_server {
  server localhost:3000;
  }
   
  # 指定一个范围,默认 / 表示全部请求
  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;
   
  # 当UA里面含有Baiduspider的时候,流量Nginx以反向代理的形式,将流量传递给spider_server
  if ($http_user_agent ~* "Baiduspider") {
  proxy_pass http://spider_server;
  }
  }
view raw nginx_spider.conf hosted with  by  GitHub

这个栗子里面仅仅对百度爬虫做了处理,可以自行把爬虫都补完整。

Free

说了这么多,我突然觉得这篇文章非常值钱。

因为,国外也有专门的服务端预渲染服务了,但他们统统要收费。

你可以根据本文的思路,自行部署一套旁路渲染服务。

附上一份新鲜收集的爬虫UA列表

  • 360 【文档】
    • 360Spider
    • HaoSouSpider
    • 360Spider-Image
    • 360Spider-Video
  • Baidu 【文档】
    • Baiduspider
  • Google 【文档】
    • Googlebot
    • Googlebot-News
    • Googlebot-Video
    • Googlebot-Mobile
  • Sogou 【文档】
    • Sogou web spider
    • Sogou inst spider
    • Sogou Spider
  • Bingbot【文档
    • bingbot
    • msnbot
    • msnbot-media
    • adidxbot
    • BingPreview

本文转载自:http://huqiji.iteye.com/blog/2325810

上一篇: css 垂直居中
下一篇: input 提示文字
壹峰
粉丝 9
博文 595
码字总数 9582
作品 0
广州
其他
私信 提问
使用 Node.js 实现的网页抓取

现今,网页抓取已经是一种人所共知的技术了,然而依然存在着诸多复杂性, 简单的网页爬虫依然难以胜任Ajax轮训、XMLHttpRequest,WebSockets,Flash Sockets等各种复杂技术所开发出来的现代化...

oschina
2012/12/30
16.1K
2
聊聊phantomjs的优化措施

序 本文主要小结一下phantomjs的优化措施 phantomjs phantomjs相当于一个后台浏览器,有点内嵌jetty的味道,通常在自动化测试或者爬虫领域用。 优化点 池化技术,避免重复启动 对于其他语言进...

go4it
2017/11/07
77
0
基于 PhantomJS + Node + Express + VueJS 1.x 的服务端渲染实践

项目地址:github.com/jrainlau/vu… 随着Vue 2.0的发布,服务端渲染一度成为了它的热卖点。在此之前,单页应用的首屏加载时长和SEO的问题,一直困扰着开发者们,也在一定程度上制约着前端框...

Jrain
2018/12/26
0
0
如何在无头模式下运行WebDriver?

如何在无头模式下运行WebDriver?如果您的CI工具(例如Jenkins)不支持UI,则可能需要这样做。 在无头模式下运行WebDriver自动测试在测试执行速度和更轻松地集成到CI管道方面具有优势。 下面...

程序猿拿Q
2018/11/26
152
0
ubuntu 安装phantomjs、chromedriver、geckodriver

一、安装phantomjs 1.从官网下载http://phantomjs.org/download.html 2.加压 tar -zxvf phantomjs-2.1.1-linux-x86_64.tar.bz2 3.mv phantomjs-2.1.1-linux-x86_64 /usr/local/share/phanto......

littlemesieV
2018/04/18
314
0

没有更多内容

加载失败,请刷新页面

加载更多

从0开始学FreeRTOS-(列表&列表项)-6

FreeRTOS列表&列表项的源码解读 第一次看列表与列表项的时候,感觉很像是链表,虽然我自己的链表也不太会,但是就是感觉很像。 在FreeRTOS中,列表与列表项使用得非常多,是FreeRTOS的一个数...

杰杰1号
30分钟前
4
0
Java的23种设计模式,详细讲解(一)

一、概述 设计模式是解决问题的方案,学习现有的设计模式可以做到经验复用。 拥有设计模式词汇,在沟通时就能用更少的词汇来讨论,并且不需要了解底层细节。 二、创建型 1. 单例(Singleton...

李红欧巴
46分钟前
5
0
android 使用asynctask结合fragment更新UI(另附线程池管理示例)

https://blog.csdn.net/qq_16064871/article/details/70767949

shzwork
46分钟前
3
0
SpringCloud实现分库分表模式下,数据库实时扩容方案

本文源码:GitHub·点这里 || GitEE·点这里 一、项目结构 1、工程结构 2、模块命名 shard-common-entity: 公共代码块shard-open-inte: 开放接口管理shard-eureka-7001: ...

知了一笑
47分钟前
5
0
js--时间切割装换工具类

<!DOCTYPE html><html><head> <meta charset="UTF-8"> <title></title> <script type="text/javascript"> /* * 修改data原型对象Format方法 ......

zhengzhixiang
57分钟前
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部