超实用!CSDN个人数据Chrome插件开发

插件简介

相信写过博客的都知道,每天会经常打开自己的主页无数次,尤其是写了一篇新文章,就为了看文章浏览量增长了多少,文章获得了多少个赞,有多少人评论(谁不想自己写的文章成为爆款呢~)

因此我特意做了一个Chrome插件,让用户更方便地实时查看自己在CSDN博客上的数据

我们先来看看最后的实现效果:

在这里插入图片描述

这个插件能显示两部分数据,一部分是用户个人数据,包括:总阅读数、文章数、排名、粉丝数,另一部分是个人博客成就数据,包括:点赞数、评论数、收藏数、分享数

插件显示的数据默认每1小时更新一次,用户可以自行修改数据更新时间

插件的UI第一版写的暂时比较简陋,可以后期版本再修改以及增加一些功能

在开发之前,我们需要会一些前置知识,包括:Node.js爬虫、Chrome插件开发

不会的同学也没关系,可以看我之前写过的博客,里面关于爬虫和插件开发写的非常详细:

都 2023 年了还不会 Node.js 爬虫?快学起来!

Node.js 爬虫只会 Cheerio?来试试 Puppeteer!

从零入门 Chrome 插件开发

好的,现在我们就来开动吧!

爬取CSDN个人数据

这里默认大家已经学过puppeteer,具体爬取的操作在上一篇文章中有很详细的解释

这里我们分析完网页结构之后直接来编写爬虫脚本:

// 无头浏览器模块
const puppeteer = require("puppeteer");// 目标页面
const crawlPage = "https://blog.csdn.net/weixin_46232841?spm=1000.2115.3001.5343";// 网页爬虫
async function crawler() {//创建实例const browser = await puppeteer.launch({//无浏览器界面启动headless: "new",//放慢浏览器执行速度,方便测试观察slowMo: 100,// 设置打开的浏览器窗口尺寸defaultViewport: { width: 960, height: 540 },});// 新开一个tab页面const page = await browser.newPage();// 加载目标页,在 500ms 内没有任何网络请求才算加载完await page.goto(crawlPage, { waitUntil: "networkidle0" });// 在无头浏览器页面dom环境,获取页面数据const myData = await page.evaluate(() => {let data = {};let name = "萌萌哒の瑞萌萌";let achievement = {};// 个人数据document.querySelectorAll(".user-profile-head-info-r-c ul").forEach((ele) => {const allReadNum = ele.querySelector("li:nth-child(1) .user-profile-statistics-views .user-profile-statistics-num").innerText;const articleNum = ele.querySelector("li:nth-child(2) .user-profile-statistics-num").innerText;const rank = ele.querySelector("li:nth-child(3) .user-profile-statistics-num").innerText;const fans = ele.querySelector("li:nth-child(4) .user-profile-statistics-num").innerText;data = {allReadNum,articleNum,rank,fans}});// 个人成就document.querySelectorAll(".aside-common-box-achievement").forEach((ele) => {const like = ele.querySelector("li:nth-child(1) div").innerText;const comment = ele.querySelector("li:nth-child(2) div").innerText;const favorite = ele.querySelector("li:nth-child(3) div").innerText;const share = ele.querySelector("li:nth-child(4) div").innerText;achievement = {like,comment,favorite,share}});return {name,data,achievement};});console.log(myData);// 关闭tab页await page.close();// 关闭实例await browser.close();
})

运行一下看看有没有获取到我们想要的数据吧:

在这里插入图片描述

完美!现在我们拿到了我们想要的数据,接下来要完成的需求就是:

  1. 将数据存储到json文件里,方便插件目录获取
  2. 定时爬取,每隔一小时爬取一次,及时更新json文件

解决办法:用 fs 模块的fs.writeFile来保存文件,用 node-schedule 模块来实现定时爬取

上代码!

// 无头浏览器模块
const puppeteer = require("puppeteer");
const schedule = require('node-schedule');
const fs = require('fs');// 目标页面
const crawlPage = "https://blog.csdn.net/weixin_46232841?spm=1000.2115.3001.5343";// 创建一个定时任务,每隔1小时执行一次
let job = schedule.scheduleJob('0 0 */1 * * *',// 网页爬虫
async function crawler() {//创建实例const browser = await puppeteer.launch({//无浏览器界面启动headless: "new",//放慢浏览器执行速度,方便测试观察slowMo: 100,// 设置打开的浏览器窗口尺寸defaultViewport: { width: 960, height: 540 },});// 新开一个tab页面const page = await browser.newPage();// 加载目标页,在 500ms 内没有任何网络请求才算加载完await page.goto(crawlPage, { waitUntil: "networkidle0" });// 在无头浏览器页面dom环境,获取页面数据const myData = await page.evaluate(() => {let data = {};let name = "萌萌哒の瑞萌萌";let achievement = {};// 个人数据document.querySelectorAll(".user-profile-head-info-r-c ul").forEach((ele) => {const allReadNum = ele.querySelector("li:nth-child(1) .user-profile-statistics-views .user-profile-statistics-num").innerText;const articleNum = ele.querySelector("li:nth-child(2) .user-profile-statistics-num").innerText;const rank = ele.querySelector("li:nth-child(3) .user-profile-statistics-num").innerText;const fans = ele.querySelector("li:nth-child(4) .user-profile-statistics-num").innerText;data = {allReadNum,articleNum,rank,fans}});// 个人成就document.querySelectorAll(".aside-common-box-achievement").forEach((ele) => {const like = ele.querySelector("li:nth-child(1) div").innerText;const comment = ele.querySelector("li:nth-child(2) div").innerText;const favorite = ele.querySelector("li:nth-child(3) div").innerText;const share = ele.querySelector("li:nth-child(4) div").innerText;achievement = {like,comment,favorite,share}});return {name,data,achievement};});// 将数据写入文件中fs.writeFile('../chrome/mycsdn.json', JSON.stringify(myData), function (err, data) {if (err) {throw err}console.log('文件保存成功,当前时间:' + new Date());})// 关闭tab页await page.close();// 关闭实例await browser.close();
})

最终实现效果:

在这里插入图片描述

到此我们就实现了定时爬取CSDN个人主页数据的操作,接下来我们一起来完成Chrome插件开发!

Chrome插件开发

先来创建一个 Chrome 插件项目叫 mycsdn:

mkdir mycsdn  # 创建插件项目
cd mycsdn # 进入项目根目录
touch manifest.json # 在项目根目录中创建一个名为 manifest.json 的文件

接下来我们简单编写 manifest.json文件,参数配置如下:

{"manifest_version": 2,"name": "CSDN个人数据","version": "1.0","description": "CSDN个人数据展示","permissions": ["activeTab"],"browser_action": {"default_title": "CSDN个人数据","default_popup": "popup/popup.html","default_icon": {"128": "icon/icon128.png"}}
}

然后我们在根目录创建popup文件夹,添加一个名为 popup.html 的文件,我们来完成点击弹出的页面结构:

<!DOCTYPE html>
<html>
<head><title>CSDN个人数据展示</title><meta charset="utf-8" name="viewport" content="width=device-width, initial-scale=1.0"><link rel="stylesheet" href="popup.css"><script src="popup.js"></script>
</head>
<body><div class="container"><div class="box"><div class="card-data content"><h2>个人数据</h2><p>总阅读数</p><p>文章数</p><p>排名</p><p>粉丝数</p></div></div><div class="box"><div class="card-data card-achievement content"><h2>个人成就</h2><p>点赞数</p><p>评论数</p><p>收藏数</p><p>分享数</p></div></div></div>
</body>
</html>

来点CSS装饰一下:

body {min-width: 400px;min-height: 300px;font-family: Arial, Helvetica, sans-serif;background-color: #f2f2f2;margin: 0;padding: 0;
}.container {max-width: 800px;margin: 0 auto;box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);padding: 10px 20px;background: radial-gradient(circle, rgba(238,174,202,1) 0%, rgba(148,187,233,1) 100%);
}.box {position: relative;height: 220px;align-items: center;transition: 0.5s;z-index: 1;display: flex;flex-wrap: wrap;align-items: center;}.box .content {position: relative;padding: 0px 40px 10px 40px;backdrop-filter: blur(10px);z-index: 1;transform: 0.5s;background-color: rgba(255, 255, 255, 0.8);border-radius: 20px;box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);overflow: hidden;margin-bottom: 0;font-size: 18px;color: #666;flex-grow: 1;text-align: right;
}.content::before {content: "";background-image: url("../bk1.jpg");background-repeat: no-repeat;background-size: cover;opacity: 0.5;filter: blur(1px);position: absolute;top: 0;left: 0;bottom: 0;right: 0;
}.box .content p {font-size: 14px;font-weight: bolder;color: black;
}.box .content h2 {font-size: 20px;margin-bottom: 10px;font-weight: bolder;color: black;
}

