前端SEO优化思路一二

背景

虽然,现在大家对于搜索引擎优化没有很多的需求,但是搜索引擎入口还是一个流量比较多的入口。 所有大部分网站并没有放弃这个入口,并且会为此做优化。现在前端技术发展到今天,现阶段出现了各种渲染(前端渲染指的是如何生成网页代码结构)的技术手段。 如果你的网站是CSR(客户端渲染),那么搜索引擎爬虫在爬到你的时候,就会一脸懵逼,因为他只看到一个空的DIV结构,看不到任何可以收录的有效文字。 所以,我们来聊聊,关于前端SEO上面的一些如何静态化的思路。

目标

为了能让搜索引擎爬虫正确抓取识别到网页内容。

思路

全站静态化

首先,想到的就是全站静态化,也就是编写好URL规则,使用无头浏览器直接遍历所有的地址并且生成静态网页保存到硬盘上,最后将整个文件夹部署到服务器上的一种手段。 我们最常见的就是一些博客系统,他们大部分都采用这种技术架构。在本地写博客,在本地编译,最后发布到线上。 这个的优势是线上部署十分简单并且稳固。劣势也十分明显,每次都需要进行编译部署。不过这对于一些小的站点来说,完全不是问题。

我这里简单的写一个无头浏览器生成本地静态文件的node.js代码。整体思路是,按照映射关系访问对应的路径,并且在本地生成文件,最后整体提交部署。

js 复制代码
const puppeteer = require("puppeteer");
const fs = require("fs");
const path = require("path");
const axios = require("axios");

const folder = "d:\\dist\\";
const domain = "https://www.baidu.com";
const urlMap = {
  "index.html": "https://www.baidu.com",
};

function formatDate(date) {
  const year = date.getFullYear();
  const month = String(date.getMonth() + 1).padStart(2, "0"); // 月份从0开始计数
  const day = String(date.getDate()).padStart(2, "0");
  return `${year}-${month}-${day}`;
}

(async () => {
  const browser = await puppeteer.launch({ headless: true });
  const paths = Object.keys(urlMap);
  const sitmapXML = ['<?xml version="1.0" encoding="UTF-8"?>'];
  sitmapXML.push('<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">');
  for (let i = 0; i < paths.length; i++) {
    const key = paths[i];
    const url = urlMap[key];
    const page = await browser.newPage();
    sitmapXML.push(`<url>
<loc>${domain}/${key}</loc>
<lastmod>${formatDate(new Date())}</lastmod>
</url>`);
    console.log(`准备请求:${url}`);
    await page.goto(url);
    const pageContent = await page.content();
    console.log(`保存网页文件: ${key}`);
    fs.writeFileSync(path.join(folder, key), pageContent);
    // 获取页面上的所有资源
    const resources = await page.evaluate(() => {
      const imgs = Array.from(document.querySelectorAll("img")).map((img) => img.src);
      const stylesheets = Array.from(document.querySelectorAll('link[rel="stylesheet"]')).map((link) => link.href);
      const scripts = Array.from(document.querySelectorAll("script")).map((script) => script.src);
      return imgs.concat(stylesheets, scripts).filter((item) => item && !item.startsWith("https://"));
    });

    console.log("资源列表", resources);

    for (const resource of resources) {
      try {
        const response = await axios.get(resource, { responseType: "arraybuffer" });

        const filename = path.basename(resource);

        const folderPath = path.join(folder, path.dirname(resource));

        if (!fs.existsSync(folderPath)) {
          fs.mkdirSync(folderPath, { recursive: true });
        }

        fs.writeFileSync(path.join(folderPath, filename), response.data);

        console.log(`资源下载: ${filename}`);
      } catch (error) {
        console.error(`资源下载异常 ${resource}: ${error}`);
      }
    }

    console.log("关闭页面");

    await page.close();
  }

  console.log("退出浏览器");

  await browser.close();

  sitmapXML.push("</urlset>");

  console.log("写入站点地图");

  fs.writeFileSync(path.join(folderPath, "sitemap.xml"), sitmapXML.join("\n"));
})();

Nginx网关静态化

Prerender.io提供了网关级的预渲染,他也开源了他的程序代码,具体可以参考 GitHub - prerender/prerender: Node server that uses Headless Chrome to render a javascript-rendered page as HTML. To be used in conjunction with prerender middleware.

他整个文档都提供了非常详细的说明。就是安装这个开源包。运行它,直接设置nginx配置文件即可。

js 复制代码
const prerender = require('prerender');
const server = prerender();
server.start();

然后修改nginx配置文件

c 复制代码
# Change YOUR_TOKEN to your prerender token
# Change example.com (server_name) to your website url
# Change /path/to/your/root to the correct value

server {
    listen 80;
    server_name example.com;
 
    root   /path/to/your/root;
    index  index.html;

    location / {
        try_files $uri @prerender;
    }
 
    location @prerender {
        proxy_set_header X-Prerender-Token YOUR_TOKEN;
        
        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|whatsapp") {
            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;
        }
        
        #resolve using Google's DNS server to force DNS resolution and prevent caching of IPs
        resolver 8.8.8.8;
 
        if ($prerender = 1) {
            
            #setting prerender as a variable forces DNS resolution since nginx caches IPs and doesnt play well with load balancing
            set $prerender "service.prerender.io";
            rewrite .* /$scheme://$host$request_uri? break;
            proxy_pass http://$prerender;
        }
        if ($prerender = 0) {
            rewrite .* /index.html break;
        }
    }
}

服务端同构

使用Next.js或者Nuxt.js去做服务端渲染。这个方式就更简单了,直接按照Vue或者React方式去开发。最后使用PM2部署到远程服务器上即可。没有任何迁移问题。 同构是结合了服务端渲染和前端渲染一起干活的一种技术架构。 在第一次打开浏览器请求页面的时候,这个页面所有的内容将会在服务端渲染出整个结构输出到客户端。而后续则是一直由客户端进行渲染网页结果内容。 这也是一种迁移量比较低的一种方案。

结论

以上三种方案均可以实现爬虫读取网页内容,但是对于整个技术需要自行评判做一定的取舍。

  1. 如果服务器自由度不高,直接全栈静态化即可。
  2. 改造成本低可以使用Prerender配合nginx做静态缓存。
  3. 需要有一定基础,但是整个技术架构比较统一的,可以采用服务端同构技术方案。
相关推荐
gqkmiss31 分钟前
Chrome 浏览器插件获取网页 iframe 中的 window 对象
前端·chrome·iframe·postmessage·chrome 插件
车载诊断技术1 小时前
电子电气架构 --- 什么是EPS?
网络·人工智能·安全·架构·汽车·需求分析
武子康1 小时前
大数据-258 离线数仓 - Griffin架构 配置安装 Livy 架构设计 解压配置 Hadoop Hive
java·大数据·数据仓库·hive·hadoop·架构
m0_748247553 小时前
Web 应用项目开发全流程解析与实战经验分享
开发语言·前端·php
m0_748255023 小时前
前端常用算法集合
前端·算法
真的很上进3 小时前
如何借助 Babel+TS+ESLint 构建现代 JS 工程环境?
java·前端·javascript·css·react.js·vue·html
web130933203983 小时前
vue elementUI form组件动态添加el-form-item并且动态添加rules必填项校验方法
前端·vue.js·elementui
NiNg_1_2344 小时前
Echarts连接数据库,实时绘制图表详解
前端·数据库·echarts
如若1234 小时前
对文件内的文件名生成目录,方便查阅
java·前端·python
滚雪球~5 小时前
npm error code ETIMEDOUT
前端·npm·node.js