一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第8天,点击查看活动详情 。
本次开发命令行看小说的工具, 环境的搭建细节可以看之前的活动文章
安装依赖
cheerio
cheerio为服务器特别定制的,快速、灵活、实施的jQuery核心实现.
安装
pnpm add cheerio
开发
这次请求的是起点中文网的页面,侵权请联系我删除.
- 创建
readBook.ts文件。
封装请求, 起点web端的页面是服务端渲染的, 所以我们直接请求html, 做相应处理。
import { get } from "https";
/**
* 发送请求
* @param url 请求的页面地址
* @returns 页面html的字符串
*/
function getHttp(url: string): Promise<string> {
return new Promise((resolve, reject) => {
get(
url,
{
headers: {
"Content-type": "text/html",
},
},
(res) => {
res.setEncoding("utf8");
let rawData = "";
// 拼接数据片段
res.on("data", (d: string) => {
rawData += d;
});
res.on("end", () => {
resolve(rawData);
});
}
).on("error", (e) => {
reject(e);
});
});
}
使用cherrio,注入html字符产,可以像jQuery一样操作html, 接下来就不多赘述了,dom的操作就是前端的拿手好戏。 思路是先搜索, 在跳详情, 详情页有章节目录,处理之后根据输入跳转到内容页。
import { load } from "cheerio";
import { echo } from "./echo.js";
import ora from "ora";
import chalk from "chalk";
export async function readBook(options: Record<string, string>) {
const spinner = ora();
spinner.start(
chalk.blue("(温馨提示: 看小说 看正版 !!)正在从起点小说网中查询....")
);
const html = await getHttp(`https://www.qidian.com/soushu/${options.n}.html`);
const querySelect = load(html);
const bookList = querySelect(
".book-img-text li .book-mid-info .book-info-title a"
)
.map((_, el) => {
return {
name: querySelect(el).text(),
url: "https:" + el.attribs["href"],
};
})
.toArray();
spinner.succeed(chalk.blue("获取以下目录"));
console.log(
chalk.cyan(
bookList
.map((v, i) => {
return `${i}. ${v.name}`;
})
.join("\n")
)
);
const bookindex = Number(
await echo("获取到以上的近似书籍列表, 请输入要阅读的书籍编号: ")
);
spinner.start(chalk.blue("正在获取章节列表....."));
const chapter = await getHttp(bookList[bookindex].url);
const chapterHtml = load(chapter);
const chapterList = chapterHtml(".cf li .book_name a")
.map((_, el) => {
return {
name: chapterHtml(el).text(),
url: "https:" + el.attribs["href"],
};
})
.toArray();
spinner.succeed(chalk.green(`共获取${chapterList.length}章`));
let chapterIndex = Number(
await echo("请输入要阅读的章节(vip章节无法观看): ")
);
while(true) {
console.log(await readDetails(chapterList[chapterIndex - 1].url))
console.log('\n\n\n\n')
const isRead = await echo("是否阅读下一张(Y/N): ")
if(isRead !== 'Y') break
chapterIndex++
if(chapterIndex >= chapterList.length) chapterIndex = chapterList.length
}
}
- 在
inde.ts文件中添加book命令。
import { readBook } from "./readBooks.js";
// book
program
.command("book")
.description("看书")
.option('-n [name]', '书名')
.action(readBook);