文章目录
哈喽,大家好!我是「励志前端小黑哥」,我带着最新发布的文章又来了!
老规矩,小手动起来~点赞关注不迷路!
上篇文章,咱们对插件的技术方案进行了选型,项目终于可以投入正式开发了。
这篇文章,我们就来实现一下CSDN文章一键发布的核心逻辑。
喜报!!!目前插件的预览版本已经做好了,大家想提前体验的朋友,文末附下载链接,这里补充一个使用视频:
视频-插件使用效果
本人前几篇文章就已经使用上了,效果杠杠的!
需求回顾
此插件的大致需求是:一键发布vscode
保存的markdown
文件到CSDN
、知乎、掘进等三方平台。
插件的详细需求分析,请参考我的另一篇文章《基于vscode的markdown文章一键发布客户端 - 需求梳理》。
话不多说,开干!
界面流程分析
因为技术选型是puppeteer
,实质上就是一个浏览器,平常你怎么用的,你就调用它怎么操作。
所以首先,我们需要分析一下,要在CSDN发布一篇文章需要经过的步骤,这里就用图例的形式展示给大家吧!
第一步,打开浏览器,跳转到https://csdn.net
,这一步是浏览器的初始化阶段
第二步,扫码登录:
第三步,点击发文按钮:
第四步,输入标题、输入内容、点击保存草稿:
简单的几个步骤,一篇CSDN文章就保存好了,这几步非常关键,后面都会用到。
有了这几步,咱么就可以对应的写一下入口函数了!
代码实现
入口函数设计
这里就直接贴上我写的代码吧!
/**
* CSDN发布文章的入口函数
*/
export async function startSync() {
// 第一步,初始化
await pageInit();
// 第二步,登录并跳转至编辑器
await loginAndRedirect();
// 获取vscode编辑器的markdown文本,并完成转换
const mdTextToEditor = await contentCreator();
// 第三步,输入标题、输入内容
await inputResult(mdTextToEditor)
// 第四步,保存
const url = await saveArticle();
// 第五步,结束
await pageExit();
}
可以看到,代码里的步骤,与我们上一小节文章内的步骤,基本是一模一样的,每一步都对应一个处理函数。
通过一步步的拆分,咱们把整个流程分解为了一个个小小的函数。现目前咱不用管函数内的具体实现,把基础框架先搭建出来,下面我就带大家慢慢的来实现这些函数。
Tips:这一步在软件开发过程中叫
接口设计
,其实不止是后端开发才有接口设计,前端开发过程中也是需要的!
页面初始化函数的实现 pageInit
首先第一个要实现的函数是pageInit
,函数的功能和相关知识点,是我们需要提前知道的。
此处提醒一下新手的开发朋友,
API文档
只是工具,有需要的时候拿出来查一查就行了!没必要死记硬背的。
下面就贴一个大致实现代码吧,如下:
import * as puppeteer from "puppeteer";
// page全局访问
const page:puppeteer.Page;
/**
* browser初始化、page初始化
*/
async function pageInit() {
// 打开浏览器
const browser = await puppeteer.launch({
// 本地浏览器的路径,我这里是MacOS的地址
executablePath: "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
args: [
'--no-sandbox', '--disable-setuid-sandbox', '--disable-blink-features=AutomationControlled', '--enable-automation=false'
],
headless: true,
channel: 'chrome',
defaultViewport: {
width: 1200,
height: 768
}
});
// 新开页面
page = await browser.newPage();
// 跳页至https://csdn.net/,直到无新的网络加载才返回
await page.goto(
'https://csdn.net/',
{ waitUntil: ['domcontentloaded', 'load', 'networkidle0'] }
);
}
登录函数的实现 loginAndRedirect
同理,我们需要知道登录函数的功能以及相关的知识点:
- 函数的主要功能:扫码登录CSDN、跳转至CSDN的编辑器界面
- 相关的知识点:
puppeteer
的page操作
大致如下:
/**
* 校验登录、打开csdn的md编辑器
*/
async function loginAndRedirect() {
// 校验登录
const { success, imgurl } = await checkLogin(page);
// 未登录时提示登录
if (!success) return '请扫码登录!';
// 跳页
await page.goto(
'https://editor.csdn.net/md/',
{ waitUntil: ['domcontentloaded', 'load', 'networkidle0'] }
);
}
/**
* 校验登录,方法:同时寻找登录的扫码图片和头像,谁先找到就用谁返回
* 若未登录,还将返回扫码二维码url
* @param page
* @returns {Promise<{success: boolean;imgurl: string;}>}
*/
async function checkLogin(page: Page): Promise<{ success: boolean; imgurl: string; }> {
// 登录二维码
const loginTask = page.waitForSelector('.login-box .login-code-wechat .public-code');
// 头像任务
const avatarTask = page.waitForSelector('#csdn-toolbar .toolbar-inside.exp3 .toolbar-btn.toolbar-btn-login-new a.hasAvatar', { timeout: 120000 });
// 登录结果
const result = {
// 成功 or 失败
success: false,
// 扫码登录的二维码地址
imgurl: ''
}
return new Promise(resolve => {
loginTask.then(async loginElement => {
const imgurl = (await page.$eval('.login-box .login-code-wechat .public-code>img', (ele) => (ele as HTMLImageElement).src))!;
result.success = false;
result.imgurl = imgurl;
resolve(result);
}).catch(e => {
resolve(result)
})
avatarTask.then(ele => {
result.success = true;
resolve(result)
}).catch(e => {
resolve(result)
})
})
}
上面的代码,最关键的部分要数checkLogin
这个子方法了。它的功能是:同时寻找未登录时的二维码
和已登录时的头像
,判断谁先找到,从而判断是否已登录CSDN
。这个设计是不是非常巧妙呢?
同理,我们再继续实现剩下来的几个函数接口吧
输入内容函数的实现 inputResult
- 函数的主要功能:输入标题、输入内容
- 相关的知识点:
puppeteer
的page操作、vscode
插件的document操作
/**
* 将最终结果输入到编辑器、输入框
* @param mdTextToEditor
*/
async function inputResult(mdTextToEditor: string) {
// 输入内容
const editor = await page.$('.editor__inner')
await editor?.type(mdTextToEditor);
// 输入标题,标题取markdown的文件名
const input = await page.$('.article-bar__input-box .article-bar__title--input')
await input?.type(vscode.window.activeTextEditor.document.fileName.split('/').pop() + '');
}
文章保存函数的实现 saveArticle
- 函数的主要功能:点击保存按钮、判断成功
- 相关的知识点:
puppeteer
的page操作
/**
* 保存文章草稿
* @param page
*/
async function saveArticle() {
const submitBtn = await page.$('.article-bar__user-box>button.btn-save');
submitBtn?.click()
// 点击保存后,有一次重定向,监听此次重定向来判断是否成功
await page.waitForNavigation();
const url = await page.url();
console.log('草稿保存成功', url)
return url
}
界面退出函数的实现 pageExit
- 函数的主要功能:关闭浏览器、关闭界面
- 相关的知识点:
puppeteer
的page操作
async function pageExit() {
// 关闭界面
await page.close()
// 关闭浏览器
await page.browser().close();
}
至此,整个流程的核心逻辑就实现完了!可以看到,所需的知识点都是puppeteer
,只要掌握了它,其实是很简单的!
问题与优化
核心代码倒是很简单,不过本人在开发过程中也做了很多的优化。这些优化的代码并不是本文的重点,这里就不一一贴出来了,仅罗列在此:
puppeteer
如何防止csdn
的自动化检测puppeteer
如何存储上一次的登录状态puppeteer
如何设置UserAgent
puppeteer
如何设置代理,并输入秘钥puppeteer
如何防止界面弹窗puppeteer
如何上传图片markdown
文档如何处理- …
这些优化问题,如果你也遇到了,欢迎留言或者给我发私信,很乐意给你解答!
插件预览版安装
现目前的开发进度,已支持CSDN平台的一键发布,欢迎大家下载使用!
链接:https://pan.baidu.com/s/1zlF-2YnJEJKVW6lpt_COwg
提取码:5khx
链接7天有效,过期了可以私聊我哦~
结语
这篇文章,介绍了CSDN的登录发文步骤,使用接口的形式对每个步骤进行了细分,还采用puppeteer
实现了插件的核心逻辑。开发过程中遇到的优化点,欢迎大家留言私信!
预告
下篇文章,咱们就来看看这个核心逻辑怎么嵌入到VSCODE
,从而形成一个完整可运行的插件,敬请期待!
励志前端小黑哥,全网唯一账号!
关注我,带你了解更多前端知识!