功能

从豆瓣收集图书 _ 电影 _ 电视剧信息,插入 Obsidian 笔记。

原理

用 JS 抓取网站内容填入模板,插入笔记,通过插件之间的配合实现强大功能。

设置

  • 安装 QuickAdd 插件
  • 把 js 文件复制到 templates/script/目录下
  • 把模板复制到 templates/目录下
  • 在左下角设置界面调出 QuickAdd 的设置界面
|400
  • 加 Micro
    • 点 Manage Micros->起个名 ->Add Micro
    • 加好 Macro 后点其对应的 Configure 设置
    • 加 JS 脚本
      • 在 User Scripts 中选刚才拷到 script 下的脚本,然后点其后的 Add
    • 点 Template 按钮,加上一个模板后,设置其内容
      • 在 Template Path 中设置刚才拷进的模板
      • 勾选 File Name Format
      • 在 File Name 中输入:{{VALUE:name}}
      • 双击顶部的模板名,设置成自定义名称
      • 点右上的叉退出,自动保存
  • 连接显示名称和 Macro
    • 进入最顶层的 QuickAdd Setting 界面(上图所示界面)
    • 起一个最终显示的名字,选 Macro,点 Add Choice
    • 设置刚生成项目,将其与前面设置的 Macro 连接
  • 点亮对应的 Add Command for xxx(打雷图标)

添加豆瓣

  • Ctrl+P 调出菜单
  • 选择 QuickAdd 新建的功能

其它

  • 稍微改一下 js 脚本,支持 IMDb,就可以支持收集电影电视剧信息。
  • 对于唯一标识,书是采用的是 ISBN;电影是 IMDb;音乐国外是条形码,国内是 ISRC。
  • 如果还想收集综艺,就把那个网页地址填入输入框。

代码

方法主要参考 " 参考 " 部分的代码和视频,我加了少量简化和修改

我的模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
---
title: {{VALUE:title}}
author: {{VALUE:author}}
transAuthor: {{VALUE:transAuthor}}
rating: {{VALUE:rating}}
tags: {{VALUE:tags}}
ISBN: {{VALUE:isbn}}
type: ReadNote
link: {{VALUE:link}}
cover: {{VALUE:coverUrl}}
pages: {{VALUE:pages}}
BeginDate: {{VALUE:today}}
---

# {{VALUE:name}}

![{{VALUE:name}}|300]({{VALUE:coverUrl}})

## 简介
### 书籍简介

{{VALUE:intro}}

### 作者简介

{{VALUE:authorIntro}}

另外,修改 js,兼容了综艺和电影和直接输入 http 地址,只简单加了一个,没做太多解释:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
// Author: @Lumos
// Url: https://github.com/LumosLovegood
const headers = {
"Content-Type": "text/html; charset=utf-8",
'Connection': 'keep-alive',
'Upgrade-Insecure-Requests': '1',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
'sec-ch-ua': '" Not A;Brand";v="99", "Chromium";v="98", "Google Chrome";v="98"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
'Sec-Fetch-Site': 'same-site',
'Sec-Fetch-Mode': 'navigate',
'Sec-Fetch-User': '?1',
'Sec-Fetch-Dest': 'document',
'Referer': 'https://m.douban.com/',
'Accept-Language': 'en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7',
}

async function douban(QuickAdd){
const user_input = await QuickAdd.quickAddApi.inputPrompt(
"请输入书籍背后的13位ISBN码/10位IMDb/网页地址:"
);
let simpleInfo={};
if(user_input.substring(0,4)!='http') {
if(user_input.length!=13 && user_input.length!=10 && user_input.substring(0,4)!='http'){
new Notice("输入错误");
throw new Error("输入错误");
}
simpleInfo =await getBookUrl(user_input);
if(!simpleInfo){
new Notice("无法识别此ISBN码");
throw new Error("无法识别此ISBN码");
}
} else {
simpleInfo.title='数据';
simpleInfo.url = user_input;
}

let url = simpleInfo.url;
new Notice("准备获取《"+simpleInfo.title+"》的内容信息",1000)
let bookInfo = await getDetailInfo(url)
if(!bookInfo){
new Notice("获取内容失败");
throw new Error("获取内容失败");
}
new Notice("笔记已生成!",500);
// 获取今日日期
const date = window.moment().format("gggg-MM-DD")
bookInfo.today = date;

QuickAdd.variables = {
...bookInfo
};
}

