使用puppeteer, 以无侵入方式给vue项目增强SEO能力

1,036 阅读3分钟

适用场景

  1. 已经用vue,完成了项目开发,但想增强SEO能力的场景

使用约束

  1. 该方案仅适用于针对特定爬虫程序的SEO增强。
  2. 因为是基于puppeteer,而puppeteer实际内部使用的是Chromium,所以,资源占用会比较多,要注意防止,被大规模访问时的相关问题

vue项目为什么seo能力不行?

因为vue项目所有用户看到的界面元素都是在浏览器渲染之后才有的。而爬虫程序,如果只是对网页源码进行收录,那将毫无意义,因为没有通过浏览器渲染的html源码,毫无价值。

原理

image.png

简单测试下puppeteer

import puppeteer from "puppeteer";

(async () => {
  // 禁止发出请求的资源类型
  const disabled_resource_type_arr = [
    "stylesheet",
    "image",
    "font",
    "fetch",
    "media",
  ];
  const browser = await puppeteer.launch({
    args: [
      "--disable-gpu",
      "--disable-dev-shm-usage",
      "--disable-setuid-sandbox",
      "--no-first-run",
      "--no-sandbox",
      "--no-zygote",
      "--single-process",
      "--blink-settings=imagesEnabled=false",
    ],
  });
  const page = await browser.newPage();
  // 这里是将web控制台的信息打印到终端(可用于调试)
  // page.on("console", (msg) => console.log("PAGE LOG:", msg.text));
  // 启用拦截器,仅加载html和js即可
  await page.setRequestInterception(true);
  page.on("request", (interceptedRequest) => {
    // console.log(
    //   interceptedRequest.resourceType(),
    //   interceptedRequest.isInterceptResolutionHandled(),
    //   interceptedRequest.url()
    // );
    if (interceptedRequest.isInterceptResolutionHandled()) return;
    // 禁止所有:图片/css/字体/视频/音频的加载
    if (
      disabled_resource_type_arr.indexOf(interceptedRequest.resourceType()) >= 0
    )
      interceptedRequest.abort();
    else interceptedRequest.continue();
  });
  // 从浏览器查看源码对比这里获取到的html内容,看看有什么区别
  await page.goto("https://free_pan.gitee.io/archetype-backend-template", {
    // 关于 waitUtil 官方说明: https://pptr.nodejs.cn/api/puppeteer.puppeteerlifecycleevent
    // 等到页内网络请求少于等于2个,且至少持续500ms时,则认为页面加载完毕
    waitUtil: "networkidle2",
  });
  // 生成网页截图,可用于判断拦截器是否生效效果
  await page.screenshot({ path: "screenshot-12138.png", fullPage: false });
  // 获取网页的完整html字符串
  const htmlContent = await page.content();
  // console.log(htmlContent);
  await browser.close();
})();

完整puppeteer服务代码

free pan/puppeteer-seo-service (gitee.com)

nginx配置

有了puppeteer服务之后,还需要nginx协助,所有请求在经过nginx时, nginx判断该请求是否爬虫发出的请求,如果是,则转发到puppeteer服务, 否则,按正常方式处理

以下配置方式任选一种即可,作用是通过UserAgent判断,当前请求是否为爬虫程序发出

配置方式一

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") {
      # 当浏览器的 UserAgent 包含如上的部分内容时,则认为是爬虫程序,因此将请求转发给`puppeteer`服务
      proxy_pass  http://192.168.1.111:3000;
    }
    
    # 非爬虫程序,走正常方式

    alias /usr/share/nginx/html/;
    index  index.html index.htm;
    try_files $uri $uri/ /index.html;
}

配置方式二

http {
  map $http_user_agent $is_bot {
    default 0;
    ~*bot 1;
    ~*spider 1;
    ~*crawl 1;
    ~*Googlebot 1;
  }

  server {
    listen 80;
    server_name your-domain.com;

    location / {
      if ($is_bot) {
        proxy_pass http://127.0.0.1:4000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      }
      try_files $uri $uri/ /index.html;
    }
  }
}

配置方式三

server {
        listen       80;
        server_name  localhost;

        location / {
            root   /usr/local/etc/nginx/html;
            index  index.html index.htm;

            set $prerender 0;
            if ($http_user_agent ~* "googlebot|bingbot|yandex|baiduspider|twitterbot|facebookexternalhit|rogerbot|linkedinbot|embedly|quora link preview|showyoubot|outbrain|pinterest/0.|pinterestbot|slackbot|vkShare|W3C_Validator") {
                set $prerender 1;
            }
            if ($args ~ "_escaped_fragment_") {
                set $prerender 1;
            }
            if ($http_user_agent ~ "Prerender") {
                set $prerender 0;
            }
            if ($uri ~* ".(js|css|xml|less|png|jpg|jpeg|gif|pdf|doc|txt|ico|rss|zip|mp3|rar|exe|wmv|doc|avi|ppt|mpg|mpeg|tif|wav|mov|psd|ai|xls|mp4|m4a|swf|dat|dmg|iso|flv|m4v|torrent|ttf|woff|svg|eot)") {
                set $prerender 0;
            }

            if ($prerender = 1) {
                set $prerender "0.0.0.0:10001";
                rewrite .* /$scheme://$host$request_uri? break;
                proxy_pass http://$prerender;
            }
        }
   }

配置完成之后,记得先测试是否配置正确nginx -t, 配置正确之后,再重启 nginx -s reload

给了爬虫不同的页面,会不会被认为是网页作弊行为呢?

google认为,只要与原始网页差异不大,就不算作弊

google的回复

动态渲染作为解决方法 |Google 搜索中心 |文档 |面向开发者的 Google

Google Web Search 的垃圾邮件政策 |Google 搜索中心 |文档 |面向开发者的 Google

参考资料

SPA 的 SEO 方案对比、最终实践 - 掘金 (juejin.cn)

Vue3+Vite使用Puppeteer进行SEO优化(SSR+Meta)_vue3怎么解决seo引擎-CSDN博客

巧用Puppeteer解决SEO问题 - 掘金 (juejin.cn)

puppeteer调试方法 - 掘金 (juejin.cn)

Prerender(JavaScript 网站静态化) - 知乎 (zhihu.com)

Vue3+Vite使用Puppeteer进行SEO优化(SSR+Meta)_vue3怎么解决seo引擎-CSDN博客

Puppeteer 截图及相关问题 - 刘哇勇 - 博客园 (cnblogs.com)

Gitee 极速下载/prerender - 码云 - 开源中国

Centos7 yum安装Chrome浏览器 - ianduin - 博客园 (cnblogs.com)

javascript - 在node中import from引入的文件要跟.js后缀,但是webapck不用? - SegmentFault 思否

巧用Puppeteer解决SEO问题 - 掘金 (juejin.cn)

1907 - Prerender入门实践 | zhongxia (zhongxia245.github.io)

vue无需改动代码的SEO优化【百度收录问题】优化--puppeteer(详细流程)_vue百度不收录-CSDN博客