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

SEO 优化,旁路服务处理 AJAX 数据 #70

Open
YIXUNFE opened this issue Apr 10, 2016 · 7 comments
Open

SEO 优化,旁路服务处理 AJAX 数据 #70

YIXUNFE opened this issue Apr 10, 2016 · 7 comments

Comments

@YIXUNFE
Copy link
Owner

YIXUNFE commented Apr 10, 2016

SEO 优化,旁路服务处理 AJAX 数据

平时接触 SEO 不算多,但是公司最近挺重视 SEO 的,因为我们除了商品,还有内容频道。这个内容频道叫易启玩,上面有许许多多的关于测评、导购、晒单等等的文章。公司希望这些文章能给我们带来用户,就特意为我们招聘了一位 SEO 专员。

专员上来的第一件事,当然是纠正我们过去犯下的 SEO 禁忌。这里面有一条,就是 ajax 接口方式获取数据。


ajax 接口方式 与 源码输出方式

因为大多数爬虫不会运行 JS(据说谷歌的会),它只能分析服务器给到的源码,即我们在浏览器右键查看源码得到的内容,这种我们通常成为源码输出方式。所以我们在项目中,需要把用 ajax 接口方式获取的数据嵌在源码中输出。

比如一个列表,本身为了照顾用户体验,分页通过 ajax 拉取数据,这样可以实现无跳转刷新。但是这对于爬虫来说,就拿到什么信息了。于是改成降低体验,将 ajax 接口方式换成源码输出方式,数据直接嵌在在服务器返回的源码中才行。


如何偷懒

偷懒的思路是从网上看来的,简明扼要的概括下就是:

既然蜘蛛不会执行 JS,那么由我们服务器帮助蜘蛛执行 JS 后返回网页内容。

这样我们只需要启用实现一套方案,帮助我们将客户端运行的代码返回给蜘蛛就可以了。省去了对现有项目中的 ajax 接口方式输出数据的排查与更改。


施工方案

那么这套方案怎么设计呢?关键就在于 PhantomJs。

一,判断请求来源

一般蜘蛛的请求来源都有特殊的 UA 加以区别,比如百度的 Baiduspider,我们可以通过 UA 判断请求是否来自搜索引擎厂商的蜘蛛。

二,配置代理

在接入层将蜘蛛的请求代理向与正常用户不同的访问地址,即一个旁路服务。

三,获取客户端运行脚本后的网页内容

在旁路服务中,由后端程序调用无头浏览器 PhantomJs 打开请求的 URL。页面加载后会自行执行 JS,当 ajax 数据拉取成功后,将网页内容返回给蜘蛛。那么如何判断页面执行 ajax 拉取数据成功?下面会和大家探讨。

666


疑难问题

敏感数据不想被爬虫抓取?

有些数据是敏感数据,比如登录用户的账户信息等。这当然不能直接给蜘蛛了,生成了快照可怎么办?这里想了个办法,就是对敏感数据在源码的数据容器 DOM 上加一个特殊的 class,比如 privacy。在 PhantomJs 返回数据之前将带有 privacy 的整个 DOM 字符串从内容中去除。

如何判断 ajax 拉取数据完成?

有一个方案是参考文献中提出的计算所有的资源请求数,在所有请求完成后,延迟 500ms 用于网页渲染,然后捕获网页代码。这种方案最大的好处是无需对业务逻辑做修改,但是人为设定的 500ms 延迟是否最优有待商榷。

// 资源请求并计数
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){
    resourceCount--;
};

// 资源加载失败
page.onResourceError = function(err){
    resourceCount--;
};

接入层如何设置代理?

# 定义一个Nginxupstreamspider_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;
  }
}

相关项目

https://github.com/YIXUNFE/optimize_ajax_for_seo


参考文献


Thanks


@renaesop
Copy link

renaesop commented Apr 17, 2016

有个疑问,基于phantomJS或者说prerender(也是phantomjs)来做所谓的seo, 一方面,的确是让爬虫可以获取到数据了,但是另一方面,phantomjs 是有开销的, 渲染页面也需要时间, 而且还不短。 众所周知, 网站的速度也是seo的一部分,这样做的话,会不会比传统的后端页面直接渲染在seo上落后很多?

@YIXUNFE
Copy link
Owner Author

YIXUNFE commented Apr 18, 2016

@renaesop 撇开 SEO 中速度与数据完整性两者的权衡,仅从 PhantomJS 的性能来看确实比后端页面直接渲染上要慢一点,我想主要有两点原因:

  • 页面多个异步请求的往返时间开销;
  • 等待页面渲染数据至 HTML 的延时开销(代码里预设 500ms)。

但是处理这个性能问题其实很简单,就是预处理页面。预处理方案有很多,比如通过 PhantomJS 设计一个爬虫,以一星期为周期爬去页面 URL 对应的静态 HTML 内容并记录在缓存应用中或者直接存在数据库。爬虫来请求的时候可以尝试匹配一下 URL,已经爬过的直接返回就可以。

这样就可以避免上述的两个时间开销。

@renaesop
Copy link

@YIXUNFE 关于预处理页面,其实也有想到过。但是这个爬虫并不是那么好做的,首先,除了功能比较复杂以外,爬虫的周期是很难确定的,在最糟糕的情况下,搜索引擎的延迟会和自己的延迟叠加到一起,延迟也是对seo有不良影响的,而如果频率设置过高又回徒增系统的负载;其次,系统复杂度本身已经大大增加了,不仅仅是爬虫本身需要监控,爬虫缓存下的结果也是需要监控的;最后,这个“爬虫”会执行js代码,这就意味着各种统计代码,埋点代码,都会受到一定的影响。

当然,爬虫方案也有一个优点,就是……自动拥有了全站的健康度检测……

@YIXUNFE
Copy link
Owner Author

YIXUNFE commented Apr 18, 2016

@renaesop

爬虫的爬取周期一般就是网站的更新周期,所以如果有固定更新窗口的话,就可以按窗口日期进行设置,比如我们每周二、周四是发布窗口,即意味着我们可能会倾向于周二周四(夜深人静时)执行爬取任务。而对于高频产生的内容,如果没有及时预爬取,可以降级为执行原来的 PhantomJS 的任务。

在爬取频率上应(nao)该(bu)是不会对性能造成影响的,一个标准爬虫是有一个 URL 池存放已经爬过的页面的。所以对于整站的爬取,每个页面其实也仅仅是增加一个PV/UV。

统计代码和埋点代码上可以尝试通过特殊 UA 解决。如果 BI 无法区分的话,如上所述,每个页面其实也仅仅是增加一个PV/UV。

@renaesop
Copy link

== 有发布窗口好幸福

@YIXUNFE
Copy link
Owner Author

YIXUNFE commented Apr 18, 2016

@renaesop

强制规范一下 😄

@libo1106
Copy link

别想那么复杂,搜索引擎爬虫都是很温柔的。

平均一个页面饭桶1、2秒能跑出来。

对爬虫来说,5秒10秒才出来的那种才叫慢,才会对 SEO 有影响。

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

No branches or pull requests

3 participants