分享两个性价比极高的SSR方案

最近总监提出我们公司运营的一个网站运营数据有点差,亟待提升该网站的SEO(搜索引擎优化)体验。不然自然流量着实有点少,全靠氪金买百度付费流量,成本太高,显然不太现实。但是当时技术选型的时候并未考虑到SEO相关的问题,结果选了VUE框架。像vue、react这种SPA(单页应用)框架虽然开发效率高,性能和体验都得到市场认可,但是SEO却是天生的短板。项目开发运营几年了,现在重构成本太高,可SEO的问题不解决流量就起不来,没流量就不可能给公司创收,总不能看着大家辛苦几年打磨的项目就这样死掉。困难总是人去解决的不是,在领到任务之后,我开始各种查找资料、调研、反复验证,总算找到两个可落地,性价比又高的解决方案。tips: 欢迎关注作者微信公众号fever code,获取最新技术分享。

Puppeteer无头浏览器

在分享方案之前先来聊聊什么是无头浏览器,因为我们今天的主角就是无头浏览器工具库(Puppeteer)。

无头浏览器(Headless Browser)是一种没有图形用户界面(GUI)的网络浏览器。所谓无头就是没有可视化的图形界面,因此不会占用屏幕空间,也不会在屏幕上显示任何内容,因此无头浏览器可以节省大量的资源和内存消耗。在功能设计上,无头浏览器提供了大量编程接口,使得开发者可以通过代码进行控制和操作浏览器以模拟用户行为,如点击、输入、提交表单等,这使得它在自动化测试、网页爬虫等场景中非常有用。常见的无头浏览器包括Chrome Headless、PhantomJS、Puppeteer等。简单点理解的话也可以理解为是没有用户界面的浏览器内核。

那什么是Puppeteer呢?Puppeteer 是一个由 Google 开发的 Node.js 库。它提供了一组高级 API,允许用户通过 DevTools 协议控制 Chromium 或 Chrome 浏览器。主要用途包括:自动化测试、网页爬取(网页抓取)、生成网页截图和 PDF、进行页面性能分析等任务。Puppeteer 允许用户以编程方式控制浏览器的行为,如模拟用户交互(点击按钮、填写表单)、导航到网页、修改页面内容、处理网络请求等。由于其深度集成 Chrome/Chromium 浏览器的能力,Puppeteer 使得开发者能够利用 Chrome 强大的网页渲染引擎和开发者工具功能,实现高度精确和复杂的自动化任务。

方案一:

使用nodejs启一个服务,通过Puppeteer请求目标网站,并获取经过Puppeteer渲染之后的html页面,返回给客户端,实现SSR(服务端渲染)。核心代码如下:

const puppeteer = require('puppeteer')
const express = require('express')
var app = express();
app.get('*', async (req, res) => {
    let url = "https://www.elecnest.com" + req.originalUrl; // 目标网站URL
    const browser = await puppeteer.launch(); // 启动Puppeteer浏览器
    const page = await browser.newPage(); // 创建一个新页面
    await page.goto(url, { waitUntil: 'networkidle2' }); // 跳转到目标网站并等待页面完全加载
    const html = await page.content(); // 获取页面HTML代码
    await browser.close(); // 关闭浏览器
    res.send(html);
});
app.listen(3000, () => {
    console.log('服务已启动在3000端口...');
});

启了nodejs服务之后还需要nginx服务器配合。具体架构为:用户向nginx服务器发起请求,nginx通过请求头信息判断是否为搜索引擎爬虫访问,如果为爬虫访问则将请求代理至nodejs服务,返回渲染好的html页面,供爬虫爬取页面内容,收录页面。如果为普通用户访问则正常访问目标网站,不走nodejs服务进行中转。

nginx配置如下:

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") {
      proxy_pass  http://172.17.0.1:3000;
    }
    alias /usr/share/nginx/html/;
    index  index.html index.htm;
    try_files $uri $uri/ /index.html;
}

在这里插入图片描述

方案二:

这种方案也可以采用java,php等服务端语言实现。这里使用php的puphpeteer库举例:

