【搭建你的日记本】在文章正文中添加天气信息

2023.7.7 Update:
在请求位置信息时请不要开启全局代理,否则将无法获取省份导致脚本无法正常解析

Introduction

之前一直使用DayOne来写日记,但是DayOne的免费版本是不支持多平台同步的,于是我便考虑将日记从DayOne迁移到Obsidian。由于Obsidian本身带有一个日记的核心插件,其实不需要多做什么准备就可以开始写了,但是和专门的日记App相比,Obsidian还是简陋了一些,其中一点就是没法自动添加天气、位置等信息。

Related Research

有了这个需求之后我就到论坛来搜索有没有现成的解决方案,的确找到了一些:

请教:如何在日记笔记里自动加天气?

这篇文章里面提到了一个插件 Templater ,并给出了利用这个插件实现插入天气的代码。原理很简单,这个插件可以通过嵌入js代码的方式从外部获取信息,并在正文的任意位置进行插入。原文中的解决方案是去请求一个天气网站(https://www.tianqi.com/beijing/),得到结果后用正则表达式去处理,提取出需要的天气、气温等信息。

<%*
let url = 'https://www.tianqi.com/beijing/' 
let res = await request({url: url,method: "GET"}); 
res = res.replace(/\n/g,'') 
let dqwd = /<p class="now"><b>(\d+)<\/b><i>℃<\/i><\/p>/.exec(res)
let tqwdfw = /<span><b>(.*?)<\/b>(.*?)<\/span>/.exec(res)
let sdfxzwx = /<dd class="shidu"><b>(.*?)<\/b><b>(.*?)<\/b><b>(.*?)<\/b><\/dd>/.exec(res)
let kqzlrcrl = /<dd class="kongqi" ><h5 style="background-color:#[0-9a-z]{6};">(.*?)<\/h5><h6>(.*?)<\/h6><span>(.*?)<br \/>(.*?)<\/span><\/dd>/.exec(res)
let 当前温度 = '当前温度:' + dqwd[1]+'℃'
let 天气 = '天气:' + tqwdfw[1]
let 温度范围 = '温度范围:' + tqwdfw[2]
let 湿度 = sdfxzwx[1]
let 风向 = sdfxzwx[2]
let 紫外线 = sdfxzwx[3]
let 空气质量 = kqzlrcrl[1] + ' ' + kqzlrcrl[2]
let 日出日落 = kqzlrcrl[3] + ' ' +kqzlrcrl[4]
-%>
<% 当前温度 %>
<% 天气 %>
<% 温度范围 %>
<% 湿度 %>
<% 风向 %>
<% 紫外线 %>
<% 空气质量 %>
<% 日出日落 %>

然而上述的方案有两个点不能满足我的需求:

  • 请求的城市是在代码里面写死的,不能根据我的真实位置来调整
  • 正则表达式的写法很不清晰,难以自己修改

Methods

为了解决这两个问题,需要先动态获取当前的位置,再通过当前的位置去查询天气,返回的结果最好是json格式。

获取当前位置可以通过IP定位服务实现,高德、腾讯、百度等都提供了该项服务:

要使用IP定位服务需要注册相应的账号,并申请一个API KEY,这一步平台上有相应的教程,很容易就可以申请到。这里我使用的是腾讯的IP定位服务,高德的定位总是会把我定位在江苏省,不知道是怎么回事。

请求这个IP定位的API会以Json的形式返回你的位置信息,其中我们需要用到的是adcode这个属性。查询天气用到的是高德的天气查询API,它可以根据我们第一步定位得到的adcode来查询对应地区的实时天气与天气预报:高德地图 Web服务 API:天气查询

按照文档中的说明申请一个API Key之后发送请求即可得到Json格式返回的天气信息,再在Templater里展示即可。

Results

最终完整的代码如下:

<%* 
let ipUrl = 'https://restapi.amap.com/v3/ip';
let weatherUrl = 'https://restapi.amap.com/v3/weather/weatherInfo'
let key = YOUR KEY

let tencentIpUrl = 'https://apis.map.qq.com/ws/location/v1/ip';
let tencentKey = YOUR KEY

let adcode = eval("(" + 
    await request({url: tencentIpUrl + `?key=${tencentKey}`, method: "GET"})
+ ")").result.ad_info.adcode

let 位置 = ''
let 天气 = ''
let 温度 = ''
let 风向 = ''
await fetch(weatherUrl + `?key=${key}&city=${adcode}`)
.then(res => res.json())
.then((data) => {
	let info = data.lives[0]
	
	console.log(info)
	
	位置 = info.province + '-' + info.city
	天气 = info.weather
	温度 = info.temperature_float + '℃'
})
-%>
🌻日期🌻 ::  <% tp.file.creation_date("YYYY MM DD dddd") %> 
⌚️时间⌚️ ::  <% tp.file.creation_date("HH:mm:ss") %> 
🌍位置🌍 ::  <% 位置 %>
☁️天气☁️ ::  <% 天气 %> ,<% 温度 %> 

效果如下:
image

写在最后

  • 这个方案并不完美,也有别的操作可以替代,例如有一些天气相关的插件可以使用:


    可以根据自身需要进行选择,适合自己的才是最好的。

  • 高德的天气API返回的信息不是特别多,如果需要更多的信息可以考虑换成和风天气API之类的更加专业的天气API,只是目前的信息已经可以满足我最基本的需求了。

7 个赞

请问我在使用您调用API的代码时,报错
image
可能是什么原因呢?困扰很久也没有找到答案,我已经用postman检查了接口和key都没有问题,是代码哪里有问题吗?


代码截图如上

这两个key应该是字符串呀,不然的话它当变量给解析了,就报这个无效的token。
key="1dxxxx"这样试试呢

感谢您!说来惭愧,我花了三个小时,都没跑起来,刚开始我给key加了引号,但是还是报错(当时是其他的错误)
我看request里对key做了处理,我以为就是要写成变量……然后会有处理,没想到问题出在这。
谢谢您这么快速的回复,我实在没办法才注册账号来留言,感谢!

我没有前端开发的经验,如果我没有理解错误的话,这样得到的天气信息应该是静态的吧,有什么办法可以在此基础上得到每日动态的天气吗

你说的对,因为 templatar 只会在创建文档的时候加载一次,所以这里获取到的就是你创建文档当时的天气预报数据,应该是没法动态改变的

StoneLord大佬的代码 有同学是不是报错,因为腾讯的API接口须要分配额度,申请完【腾讯位置服务】的帐号后, 在配额管理账户额度 →给【IP定位】分配额度,否则会获取不到数据报错。坑死我等小白了!

1 个赞

我这一块弄的确实有点问题,如果不想折腾额度啥的可以试试这位朋友修改后的代码,只需要一个key就可以了


这可能是什么原因啊?

ctrl+shift+i,看下控制台的错误提示

请问我这个是什么问题呢,报错这样

发现错误了,我使用了Eazy Typing这个插件,他会自动首行大小写 :rofl:,所以我复制的代码有问题

大佬,请帮我看看我的报错,没运行成功。

地理位置和天气都是用高德版, 当行政区划是省时, 位置会显示 广东-广东省, 以下代码进行了修复

<%* 
let weatherUrl = 'https://restapi.amap.com/v3/weather/weatherInfo'
let key = 'YOUR_KEY'

let ipUrl = 'https://restapi.amap.com/v3/ip';

let ipResult = JSON.parse(await request({url: ipUrl + '?key=' + key, method: "GET"}))
console.log("ipResult: ", ipResult)

let adcode = ipResult.adcode


let 位置 = ''
let 天气 = ''
let 温度 = ''
let 风向 = ''
位置 = ipResult.province + '-' + ipResult.city
await fetch(weatherUrl + `?key=${key}&city=${adcode}&extensions=all`)
.then(res => res.json())
.then((data) => {
	let info = data.forecasts[0]
	天气 = '🌅' + info.casts[0].dayweather + ' / 🌃' + info.casts[0].nightweather
	温度 = '🌅' + info.casts[0].daytemp_float + '℃' + '/ 🌃' + info.casts[0].nighttemp_float + '℃'
})
-%>
---
🌻日期🌻: <% tp.file.creation_date("YYYY MM DD HH:mm:ss") %>
🌙星期🌙: <% tp.file.creation_date("dddd") %>
⌚️时间⌚️: <% tp.file.creation_date("HH:mm:ss") %>
🌍位置🌍: <% 位置 %>
☁️天气☁️: <% 天气 %>
🌡️温度🌡️: <% 温度 %>
---
<%*
let 一言 = ""
let 来源 = ""
let 作者 = ""

await fetch('https://v1.hitokoto.cn/?c=d&c=h&c=i&c=j')
.then(response => response.json())
.then(data => { 
	一言 = data.hitokoto
	来源 = data.from
	作者 = data.from_who === null ? '佚名' : data.from_who
})
-%>
>[!quote] 一言
 <% 一言 %>  —— 《<% 来源 %>》 · <% 作者 %>

---
# Tracking


# Diary




---
<< [[<% tp.date.now("YYYY-MM-DD", -1) %>]] | [[<% tp.date.now("YYYY-MM-DD", 1) %>]] >>

效果如下:
image

1 个赞


请问这是什么情况