Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

使用phantomjs进行seo优化 #10

Open
shen1992 opened this issue Jan 31, 2018 · 0 comments
Open

使用phantomjs进行seo优化 #10

shen1992 opened this issue Jan 31, 2018 · 0 comments

Comments

@shen1992
Copy link
Owner

shen1992 commented Jan 31, 2018

目录

  • 1.前言
  • 2.初试phantomjs
  • 3.使用express
  • 4.配置nginx

1.前言

最近接了一个seo优化的需求,运用到了phantomjs,它可以说是一个没有ui的webkit浏览器,所以它可以抓取由ajax返回的数据所生成的dom,这是爬虫所需要的。总体优化的思路便是:配置nginx,判断如果是爬虫,则将请求转发到自己配置的web server服务器上,使用phantomjs抓取完整的html并且返回给爬虫。下面一步一步实现这个功能。

2.初试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跑解析任务
    resourceWaitTimer = setTimeout(capture, resourceWait)
  }
}

// 资源加载超时
page.onResourceTimout = function (req) {
  resourceCount--
}

// 资源加载失败
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)
  }
})
  • 注意onResourceReceived里面判断资源加载完毕是res.stage == 'end'
  • phantom.exit(params)不传入参数,默认返回0
  • 运行phantomjs spider.js 'http://yuedu.163.com/'

每次都要使用terminal,输入命令并指定url,这样显然不够通用,需要配置一个web server,动态抓取html。

3.使用express

./app.js

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')

  // 监听phantom的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!');
})
  • child_process.spawn()方法会异步地衍生子进程,并且不会阻塞node.js事件循环
  • 默认情况下,在node.js的父进程与衍生的子进程之间会建立stdout管道。数据能以非堵塞的方式在管道流通

4.配置nginx

新建一个phantom.conf文件

# 定义一个Nginx的upstream为spider_server
upstream spider_server {
  server localhost:3000;
}

server {
  listen       80;
  server_name  yuedu.163.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;

    # 当UA里面含有Baiduspider的时候,流量Nginx以反向代理的形式,将流量传递给spider_server
    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;
    }
  }
}
  • $http_user_agent即判断是否各大搜索引擎的爬虫
  • 把phantom.conf文件include到nginx.conf文件中即可
  • 切记include要放在http里面

./ nginx.conf

include phantom.conf
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant