前言
前两天公司的测试同事问我了一个关于生成网页快照的技术问题,经过一番调研发现了nightmarejs这个库
阅读文档和简单实践后对其有了基本的认知,大体可以将其理解为运行在node Electron环境中的无头浏览器,类似python的selenium,通常用于网页的UI测试和一些自动化操作
虽然不是什么新奇的东西,但刻在一个程序员DNA内的折腾本能还是被激活了...
没有需求创造需求也要搞(不是)
所以本文也算抛砖引玉,带领大家实现一个命令行里的知乎,具有查看热门话题和搜索快捷跳转的功能
或许你在阅读完本篇文章后,会萌生更多有趣(但没用)的想法,都可以使用nightmare.js去实现,甚至可以自己搭建一个快捷指令集
那么我们就开始吧!
1.确定需求
当我们使用nightmare启动了无头浏览器后访问知乎页面 我们可以获取到页面上的任何内容 只需定位到要使用的元素在命令行里进行IO操作即可实现
所以整理一下思路就是
知乎热榜
启动无头浏览器-->访问知乎热榜-->获取列表数据在命令行中展示-->选择某条话题-->自动打开浏览器并跳转
搜索功能
在命令行中键入搜索内容-->自动打开浏览器并搜索
2.准备工作
首先我们先安装今天的主角
npm install --save nightmare
其次安装inquirer.js和open.js
npm install --save inquirer
npm install --save open
inquirer.js 有CLI搭建经历的同学一定不会陌生,它主要用于node环境下命令行的GUI交互,说白了就是命令行里的表单
open.js 是执行node环境里打开文件、浏览器网址、app等操作的库
关于这两个库使用方法都比较简单,可以自行去看文档了解,这里不做赘述
3.开整!
首先新建一个zhihu.js的文件 键入以下代码
import Nightmare from "nightmare";
import inquirer from "inquirer";
import open from 'open'
const nightmare = Nightmare({ show: true });
引入用到的包并实例化一个Nightmare对象
这里设置show:true 意思是启动后显示无头浏览器界面 如果为false则只是在后台运行
在开发阶段我们要看模拟效果所以先将其设为true,等大功告成后就不需要了
这样我们便启动了一个无头浏览器
然后我们先来把最核心的功能搞定
实现热门功能
第一步 启动无头浏览器-->访问知乎热榜
const hotTopic = () => {
nightmare
.goto("https://www.zhihu.com/knowledge-plan/hot-question/hot/0/hour")//跳转知乎热榜
.then(() => {
console.log('已成功打开页面')
});
}
很简单 调用.goto方法即可
可以看到 nightmare的方法调用方式有些像远古时期的jquery 链式调用的方式 很符合工作流的使用直觉
第二步 访问知乎热榜-->获取列表数据在命令行中展示
const hotTopic = () => {
nightmare
.goto("https://www.zhihu.com/knowledge-plan/hot-question/hot/0/hour")//跳转知乎热榜
.wait("div[role=list]>.css-vurnku")//等待热榜话题列表元素出现
.evaluate(() => {
let arr = [];
let doms = document.querySelectorAll(
"div[role=list]>.css-vurnku>div>div>a"
);
doms.forEach((item, index) => {
arr.push({ name: doms[index].innerText, href: doms[index].href });
});
return arr;
})
.then((arr) => {
console.log(arr)
});
}
打开页面后 像一般爬虫操作一样 我们先.wait等待列表元素在页面上渲染完成
目标元素类名已经帮大家找好了 直接用即可
找到目标元素后调用.evaluate进行工作流的操作 将其对应子元素的列表项一个个循环保存 输出在本地
第三步 选择某条话题-->自动打开浏览器并跳转
const hotTopic = () => {
nightmare
.goto("https://www.zhihu.com/knowledge-plan/hot-question/hot/0/hour")//跳转知乎热榜
.wait("div[role=list]>.css-vurnku")//等待热榜话题列表元素出现
.evaluate(() => {
let arr = [];
let doms = document.querySelectorAll(
"div[role=list]>.css-vurnku>div>div>a"
);
doms.forEach((item, index) => {
arr.push({ name: doms[index].innerText, href: doms[index].href });
});
return arr;
})
.then((arr) => {
const promptList = [
{
type: "rawlist",
message: "topics:",
name: "topic",
choices: arr.map((i) => i.name),
filter: (val) => arr.findIndex((i) => i.name == val),
},
];
inquirer.prompt(promptList).then((answers) => {
open(arr[answers.topic].href, {
app: {
name: open.apps.chrome,
},
});
});
})
.then((arr) => {
console.log(arr)
});
}
在我们拿到列表数据后 就要着手实现命令行的交互
首先定义了用于构造inquirer.js 的rawlist类型表单项的配置 其实就相当于配置antd中Select的options和onChange
当选中某条话题后调用open方法打开chrome浏览器并跳转对应的页面即可 当然open.js还可以打开很多其他的app 前提是要官方支持 目前支持的浏览器为chrome firefox edge
第四步 处理入口操作
const promptList = [
{
type: "rawlist",
message: "热门还是搜索?",
name: "type",
choices: ["热门", "搜索"],
},
];
inquirer.prompt(promptList).then((answers) => {
answers.type == "热门" && hotTopic();
answers.type == "搜索" && search();
});
至此 知乎热榜的功能已经完成 你可以直接执行
node zhihu.js
来查看效果
也可以进一步在package.json里配置命令
...
"scripts": {
"zhihu":"node ./zhihu.js"
},
...
这样以后如果你写了别的快捷功能都可以加在这里进行统一管理
实现搜索功能
const search = () => {
const promptList = [
{
type: "input",
message: "请输入搜索内容:",
name: "topic",
},
];
inquirer.prompt(promptList).then((answers) => {
https: open(
"https://www.zhihu.com/search?type=content&q=" + answers.topic,
{
app: {
name: open.apps.chrome,
},
}
);
});
};
搜索功能更加简单 只需通过inquirer的.prompt方法 提示并获取输入值 拼接到跳转链接即可
完整代码
//zhihu.js
import Nightmare from "nightmare";
import inquirer from "inquirer";
import open from 'open'
const nightmare = Nightmare({ show: false });
const search = () => {
const promptList = [
{
type: "input",
message: "请输入搜索内容:",
name: "topic",
},
];
inquirer.prompt(promptList).then((answers) => {
https: open(
"https://www.zhihu.com/search?type=content&q=" + answers.topic,
{
app: {
name: open.apps.chrome,
},
}
);
});
};
const hotTopic = () => {
nightmare
.goto("https://www.zhihu.com/knowledge-plan/hot-question/hot/0/hour")
.wait("div[role=list]>.css-vurnku")
.evaluate(() => {
let arr = [];
let doms = document.querySelectorAll(
"div[role=list]>.css-vurnku>div>div>a"
);
doms.forEach((item, index) => {
arr.push({ name: doms[index].innerText, href: doms[index].href });
});
return arr;
})
.then((arr) => {
const promptList = [
{
type: "rawlist",
message: "topics:",
name: "topic",
choices: arr.map((i) => i.name),
filter: (val) => arr.findIndex((i) => i.name == val),
},
];
inquirer.prompt(promptList).then((answers) => {
open(arr[answers.topic].href, {
app: {
name: open.apps.chrome,
},
});
});
})
.then((arr) => {});
};
const promptList = [
{
type: "rawlist",
message: "热门还是搜索?",
name: "type",
choices: ["热门", "搜索"],
},
];
inquirer.prompt(promptList).then((answers) => {
answers.type == "热门" && hotTopic();
answers.type == "搜索" && search();
});
本文举例的功能比较简单 有手就行 主要目的还是文章开头说的 抛砖引玉
nightmare.js为我们提供了一个完全模拟真实用户操作的浏览器环境
一旦你熟悉了它的使用方法 我相信你会有更多 更实用 更具想象力的点子去实现
理论上来说只要能在浏览器中展示和操作的内容 你都可以使用nightmare.js 在命令行中搞定
以上就是本篇文章的全部内容
如果你觉得本文对你有所帮助的话 不妨关注点赞支持我哦
(完)