查看原文
其他

使用Go语言批量同步微信读书笔记到Flomo

👆点击“博文视点Broadview”,获取更多书讯

“微信读书”和“Flomo”是我们常用的阅读和卡片记忆法软件。

在实际使用时,我们经常会有这样的需求:将微信读书中划线的笔记导入Flomo。传统的做法是先复制文字内容,然后打开Flomo,粘贴文字内容,输入适当的标签后保存,然后回到微信读书中继续阅读。

这一套动作下来,不仅要点击和切换好几次,连续阅读的体验也被破坏了。

恰好,Flomo提供了API,允许开发者通过API添加内容。按照官方文档的指引,使用Go语言就可以实现将微信读书中的笔记批量同步到Flomo的功能。

下面就将方法分享给大家!



01

操作方法

具体来说,实现这一功能的小程序是一个命令行应用程序。在编译出的可执行文件后添加help参数可以看到具体的使用方法,如下图所示。

首先,准备好要提交的笔记内容,从微信读书App中直接将内容复制出来,然后保存到纯文本文件中,比如下面这样。

显然,这里有3条划线笔记,意味着最终将添加3条Flomo。

接下来,想想这3条笔记的标签,我们也可以一次性添加多个标签。

最后,执行以下命令,在若干日志输出后,提示“全部笔记提交完成”,如下图所示。

main笔记,认知 C:\Users\wh199\Desktop\认知红利笔记.txt

打开Flomo,就可以看到刚刚提交的3条笔记了。

如此一来,我们可以专注于阅读和画线,在一个阅读阶段完成后,导出一次笔记。运行一次程序,相应的笔记内容就会被批量同步到Flomo中。是不是特别方便?



02

核心技术点

使用上述小程序确实能省去很多操作,节约很多时间。开发这个程序的过程也非常简单(仅需30分钟左右)。下面我们来细数这个小程序的核心技术点。

  • 命令行参数读取:用户需要“告知”程序读取哪个文件,添加哪些标签;

  • 字符串解析与文件读取:确保将文本文件中的笔记准确地读取出来;

  • 网络请求和解析:将读取出来的每条笔记通过Flomo API进行提交。

就这些吗?对!就只有这些!



03

代码实现

下面,我们基于代码开发,一步步实现这个小程序。

命令行参数读取

Go SDK中内置了os包os.Args,它可以实现对执行参数的获取。这是一个[]string类型的变量,里面包含着该程序执行时给定的参数。

如果不添加任何参数,执行main.exe的方式为:

main

此时,os.Args中包含1个元素——main。

如果添加了参数,如“help”,执行main.exe时,方式为:

main help

此时,os.Args中包含2个元素——main和help。

由此,我们便可实现以下功能:输出程序的“帮助文档”、解析标签组的内容、解析文件路径。

具体代码如下:

