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") %>
🌍位置🌍 :: <% 位置 %>
☁️天气☁️ :: <% 天气 %> ,<% 温度 %>
效果如下:
写在最后
-
这个方案并不完美,也有别的操作可以替代,例如有一些天气相关的插件可以使用:
可以根据自身需要进行选择,适合自己的才是最好的。 -
高德的天气API返回的信息不是特别多,如果需要更多的信息可以考虑换成和风天气API之类的更加专业的天气API,只是目前的信息已经可以满足我最基本的需求了。