composer require nesk/puphpeteer
npm install @nesk/puphpeteer
    public function index()
{
      $url = 'http://cpservice.sipo.gov.cn/SecurityCode?timestamp='.time();
      $ret = $this->ocr_code($url, 4, 1);
    }
    public function get_cookie(){
        // @unlink('verify.png');
        // @unlink('result.png');
        $puppeteer = new Puppeteer;
        $browser = $puppeteer->launch([
            'headless'=>false
        ]);
        try {
            $page = $browser->newPage();
            $page->goto('http://cpservice.sipo.gov.cn/index.jsp');
            $img = $page->querySelector("#Verify");
            $usernameInput = $page->querySelector("#username");
            $usernameInput->focus(); //定位到用户名
            $page->keyboard->type("91321102338928957N");
            $passwordInput = $page->querySelector("#password");
            $passwordInput->focus();
            $page->keyboard->type("123123123");
            $page->screenshot(['path' => 'verify.png',
                'clip'=>[
                    'x'=>445,'y'=>513, 'width'=>70,'height'=>30
                ]
            ]);
            $url = 'http://dp.cn/verify.png';
            $ocr_url = 'http://dp.cn/index/index/ocr_code';
            $debug_url = 'http://o2.cn/oxygen/user/front_log';
$js =  <<<JS
var xmlhttp;
var ret = '1111';
if (window.XMLHttpRequest){
    xmlhttp = new XMLHttpRequest();
}else{
  xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.open("POST","{$ocr_url}",false);
xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
xmlhttp.send("url={$url}");
xmlhttp.onreadystatechange=function(){
    if (xmlhttp.readyState==4 && xmlhttp.status==200){
        ret = xmlhttp.responseText;
        return;
    }
}
if (xmlhttp.readyState==4 && xmlhttp.status==200){
    ret = xmlhttp.responseText;
}else{
    xmlhttp.open("GET","{$debug_url}?msg="+ JSON.stringify([xmlhttp.readyState, xmlhttp.status,xmlhttp.responseText, xmlhttp.responseXML]),true);
    xmlhttp.send();
}
return ret;
JS;
            $verifyFunction = JsFunction::create([], $js);
            $dimensions = $page->evaluate($verifyFunction);
            dump($dimensions);
            if($dimensions != '1111' && $dimensions != ''){
                $securityCodeInput = $page->querySelector("#securityCode");
                $securityCodeInput->focus();
                $page->keyboard->type($dimensions);
                $page->screenshot(['path' => 'result.png']);
                $loginFunction = JsFunction::create([], "
                    return login();
                ");
                $dimensions2 = $page->evaluate($loginFunction);
                $page->waitForNavigation(['timeout'=>5000]);
                $jumpFunction = JsFunction::create([], "
                    return document.cookie;
                ");
                $dimensions3 = $page->evaluate($jumpFunction);
                $page->screenshot(['path' => 'result2.png']);
                dump($dimensions3);
            }else{
            }
        } catch (Node\Exception $exception) {
            ptrace($e->getMessage().PHP_EOL.$e->getTraceAsString());
            print($e->getMessage().PHP_EOL.$e->getTraceAsString());
        }
        $browser->close();
    }
    public function ocr_code($url, $length = 4, $debug =0){
        if(false !== stripos($url, 'http')){
            $img = file_get_contents($url);
        }else{
            $img = base64_decode($url);
        }
        // TODO md5 缓存识别结果
        // halt($img);
        $tmp = tempnam(sys_get_temp_dir(), 'code');
        file_put_contents($tmp, $img);
        // ptrace($tmp);
        // return $tmp;
    $ret = plugin_action('BaiduAi', 'Ocr', 'basicAccurate', [$img]);
        ptrace($ret);
        $words = $ret['words_result']['0']['words'];
        $words = trim($words, ' ');
        $words = str_ireplace(['.',' ', '\''], '', $words);
    if($debug){
          echo '<img src="'.base64EncodeImage($tmp).'">';
      // dump($ret);
      // dump($words);
    }
    return strlen($words) == $length? $words: '';
    }

启了php服务之后同样要使用nginx服务器代理至该服务,操作同上,就是把nodejs服务替换成php服务。值得注意的是,puphpeteer只是一个桥接库,php通过这个桥接库操作puppeteer的API。puppeteer是基于nodejs环境的,所以要使用php的puphpeteer库,服务器环境还需要安装nodejs。

测试验证:

1.验证爬虫访问是否代理到“无头浏览器服务”方法:

postman访问域名时添加如下请求头:User-Agent:Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)

在这里插入图片描述
2.项目发布之后也可以去百度收录看一下爬取诊断:
在这里插入图片描述
项目未做任何处理之前的效果,请求回来的页面并没有经过服务端渲染(SSR),SEO体验极差
在这里插入图片描述
使用上述方案之后的效果,请求回来的是服务端渲染好的html页面
在这里插入图片描述

写在最后

上面分享的两个方案适合历史技术包袱较重,但是对SEO又有需求的项目,这两种方案是属于非侵入式改造,不该动原有项目代码,只在服务端做处理,从成本角度考量性价比是非常高的,只是多一点服务器性能开销,并无其他成本​。如果新项目的话还是建议使用nuxt.js或者next.js这种全栈框架,毕竟这两个框架目前主流的完整的​SSR解决方案,尽管槽点也很多。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/801535.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Linux】权限的管理和Linux上的一些工具

文章目录 权限管理chgrpchownumaskfile指令sudo指令 目录权限粘滞位Linux中的工具1.软件包管理器yum2.rzsz Linux开发工具vim 总结 权限管理 chgrp 功能&#xff1a;修改文件或目录的所属组 格式&#xff1a;chgrp [参数] 用户组名 文件名 常用选项&#xff1a;-R 递归修改文…

解析 Mira :基于 Web3,让先进的 AI 技术易于访问和使用

“Mira 平台正在以 Web3 的方式解决当前 AI 开发面临的复杂性问题&#xff0c;同时保护 AI 贡献者的权益&#xff0c;让他们可以自主拥有并货币化自己的模型、数据和应用&#xff0c;以使先进的 AI 技术更加易于访问和使用。” AI 代表着一种先进的生产力&#xff0c;它通过深…

UE4-初见虚幻引擎

一.创建自己的工程 1.启动 a.通过桌面双击图标来打开对应版本的虚幻引擎 b.通过EPIC启动器开启动虚幻引擎 2.选择或新建项目 ps:高版本虚幻编辑器可以打开低版本的虚幻项目&#xff0c;但是高版本虚幻的项目不可以由低版本的虚幻编辑器打开。 3. 选择要打开的项目 4.选择模版 选…

mindspore打卡第24天之LSTM+CRF序列标注

LSTMCRF序列标注 概述 序列标注指给定输入序列&#xff0c;给序列中每个Token进行标注标签的过程。序列标注问题通常用于从文本中进行信息抽取&#xff0c;包括分词(Word Segmentation)、词性标注(Position Tagging)、命名实体识别(Named Entity Recognition, NER)等。以命名实…

Host碰撞实验

目录 Host碰撞原理 Host碰撞判断技巧 Host碰撞检测方法 Host碰撞实验步骤 从攻击者的视角来进行资产的梳理&#xff0c;采用全端口扫描子域名收集的方式&#xff0c;识别所有的企业资产暴露面。但即使是这样&#xff0c;往往会因为配置错误或是未及时回收等原因&#xff0c…

C++ std::lock_guard和 std::unique_lock

二者都是 C 标准库中用于管理互斥锁&#xff08;mutex&#xff09;的 RAII&#xff08;Resource Acquisition Is Initialization&#xff09;机制的类。这些类可以确保互斥锁在构造时被获取&#xff0c;在析构时被释放&#xff0c;从而避免死锁和资源泄漏问题。不过&#xff0c…

【数据结构取经之路】二叉搜索树的实现

目录 前言 二叉搜索树 概念 性质 二叉搜索树的实现 结点的定义 插入 查找 删除 二叉搜索树完整代码 前言 首先&#xff0c;二叉搜索树是一种数据结构&#xff0c;了解二叉搜素树有助于理解map和set的特性。 二叉搜索树 概念 二叉搜索树又称二叉排序树&#xff0c…

服务器操作集合

服务器使用PC作为代理访问外网 1、PC上启动代理&#xff0c;比如nginx 下载nginx&#xff1a;http://nginx.org/en/download.html 修改配置文件&#xff0c;在conf下&#xff1a; http {include mime.types;default_type application/octet-stream; sendfile …

C++深度解析教程笔记12ok-继承,继承的构造与析构,同名覆盖

C深度解析教程笔记12 第43课 - 继承的概念和意义实验-类的组合实验-类的继承实验-子类与父类的构造顺序小结 第44课 - 继承中的访问级别实验-子类直接访问父类非公成员&#xff08;error&#xff09;实验-子类访问父类非公成员protected实验-复杂的例子bug 小结 第45课 - 不同的…

如何构建全生命周期的安全体系架构来确保容器的安全?

容器技术在云原生应用和微服务架构中得到了广泛应用&#xff0c;其轻量、灵活和高效的特点使其成为现代IT环境中的重要工具。然而&#xff0c;尽管容器带来了许多优势&#xff0c;但其安全性问题也不容忽视。接下来跟随博主一起探索如何构建全生命周期的安全体系架构以确保容器…

《算法笔记》总结No.7——二分(多例题详解版)

一.二分查找 目前有一个有序数列&#xff0c;举个例子&#xff0c;假设是1~1000&#xff0c;让我们去查找931这个数字&#xff0c;浅显且暴力的做法就是直接从头到尾遍历一遍&#xff0c;直到找到931为止。当n非常大&#xff0c;比如达到100w时&#xff0c;这是一个非常大的量级…

经纬恒润底盘控制产品R-EPS成功量产

近日&#xff0c;经纬恒润开发的齿条式电动助力转向系统R-EPS&#xff08;Rack-Electronic Power Steering&#xff09;搭载某新能源车企中高端MPV车型&#xff0c;成功量产落地。 该产品采用恒润Double Pinion/Rack平台级的软硬件方案&#xff0c;模块复用程度更高&#xff0c…

5.4 软件工程-系统设计

系统设计 - 概述 设计软件系统总体结构 数据结构及数据库设计 编写概要设计文档、评审 详细设计的基本任务 真题

DHCP服务、FTP服务

一、DHCP 1.1 DHCP是什么 DHCP&#xff08;Dynamic Host Configuration Protocol&#xff0c;动态主机配置协议&#xff09;是一种网络协议&#xff0c;用于自动分配 IP 地址和其他网络配置信息给网络中的设备 1.2 DHCP的好处 自动化: 减少了手动配置 IP 地址和网络参数的工…

pc端注册页面 密码校验规则

1.密码校验规则 格应包含大小写字母、数字和特殊符号,长度为8-20 var validateRetrievePassword (rule, value, callback) > {let reg /^(?.*[A-Za-z])(?.*\d)(?.*[~!#$%^&*()_<>?:"{},.\/\\;[\]])[A-Za-z\d~!#$%^&*()_<>?:"{},.\/\\;…

Linux系统下weblogic10.3.6版本打补丁步骤

linux系统 weblogic补丁压缩包&#xff1a;p35586779_1036_Generic.zip 链接&#xff1a;https://pan.baidu.com/s/1EEz_zPX-VHp5EU5LLxfxjQ 提取码&#xff1a;XXXX &#xff08;补丁压缩包中包含以下东西&#xff09; 打补丁步骤&#xff1a; 1.备份原weblogic(需要先确保服…

【Linux杂货铺】期末总结篇3:用户账户管理命令 | 组账户管理命令

&#x1f308;个人主页&#xff1a;聆风吟_ &#x1f525;系列专栏&#xff1a;Linux杂货铺、Linux实践室 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 第五章5.1 ⛳️Linux 账户5.2 ⛳️用户配置文件和目录&#xff08;未完待续&#xff09;5.2.1 …

【机器学习实战】Datawhale夏令营2:音视频攻防(deepfake)Baseline句解

# Datawhale # AI夏令营 # 夏令营 文章目录 1. 赛题简要介绍2. 赛题数据集3. 评价指标4. Baseline整体4.1 计算样本数4.2 创建video对象4.3 下载需要的库&&补充知识4.4 设置pytorch随机种子&&CUDNN配置4.5 音视频预处理4.6 创建训练数据文件夹4.7 生成梅尔频谱…

habase集群安装

解压到/opt/softs目录 tar -zxvf hbase-2.4.11-bin.tar.gz -C /opt/softs/ 改名 mv hbase-2.4.11/ hbase2.4.11 配置环境变量 修改/etc/profile vim /etc/profile 添加 #HBASE_HOME export HBASE_HOME/opt/softs/hbase2.4.11 export PATH$PATH:$HBASE_HOME/bin 修改其中的…

Linux部署禅道(无脑复制版)

目录 环境部署1、下载&#xff0c;解压2、启动3、设置开机自启 登录禅道登录数据库1、设置账号2、网页登录数据库 环境 Linux系统 Centos7 《Linux一键安装包安装禅道》视频链接&#xff1a; https://www.zentao.net/zentao-install/zentao-linux-install-80523.html 部署 …