async function getBookUrl(isbn){
url = "https://m.douban.com/search/?query="+isbn;
let searchUrl = new URL(url);
const res = await request({
url: searchUrl.href,
method: "GET",
cache: "no-cache",
headers: {
"Content-Type": "text/html; charset=utf-8",
'sec-ch-ua': '" Not A;Brand";v="99", "Chromium";v="98", "Google Chrome";v="98"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
'Upgrade-Insecure-Requests': '1',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36'
}
});

if(!res){
return null;
}

let p = new DOMParser();
let doc = p.parseFromString(res, "text/html");
let title = doc.querySelector("div.subject-info span").textContent;
let detailUrl = String(doc.querySelector("ul li a").href).replace("app://obsidian.md","https://m.douban.com");
if (!detailUrl){
return null;
}
let simpleInfo={};
simpleInfo.title=title;
simpleInfo.url = detailUrl;
return simpleInfo;
}

async function getDetailInfo(url){
let bookUrl = new URL(url);
const res = await request({
url: bookUrl.href,
method: "GET",
cache: "no-cache",
headers: headers
});
let p = new DOMParser();
let doc = p.parseFromString(res, "text/html");
let $ = s => doc.querySelector(s);
let $2 = z => doc.querySelectorAll(z);

let bookInfo = {};
//书名、作者、ISBN、封面
let name = $("meta[property='og:title']")?.content;
let title = "\""+name+"\""; //用于放到front matter里,加引号避免因为包含特殊字符导致ymal解析错误
let author = "\""+$("meta[property='book:author']")?.content.replace(/[\[\]\(\)()]/g,"")+"\"";
let isbn = $("meta[property='book:isbn']")?.content;
let cover = $("meta[property='og:image']")?.content;

//其他信息(译者、原作名、页数)
let text = $("#info")?.textContent.replace("\n","");
let transAuthor = text.match(/(?<=译者:\s*)\S+\s?\S+/g)?text.match(/(?<=译者:\s*)\S+\s?\S+/g)[0].trim():"";
let originalName = text.match(/(?<=原作名:\s*)[\S ]+/g)?("\""+text.match(/(?<=原作名:\s*)[\S ]+/g)[0].trim()+"\""):"";
let pages = text.match(/(?<=页数:\s*)[\S ]+/g)?text.match(/(?<=页数:\s*)[\S ]+/g)[0].trim():"";
let publisher = text.match(/(?<=出版社:\s*)\S+\s?\S+/g)?text.match(/(?<=出版社:\s*)\S+\s?\S+/g)[0].trim():"";

//豆瓣评分
let rating = $("div#interest_sectl div div strong")?.textContent.replace(/\s/g,"");

//书籍和作者简介,这一块儿不同类型的书对应的网页结构都不太一样,尽力做兼容了,还有问题我也没办法 \摊手
let intro = "";
let authorIntro = "";
var temp1 = $("h2");
if(temp1.innerText.includes("内容简介")){
var temp2 = temp1.nextElementSibling.querySelectorAll("div.intro")
var temp3 = temp2[temp2.length-1].querySelectorAll("p");
for(var i=0;i<temp3.length;i++){
intro = intro+temp3[i].textContent+"\n";
}
try{
temp2 = $2("h2")[1].nextElementSibling.querySelectorAll("div.intro");
temp3 = temp2[temp2.length-1].querySelectorAll("p");
for(var i=0;i<temp3.length;i++){
authorIntro = authorIntro+temp3[i].textContent+"\n";
}
}catch(e){
new Notice("没有简介");
}
}else if(temp1.innerText.includes("作者简介")){
var temp2 = temp1.nextElementSibling.querySelectorAll("div.intro")
var temp3 = temp2[temp2.length-1].querySelectorAll("p");
for(var i=0;i<temp3.length;i++){
authorIntro = authorIntro+temp3[i].textContent+"\n";
}
}

//豆瓣常用标签,记得之前这一块儿网页元素里是有的,后来找不到了,但是尝试性源代码全文搜索的时候 在Script标签里找到了,但是感觉随时会改。
var temp = $2("script");
let tags = temp[temp.length-3].textContent.match(/(?<=:)[\u4e00-\u9fa5·]+/g);
if (tags === null) {
tags = [];
} else {
tags.push("book");
}

bookInfo.name = name;
bookInfo.title=title;
bookInfo.author=author;
bookInfo.transAuthor=transAuthor;
bookInfo.coverUrl=cover;
bookInfo.originalName=originalName;
bookInfo.pages=pages;
bookInfo.publisher=publisher;
bookInfo.intro=intro;
bookInfo.isbn=isbn;
bookInfo.rating=rating;
bookInfo.authorIntro =authorIntro;
bookInfo.tags=tags;
bookInfo.link = url;

// 如果为空的话,quickadd会出现提示框让自己填,太麻烦了,所以先填一个默认空值
for(var i in bookInfo){
if(bookInfo[i]==""){
bookInfo[i]="Not Found.";
}
}

return bookInfo;
}
module.exports = douban

参考

代码

视频