if len(os.Args) >= 2 {if os.Args[1] == "help" {fmt.Println("▶▶ 当存在2个命令行参数时:")fmt.Println("▶▶ 第一个参数是标签,多个标签以逗号隔开;")fmt.Println("▶▶ 第二个是文件输入源,要求同目录下的完整文件名。")fmt.Println("��� 注意1:文件要求UTF-8编码,内容直接粘贴微信读书导出的内容即可。��� ")fmt.Println("��� 注意2:单日最多可上传100条memo。��� ")fmt.Println("。◕‿◕。")return} // 解析标签tags = "#" + strings.ReplaceAll(os.Args[1], ",", " #")fmt.Println("笔记标签为:", tags) // 读文件,并获取笔记信息singleExcerpts = readFile(os.Args[2])fmt.Println("总共笔记数量:", len(singleExcerpts)) // 循环方式,提交每条笔记for i := 0; i < len(singleExcerpts); i++ {if singleExcerpts[i] != "" && singleExcerpts[i] != "\n" {singleExcerpts[i] = strings.ReplaceAll(singleExcerpts[i], ">>", "")fmt.Println("--------------------------------------------------")fmt.Printf("提交第%d条笔记\n%s\n", i, singleExcerpts[i])upload(tags, strings.TrimSuffix(singleExcerpts[i], "\n"))fmt.Println("--------------------------------------------------")}}fmt.Println("全部笔记提交完成")}

这段代码实际上就是main()函数的全部内容,它实现了程序执行的完整流程。

解析标签无须多说,当我们在命令行中给定“笔记,认知”作为标签时,程序将替换“,”为“ #”(注意:此处时空格加上井号)。再回到开头补足首个标签的“#”,最终保存到tags中,成为:“#笔记 #认知”。

文件路径保存在os.Args[2]中,readFile()是读文件,并解析每条笔记的函数,它最终将返回[]string类型值。于是,singleExcerpts便包含了所有的笔记,它是[]string类型的变量。

最后,根据singleExcerpts的长度,通过for结构的循环进行提交。upload()函数是具体的提交逻辑,需要标签和单条笔记的文本内容。

读取文件、解析字符串

文件的读取和全部笔记的分割通过readFile()函数来完成。该函数需要传入完整的文件路径,最终返回包含分割好的每条笔记的string类型切片。

读文件用到两个包,一个是os,另一个是bufio。

仔细观察导出的文本内容,一开始是书名、作者和笔记个数统计,“◆ ”开头表示章节名,“>> ”开头表示单个划线内容。这三者之中,我们只取最后一类即可。

因此,思路是这样的:按行读取文本文件,遇到“◆ ”时,表示接下来将会有具体的划线笔记。遇到“>> ”时,将其汇总到另一个string类型变量中(fullContentFiltered)。重复上述过程,直到文件末尾。最后,以“>> ”为依据,对fullContentFiltered进行拆分,并辅以Trim操作,即可得到单条笔记了。

完整代码如下:

// 读文件内容(UTF-8编码兼容)func readFile(filepath string) []string { var returnData []string file, err := os.Open(filepath) if err != nil { panic(err) } defer file.Close() fileScanner := bufio.NewScanner(file)

fullContentFiltered := "" contentBegin := false

for fileScanner.Scan() { singleLine := fileScanner.Text() if strings.HasPrefix(singleLine, "◆ ") || strings.HasPrefix(singleLine, ">> ") { contentBegin = true } if !strings.HasPrefix(singleLine, "◆ ") && singleLine != "" { if contentBegin { fullContentFiltered += singleLine + "\n" } } } fullContentFiltered = strings.ReplaceAll(fullContentFiltered, ">> ", ">>") fullContentFiltered = strings.TrimSuffix(fullContentFiltered, "\n") fullContentFiltered = strings.TrimPrefix(fullContentFiltered, " ") returnData = strings.Split(fullContentFiltered, ">>") return returnData}

网络请求和解析

最后就是网络请求,这里要结合Flomo的官方文档,并使用http、ioutil和json包进行。这一步较为简单,这里就不再详述了,具体代码如下:

// 请求flomo接口,提交数据func upload(tags string, content string) { content = content + "\n\n" + tags uploadDataObj := Content{ Content: content, } uploadData, _ := json.Marshal(uploadDataObj) resp, err := http.Post("https://flomoapp.com/iwh/xxxxxx/", "application/json", strings.NewReader(string(uploadData))) if err != nil { fmt.Println(err) } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { panic(err) } fmt.Println(string(body))}

到此,整个小程序的代码实现就完成了。



04

还能做什么?

通过实现这样一个简单的程序,我们能得到什么灵感呢?做些改进,通过书名自动添加标签?或是增加更多的字符串解析方法,做一个更通用的程序?或是绘制一个GUI,让用户自己填写识别字符串的正则表达式?

Go语言可以做服务器软件,做起客户端软件来也丝毫不含糊。高效的开发效率不仅可以节省开发者的时间,还“鼓励”着人们亲自动手,方便自己的生活与学习。毕竟,自己写一个这样的小工具比找一个现成的更省时间。


想要使用Go语言实现更多有意思的项目吗?

欢迎阅读《Go语言从入门到项目实战(视频版)》一书了解更多哦~~


粉丝专享六折优惠,快快扫码抢购吧!

发布:刘恩惠

审核:陈歆懿

 

如果喜欢本文欢迎 在看留言分享至朋友圈 三连

 热文推荐  





▼点击阅读原文,了解本书详情~

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存