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

博文小编

2022-08-25

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

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

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

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

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

01 操作方法

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

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

接下来,想想这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语言从入门到项目实战(视频版)》一书了解更多哦~~

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

发布:刘恩惠

审核:陈歆懿

读者评论

相关博文

  • 社区使用反馈专区

    陈晓猛 2016-10-04

    尊敬的博文视点用户您好: 欢迎您访问本站,您在本站点访问过程中遇到任何问题,均可以在本页留言,我们会根据您的意见和建议,对网站进行不断的优化和改进,给您带来更好的访问体验! 同时,您被采纳的意见和建议,管理员也会赠送您相应的积分...

    陈晓猛 2016-10-04
    5700 747 3 7
  • 迎战“双12”!《Unity3D实战核心技术详解》独家预售开启!

    陈晓猛 2016-12-05

    时隔一周,让大家时刻挂念的《Unity3D实战核心技术详解》终于开放预售啦! 这本书不仅满足了很多年轻人的学习欲望,并且与实际开发相结合,能够解决工作中真实遇到的问题。预售期间优惠多多,实在不容错过! Unity 3D实战核心技术详解 ...

    陈晓猛 2016-12-05
    3427 36 0 1
  • czk 2017-07-29
    6277 28 0 1