在这里插入图片描述

效果还不错,大家也可以根据自己的审美自己来设计页面

接下来我们来写JS,这里因为已经将数据存储在本地JSON文件中,所以我们用 XMLHttpRequest 对象来读取这个JSON文件,详细信息可以看一下注释:

// 当DOM加载完成时执行以下代码
document.addEventListener("DOMContentLoaded", function () {// 创建一个新的XMLHttpRequest对象let xhr = new XMLHttpRequest();// 指定要读取的文件路径xhr.open('GET', '../mycsdn.json', true);// 当请求完成时执行的回调函数xhr.onload = function() {if (xhr.status === 200) {// 将JSON字符串转换为JavaScript对象let data = JSON.parse(xhr.responseText);// 处理读取到的数据const allReadNum = document.querySelector("p:nth-of-type(1)");allReadNum.innerHTML = `总阅读数:${data.data.allReadNum}`;const articleNum = document.querySelector("p:nth-of-type(2)");articleNum.innerHTML = `文章数:${data.data.articleNum}`;const rank = document.querySelector("p:nth-of-type(3)");rank.innerHTML = `排名:${data.data.rank}`;const fans = document.querySelector("p:nth-of-type(4)");fans.innerHTML = `粉丝数:${data.data.fans}`;const like = document.querySelector(".card-achievement p:nth-of-type(1)");like.innerHTML = `点赞数:${data.achievement.like}`;const comment = document.querySelector(".card-achievement p:nth-of-type(2)");comment.innerHTML = `评论数:${data.achievement.comment}`;const favorite = document.querySelector(".card-achievement p:nth-of-type(3)");favorite.innerHTML = `收藏数:${data.achievement.favorite}`;const share = document.querySelector(".card-achievement p:nth-of-type(4)");share.innerHTML = `分享数:${data.achievement.share}`;}};// 发送请求xhr.send();// 设置插件图标chrome.browserAction.setIcon({ path: "icon/icon128.png" });// 设置插件标题chrome.browserAction.setTitle({ title: "CSDN个人数据展示" });
});

我们首先创建了一个XMLHttpRequest对象,然后使用open方法指定要读取的文件路径。接着我们定义了一个onload回调函数,在请求完成时执行。在这个回调函数中,我们首先检查请求的状态是否为200(表示成功),然后使用JSON.parse方法将JSON字符串转换为JavaScript对象,最后我们就可以处理读取到的数据将其显示在页面上。

在这里插入图片描述

进程管理工具PM2

到此为止,我们这个插件项目还剩最后一个问题没有解决:我们编写的这个nodejs 爬虫文件不可能一直在控制台运行,那么我们该怎么让这个文件一直运行从而来定时爬取更新JSON文件呢?

我们这里来用一个管理 Node.js 进程的第三方工具 PM2

PM2是一个流行的Node.js进程管理器,它可以帮助我们简化应用程序的部署、监控和管理:

  • 进程管理:PM2可以启动、停止、重启和删除进程,可以使用PM2来管理单个应用程序或多个应用程序。
  • 自动重启:如果进程崩溃或异常退出,PM2会自动重启它。
  • 监控和日志记录:PM2可以监控应用程序的CPU和内存使用情况,并将日志记录到文件中。
  • 负载均衡:如果运行多个实例,PM2可以使用负载均衡来将流量分配到不同的实例上。
  • 部署:PM2可以在生产环境中部署应用程序,可以将应用程序作为系统服务运行,并在系统启动时自动启动。

以下是一些PM2的常用命令:

  • pm2 start <file>:启动一个应用程序。
  • pm2 stop <id|name>:停止一个应用程序。
  • pm2 restart <id|name>:重启一个应用程序。
  • pm2 delete <id|name>:删除一个应用程序。
  • pm2 list:列出所有正在运行的应用程序。
  • pm2 monit:监视所有正在运行的应用程序的CPU和内存使用情况。

我们来安装一下 pm2:

npm install -g pm2

然后用 pm2 启动项目:

pm2 start /path/to/your/nodejs/script.js

这个命令会启动Node.js程序,并且在后台运行,即使关闭了终端也不会停止运行

