使用jekyll搭建站点,用markdown编写POST,如果图片都存在本地,而且repo存在类似github等国外网站的时候,速度就会非常慢。这个时候,如果图片放在图床或者对象存储器上,就比较轻量化了。我有一个腾讯云账号,对象存储服务每个月有10G的免费流量。之前都是使用自己写的上传脚本上传图片,然后将回传的地址复制到markdown文件中。

如果直接在vscode粘贴图片的过程就实现了上传,那就比较好了。搜索了一下,发现tencent-cloud-cos-upload-image这个插件可以实现该功能。不过使用的过程中,发现了在windows平台使用过程中的一个小bug。默认在windows中,path.seq为\符号,但是这个符号在cos中会被转移成文件名的一部分,导致无法正常上传到目标文件夹中。这个时候只能修改插件的代码了。

  • 首先,如下图所示复制插件的id

复制插件的id

  • 根据id检索到插件所在的位置

修改插件代码

如上图所示,打开cos.js文件夹,增加红色方框处的代码,将path.seq从\更改成/,这样cos存储服务器可以正确识别路径,可以正确将图片文件上传到目标路径下了。

安装了anaconda之后,有时安装扩展包的时候,可能出现如下的权限错误:

The current user does not have write permissions to the target environment

这个错误代表当前用户没有访问anaconda文件夹的权限,一个临时的解决办法就是安装扩展的时候,使用包含了administrator权限的终端进行安装。另外一个方法就是修改anaconda的权限。

修改权限

禁止继承,编辑权限

安装

vscode-powertools是一款非常好用的vscode插件,可以用来自定义一些命令,菜单和按钮功能,而不必自己去开发插件。

安装的时候,直接去vscode中,搜索名称Power Tools的插件,点击安装即可。

自定义命令

在vscode的settings.json中插入类似的代码块即可定义命令(参考:https://github.com/egodigital/vscode-powertools/wiki/Commands):

{
    "ego.power-tools": {
        "commands": {
            "myCommand": {
                "script": "my_command.js",
                "button": {
                    "text": "Click here to start the command."
                }
            }
        }
    }
}

这里,myCommand这个键及其子对象,都是可以重复定义的,这样可以用来定义更多的命令。而对于my_command.js的位置,如果设置在C:\Users\<username>\.vscode-powertools是比较妥当的。

运行命令

命令的运行实视settings.json中定义的不同而有不同的运行方式:(1)如果包含了Button的定义,则在状态栏就会存在相应的按钮;(2)如果包含了forFile或者forFolder的定义,则在SideBar中可以通过右键文件或者文件夹的而运行Excute Power Command菜单;(3)或者直接按下CTRL+SHIFT+P快捷键,输入PTC三个键,运行Power Tools: Commands命令,然后选中需要运行的命令即可。

使用Zotero软件可以非常方便的将一个文件夹中的数据转换成bib后缀的文件。而如果我们想要在jekyll中发布我们的论文列表,则需要转换成jekyll支持的比如yaml、json格式的数据。因此,我使用python脚本写了一个转换的脚本,暂且将转换过程设计的主要代码说明如下:

安装python包

首先我们需要安装bibtexparser包,该包可以非常方便的加载bib文件,使用pandas将数据转换成Dataframe,就可以进行下一步处理了。

with open("./papers.bib", encoding='utf-8') as bibtex_file:
    bib_database = bibtexparser.load(bibtex_file)

df = pd.DataFrame(bib_database.entries)

格式化数据

  • 生成子集

由于bib文件包含了一些冗余的信息,如果不是很想要,可以Copy一个Dataframe出来,然后选择目标字段。

# 由于df_sel后续需要执行赋值,因此必须是df的copy版本
df_sel = df.loc[:, ['author', 'title', 'booktitle', 'journaltitle', 'date', 'volume', 'number', 'pages', 'doi']].copy()

# 将null值复制为空字符串
df_sel.fillna('', inplace=True)

需要注意的是,必须使用loc索引以及copy函数,否则后面df_sel进行再次处理的时候会产生SettingwithCopy错误。

  • 格式化列数据
for k, v in df_sel.items():
    if k == 'date':
        df_sel[k] = df_sel[k].apply(lambda p: p.split('-')[0])
    if k == 'author':
        df_sel[k] = df_sel[k].apply(lambda p: re.sub(r'(Xing, [a-zA-Z-]+)', r"<u>\1</u>", p))
    if k == 'pages':
        df_sel[k] = df_sel[k].apply(lambda p: re.sub(r'[-]+', '-', p))
    if k == 'title' or k == 'booktitle':
        df_sel[k] = df_sel[k].apply(lambda p: re.sub(r'(\{|\}|\\)', '', p))
    if k == 'journaltitle':
        df_sel[k] = df_sel[k].apply(lambda p: re.sub(r'\\', '', p))

apply类似map函数,可以方便的将列数据当做数组进行处理。其中author列的操作主要是将当前作者的名字下划线处理。

  • 分组以及转换成json数据
js = df_sel.groupby('date').apply(lambda x: json.loads(x.to_json(orient='records'))).sort_index(ascending=False)

groupby之后,如果不apply,则每个date对应的是一个字数穿列表,使用apply之后就是一个json列表。

  • 保存数据
js.to_json('./papers.json', indent=4, force_ascii=False)

至此,一个按照年份分组的JSON格式的文献引用数据文件就生成了。

引用文件

将该文件放在jekyll网站目录的_data文件夹中,就可以使用json格式的数据了,这些数据可以在markdown、html文件中使用,只需要使用liquid代码即可。下面是一个示例的格式化代码。


<h4>{{kp[0]}}</h4>
{% for paper in kp[1] %}
    <li style="margin-bottom: 10px;line-height: 1.5em;">
    {{paper.author}}, 
    {% if paper.doi =="" %}
        <i>{{paper.title}}</i>
    {% else %}
        <a href="https://doi.org/{{ paper.doi }}" target="_blank"><i>{{paper.title}}</i></a>
    {% endif %}. 
    {{paper.journaltitle}}
    {{paper.booktitle}}, 
    {{paper.date||date: "%Y"}}. 
    <b>{{paper.volume}}</b>
    {% if paper.number != "" %}
        ({{paper.number}})
    {% endif %}: p{{paper.pages}}
    </li>
{% endfor %}
{% endfor %}

如果展示Liquid模板内容,参考:https://www.jasongaylord.com/blog/2020/05/31/displaying-liquid-templates-in-jekyll-code-blocks

使用python的requests包执行网络请求非常方便,可以便利地设置headers、cookies以及请求数据。根据多年使用requests包的经验,以及最近给一个客户的写的代码,下面总结出该包的最佳实践方式。

整体的结构

def read_header_file(header_path):
    # 加载代码

if __name__ == '__main__':
    s = requests.Session()
    read_header_file(header_path)
    # 执行请求,无论是get或者post,则无需再设置headers和cookies
    s.get('<url>')

headers以及cookies的获取

如果使用chrome浏览器,则按F12键后可以调出浏览器的开发工具,然后按照下图所示的方式可以获取到请求的headers和cookies,将其保存为一个文件。

headers和cookies获取的示意图

如果对于cookies的要求细节比较多,那么可以在chrome浏览器中安装EditThisCookie插件,然后按照如下图的方式获取Json格式的cookies。

使用EditThisCookie获取Json格式的cookies

加载headers和cookies

import pandas as pd

def read_header_file(header_path):
    df = pd.read_csv(header_path, skiprows=1, skipfooter=1,
                     header=None, engine="python", sep=r"\s*'\s*", usecols=[1])
    for _, r in df.iterrows():
        v = r.values[0]
        if 'cookie' in v.lower():
            jc = v.split(': ', 1)
            for kv in jc[1].split(';'):
                s.cookies.update(dict([kv.split('=', 1)]))
        else:
            s.headers.update(dict([v.split(': ', 1)]))

其中header_path变量指的就是之前保存的bash格式的请求文件。

pandas中的read_csv函数非常强大,可以非常方便将一些没有结构化的数据转换成结构化数据。dict函数可以将类似[[a, b]]转换成字典。或者结合for...in...语句,将更多偶数的列表转换成字典。

# INPUT: a = [1, 2, 3, 4]
[dict([a[i:i+2]]) for i in range(0, len(a), 2)]
# OUTPUT: [{1: 2}, {3: 4}]