在这里插入图片描述

如果想停止程序可以使用以下命令:

pm2 stop /path/to/your/nodejs/script.js

插件开发总结

本项目是Node爬虫和Chrome插件开发的一个综合案例,这个插件的功能非常实用,可以帮助我们更好地了解自己的CSDN博客个人数据。

我们首先用puppeteer编写node爬虫脚本爬取到了CSDN个人主页数据,然后将数据写入了JSON文件,并用node-schedule模块完成了定时爬取的功能。

之后我们完成了Chrome插件的主体开发,最后我们使用PM2进程管理工具让这个爬虫文件一直运行。

插件完整代码可以自取:https://github.com/KongC-X/node-creeper/tree/main/chrome

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于Java学生成绩管理系统设计与实现(源码+部署文档+报告)

博主介绍&#xff1a; ✌至今服务客户已经1000、专注于Java技术领域、项目定制、技术答疑、开发工具、毕业项目实战 ✌ &#x1f345; 文末获取源码联系 &#x1f345; &#x1f447;&#x1f3fb; 精彩专栏 推荐订阅 &#x1f447;&#x1f3fb; 不然下次找不到 Java项目精品实…

【JavaEE进阶】 初识Spring Web MVC

文章目录 &#x1f334;什么是Spring Web MVC&#xff1f;&#x1f6a9;什么是Servlet呢? &#x1f38b;MVC 定义&#x1f6a9;再理解Spring MVC &#x1f340;如何学习Spring MVC呢&#xff1f;⭕总结 &#x1f334;什么是Spring Web MVC&#xff1f; Spring Web MVC 是基于…

C编程指针篇----包括历年真题

一&#xff0c;&#xff08;20年&#xff09;用指针字符逆序 代码&#xff1a; int main() {char s[7] "monkey", * p1, * p2, c;p1 p2 s;while (*p2) p2;p2--;while (p2 > p1) {c *p1; *p1 *p2; *p2-- c; }printf("%s", s);return 0; } 运行结…

LCR 150. 彩灯装饰记录 II

解题思路&#xff1a; 与LCR. 彩灯装饰记录 I类似&#xff0c;增加了分层输出。 class Solution {public List<List<Integer>> decorateRecord(TreeNode root) {Queue<TreeNode> queue new LinkedList<>();List<List<Integer>> res ne…

【Linux操作系统】探秘Linux奥秘:操作系统的入门与实战

&#x1f308;个人主页&#xff1a;Sarapines Programmer&#x1f525; 系列专栏&#xff1a;《操作系统实验室》&#x1f516;诗赋清音&#xff1a;柳垂轻絮拂人衣&#xff0c;心随风舞梦飞。 山川湖海皆可涉&#xff0c;勇者征途逐星辉。 目录 &#x1fa90;1 初识Linux OS …

WebGL以及wasm的介绍以及简单应用

简介 下面主要介绍了WebGL和wasm,是除了html,css,js以外Web标准所支持的另外两个大件 前者实现复杂的图形处理,后者提供高效的代码迁移以及代码执行效率 WebGL 简介 首先,浏览器里的游戏是怎么做到这种交互又显示不同的画面的? 试想用我们的前端三件套实现一下.好像可以…

大创项目推荐 深度学习二维码识别

文章目录 0 前言2 二维码基础概念2.1 二维码介绍2.2 QRCode2.3 QRCode 特点 3 机器视觉二维码识别技术3.1 二维码的识别流程3.2 二维码定位3.3 常用的扫描方法 4 深度学习二维码识别4.1 部分关键代码 5 测试结果6 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天…

Unity坦克大战开发全流程——开始场景——设置界面

开始场景——设置界面 step1&#xff1a;设置面板的背景图 照着这个来设置就行了 step2&#xff1a;写代码 关联的按钮控件 监听事件函数 注意&#xff1a;要在start函数中再写一行HideMe函数&#xff0c;以便该面板能在一开始就能隐藏自己。 再在BeginPanel脚本中调用该函数即…

基于SSM的蛋糕甜品店管理系统的设计与开发论文

基于SSM的蛋糕甜品店管理系统的设计与开发 摘要 如今&#xff0c;科学技术的力量越来越强大&#xff0c;通过结合较为成熟的计算机技术&#xff0c;促进了学校、医疗、商城等许多行业领域的发展。为了顺应时代的变化&#xff0c;各行业结合互联网、人工智能等技术&#xff0c…

手写Spring与基本原理--简易版

文章目录 手写Spring与基本原理解析简介写一个简单的Bean加载容器定义一个抽象所有类的BeanDefinition定义一个工厂存储所有的类测试 实现Bean的注册定义和获取基于Cglib实现含构造函数的类实例化策略Bean对象注入属性和依赖Bean的功能Spring.xml解析和注册Bean对象实现应用上下…

API 开放平台项目(已整理,已废弃)

项目大纲 前端 React 18Ant Design Pro 5.x 脚手架Ant Design & Procomponents 组件库Umi 4 前端框架OpenAPI 前端代码生成 后端 Java Spring BootMySQL 数据库MyBatis-Plus 及 MyBatis X 自动生成API 签名认证&#xff08;Http 调用&#xff09;Spring Boot Starter&#…

探寻数据压缩——第一代小波构造的统一框架

小波分析作为一种多尺度分析方法&#xff0c;已经在信号处理、图像处理、数据压缩和模式识别等领域中展现出了巨大的应用潜力。在小波分析的发展历程中&#xff0c;第一代小波构造算法被广泛应用&#xff0c;为了更好地理解和应用第一代小波构造算法&#xff0c;构建一个统一的…

数据库——创建存储过程、函数和触发器安装phpmyadmin

1.实验内容及原理 1. 在 Windows 系统中安装 VMWare 虚拟机&#xff0c;在 VMWare 中安装 Ubuntu 系统,并在 Ubuntu 中搭建 LAMP 实验环境。 2. 使用 MySQL 进行一些基本操作&#xff1a; &#xff08;1&#xff09;登录 MySQL&#xff0c;在 MySQL 中创建用户&#xff0c;…

事实验证文章分类 Papers Category For Fact Checking

事实验证文章分类 Papers Category For Fact Checking By 2023.11 个人根据自己的观点&#xff0c;花了很多时间整理的一些关于事实验证领域证据召回&#xff0c;验证推理过程的文献综合整理分类&#xff08;不是很严谨&#xff09;。 引用请注明出处 欢迎从事事实验证Fact…

TypeScript源码中的一个很有意思的简写

在读TypeScript源码时&#xff0c;发现一个很有意思的简写 &#xff1a; return scriptInfo ? scriptInfo.getDefaultProject() : (this.logErrorForScriptInfoNotFound(isString(fileNameOrScriptInfo) ? fileNameOrScriptInfo : fileNameOrScript…

挑战Python100题(6)

100 Python challenging programming exercises 6 Question 51 Define a class named American and its subclass NewYorker. Hints: Use class Subclass(ParentClass) to define a subclass. 定义一个名为American的类及其子类NewYorker。 提示&#xff1a;使用class Subcla…

【史上最细教程】1台服务器部署2台MongoDB实例

文章目录 【史上最全教程】1台服务器部署2台MongoDB实例1.下载解压安装包2.配置系统环境变量3.创建实例27017扩展内容(可跳过) 4.配置安全组、防火墙5.可视化工具连接问题1&#xff1a;not authorized on admin to execute command 【史上最全教程】1台服务器部署2台MongoDB实例…

Flink(十一)【状态管理】

Flink 状态管理 我们一直称 Flink 为运行在数据流上的有状态计算框架和处理引擎。在之前的章节中也已经多次提到了“状态”&#xff08;state&#xff09;&#xff0c;不论是简单聚合、窗口聚合&#xff0c;还是处理函数的应用&#xff0c;都会有状态的身影出现。状态就如同事务…

Java日期工具类LocalDate

Java日期工具类LocalDate 嘚吧嘚java.util.DateJava8新增日期类时区 LocalDate - API创建日期获取年月日修改年月日日期比较 嘚吧嘚 java.util.Date 在Java8之前通常会使用Date结合SimpleDateFormat、Calender来处理时间和日期的相关需求。 1、可读性差、易用性差、使用起来冗…

C#的checked关键字判断是否溢出

目录 一、定义 二、示例&#xff1a; 三、生成&#xff1a; 一、定义 使用checked关键字处理溢出。 在进行数学运算时&#xff0c;由于变量类型不同&#xff0c;数值的值域也有所不同。如果变量中的数值超出了变量的值域&#xff0c;则会出现溢出情况&#xff0c;出现溢出…