普通视图

发现新文章,点击刷新页面。
昨天以前NoneData

博客迁移至 VuePress

2022年5月25日 08:00

博客迁移至 VuePress

NoneData 的新域名和新前端设计启用了,这次首次从动态生成内容的网站换到的静态网站,选择的还是我熟悉的Vue平台。确实就如 VuePress 官网上 所说,对于一个个人博客类网站来说,直接使用 Nuxt.js确实有些太大材小用了。之前的 Lareina 就是基于 Nuxt.js 开发的一套CMS系统,主要还是 自己当时的兴趣所在,重写了 WordPress 的大多数功能,包括实现了后台管理、评论和邮件通知系统等。但当时更多的是我的练手制作,很多方面存在不足, 在版本的维护上也存在诸多困难,所以用着用着就很难有继续维护的动力了。所以这次选择了更加轻量化的 VuePress 2,同时拥有 Vue 3 的新特性和灵活性, 可以更多的把重点集中在内容上。

为什么不是 ...?

Nuxt Nuxt 是一套出色的 Vue SSR 框架, VuePress 能做的事情,Nuxt 实际上也同样能够胜任。但 Nuxt 是为构建应用程序而生的,而 VuePress 则更为轻量化并且专注在以内容为中心的静态网站上。

VitePress VitePress 是 VuePress 的孪生兄弟,它同样由 Vue.js 团队创建和维护。 VitePress 甚至比 VuePress 要更轻更快,但它在灵活性和可配置性上作出了一些让步,比如它不支持插件系统。当然,如果你没有进阶的定制化需求, VitePress 已经足够支持你将你的内容部署到线上。

这个比喻可能不是很恰当,但是你可以把 VuePress 和 VitePress 的关系看作 Laravel 和 Lumen 。

Docsify / Docute 这两个项目同样都是基于 Vue,然而它们都是完全的运行时驱动,因此对 SEO 不够友好。如果你并不关注 SEO,同时也不想安装大量依赖,它们仍然是非常好的选择!

Hexo Hexo 一直驱动着 Vue 2.x 的文档。Hexo 最大的问题在于他的主题系统太过于静态以及过度地依赖纯字符串,而我们十分希望能够好好地利用 Vue 来处理我们的布局和交互。同时,Hexo 在配置 Markdown 渲染方面的灵活性也不是最佳的。

GitBook 过去我们的子项目文档一直都在使用 GitBook 。 GitBook 最大的问题在于当文件很多时,每次编辑后的重新加载时间长得令人无法忍受。它的默认主题导航结构也比较有限制性,并且,主题系统也不是 Vue 驱动的。GitBook 背后的团队如今也更专注于将其打造为一个商业产品而不是开源工具。

设计理念

目前体验下来,VuePress 基本可以满足我的所有需求,静态网站最常面临评论系统和搜索功能,分别使用twikoo和自己编写插件解决了(虽然官方自带了两个搜索插件,但是 我还是更习惯自己写一个)。且 VuePress 自带了一个比较耐看的主题,默认主题虽然比较偏文档类,但是经过我的魔改,已经表现的很符合我的需求了。

设计上还是专注于简洁单栏主题,在大体设计上和原先本站的主题没有太大区别,还是以卡片类为主。

开发上基于 VuePress 2 的默认主题进行 extending 开发。这个继承功能属实舒适,本人在主题开发过程中,基本上一路绿灯,很快就完成了主体设计。

以下说下主要实现的功能:

  • 顶部自定义图片及描述设计
  • 卡片化设计
  • 文章分页(带路径更新)
  • 文章预览(可手动关闭,在小屏幕下自动禁用,支持方向键操作)
  • 多种主题色
  • 多种字体搭配
  • 自动黑暗主题
  • 控制面板(集成本地搜索功能,快捷键功能,可添加更多页面链接或者功能操作)
  • 评论功能(基于twikoo)
  • 文章密码
  • 文章信息(浏览量基于twikoo,阅读时间估计,文章目录,阅读进度,图片异步加载)
  • 文章内容(查看大图功能、各类标注、脚注等)
  • 可关闭单篇文章评论
  • 更多文章推荐
  • RSS
  • SiteMap
  • PWA
  • 全自动部署

数据迁移

由于之前的 Lareina 已经是基于 MarkDown 格式储存的日志了,所以只需要把内容从数据库中导出即可。写了个python脚本,很快处理完毕。 评论数据的迁移就比较头大了,主要要处理评论的勾稽关系。现在用的twikoo也不是很清楚导入的格式数据。等之后再慢慢研究导入。

最近逛了一圈大家的博客,发现好少有用 VuePress 建站的(捂脸。静态的基本还是Hexo为主。

网站评分

Google Lighthouse
Google Lighthouse

TODO

在该版本后的所有修改均在 todo 中显示:

  • 文章图库功能、文章图片描述功能
  • 文章列表会自动使用题图主题色作为阴影色
  • 更好看的引用样式
  • 更安全的文章加密(密码只储存在文章MD文件中,基于MD5与AES加密后发布,杜绝通过网页代码查看原文)
  • 数学公式支持(自动判断,仅在有数学公式的页面按需加载katex)
  • 代码复制按钮
  • 首页公告
  • Bionic Reading
  • 修改字体样式
  • 更多短代码样式(添加了常用的 进度条,时间线,选项卡,Github卡片)
  • 优化代码结构

算法刷题 6-10

2020年4月12日 08:00

算法刷题 6-10

将一个给定字符串根据给定的行数,以从上往下、从左到右进行 Z 字形排列。

6

比如输入字符串为 "LEETCODEISHIRING" 行数为 3 时,排列如下:

L C I R E T O E S I I G E D H N

之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:"LCIRETOESIIGEDHN"。

请你实现这个将字符串进行指定行数变换的函数:

string convert(string s, int numRows);

示例 1:

输入: s = "LEETCODEISHIRING", numRows = 3 输出: "LCIRETOESIIGEDHN"

示例 2:

输入: s = "LEETCODEISHIRING", numRows = 4 输出: "LDREOEIIECIHNTSG" 解释:

L D R E O E I I E C I H N T S G

6.1

比较简单的一道题,除了看规律之外,还可以用给方向变量来做。

class Solution:
    def convert(s: str, numRows: int) -> str:
        if len(s) <= numRows or numRows == 1:
            return s
                    
        ans = ""
        for row in range(0,numRows):
            start = row
            
            if (row == 0) or (row == numRows-1):
                while True:
                    ans = ans + s[start]
                    start = start + 2*(numRows-1)
                    if start > len(s)-1:
                        break
                    
            else:
                while True:
                    print("here")
                    ans = ans + s[start]
                    start = start + 2*(numRows-1-row)
                    if start <= len(s)-1:
                        ans = ans + s[start]
                        start = start+2*row
                        if start > len(s)-1:
                            break
                    else:
                        break

            
        return ans
    

s = "A"

print(Solution.convert(s,3))

7

给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转。

示例 1:

输入: 123 输出: 321

示例 2:

输入: -123 输出: -321

示例 3:

输入: 120 输出: 21

注意:

假设我们的环境只能存储得下 32 位的有符号整数,则其数值范围为 [−231,  231 − 1]。请根据这个假设,如果反转后整数溢出那么就返回 0。

7.1

简单,略过。

class Solution:
    def reverse(self, x: int) -> int:
        
        sign = ""
        if x < 0 :
            sign = "-"
            x = str(x)[1:]

        x = int(sign + str(x)[::-1])

        if x > 2**31 -1 or x < -2**31:
            return 0

        return x
	str_num = str(x)[::-1]
        if str_num.endswith('-'):
            str_num = '-' + str_num[:-1]
            return int(str_num) if int(str_num) >= -2**31 else 0
        return int(str_num) if int(str_num) <= 2**31 - 1 else 0

8

请你来实现一个 atoi 函数,使其能将字符串转换成整数。

首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。接下来的转化规则如下:

如果第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字字符组合起来,形成一个有符号整数。 假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成一个整数。 该字符串在有效的整数部分之后也可能会存在多余的字符,那么这些字符可以被忽略,它们对函数不应该造成影响。 注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换,即无法进行有效转换。

在任何情况下,若函数不能进行有效的转换时,请返回 0 。

提示:

本题中的空白字符只包括空格字符 ' ' 。 假设我们的环境只能存储 32 位大小的有符号整数,那么其数值范围为 [−231,  231 − 1]。如果数值超过这个范围,请返回  INT_MAX (231 − 1) 或 INT_MIN (−231) 。

示例 1:

输入: "42" 输出: 42

示例 2:

输入: " -42" 输出: -42 解释: 第一个非空白字符为 '-', 它是一个负号。   我们尽可能将负号与后面所有连续出现的数字组合起来,最后得到 -42 。

示例 3:

输入: "4193 with words" 输出: 4193 解释: 转换截止于数字 '3' ,因为它的下一个字符不为数字。

示例 4:

输入: "words and 987" 输出: 0 解释: 第一个非空字符是 'w', 但它不是数字或正、负号。 因此无法执行有效的转换。

示例 5:

输入: "-91283472332" 输出: -2147483648 解释: 数字 "-91283472332" 超过 32 位有符号整数范围。   因此返回 INT_MIN (−231) 。

8.1

简单

lass Solution:
    def myAtoi( str: str) -> int:
        str = str.lstrip()

        sign = 0
        for index,letter in enumerate(str):
            if index == 0:
                if (letter == '-' and len(str) > 1) or (letter == '+' and len(str) > 1) or letter.isdigit():
                    sign = "-" if letter == '-' else letter
                    continue
                else:
                    break

            if letter.isdigit():
                sign += letter
            else:
                sign = sign if sign != "-" and sign != "+" else 0
                break
        
        sign = int(sign)
        sign = sign if sign <= 2**31-1 else 2**31-1
        sign = sign if sign >= -2**31 else -2**31
            
        return sign
    
    
str = '+-2'

print(Solution.myAtoi(str))

9

判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。

示例 1:

输入: 121 输出: true

示例 2:

输入: -121 输出: false 解释: 从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。

示例 3:

输入: 10 输出: false 解释: 从右向左读, 为 01 。因此它不是一个回文数。

进阶:

你能不将整数转为字符串来解决这个问题吗?

9.1

简单,注意一下进阶问题!

class Solution:
    def isPalindrome(self, x: int) -> bool:
        # 68 ms , 在所有 Python3 提交中击败了 84.98% 的用户
        x = str(x)
        flag = False
        if x == x[::-1]:
            flag = True

        return flag

进阶

	origin = x
        if x < 0 or (x % 10 == 0 and x != 0):
            return False

        revertedNumber = 0
        while True:
            revertedNumber = revertedNumber * 10 + x % 10
            x //= 10
            if x < 10:
                revertedNumber = revertedNumber * 10 + x % 10
                break

        return (origin == revertedNumber or origin == revertedNumber/10)

10 ❌

给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 '.' 和 '*' 的正则表达式匹配。

'.' 匹配任意单个字符 '*' 匹配零个或多个前面的那一个元素 所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。

说明:

s 可能为空,且只包含从 a-z 的小写字母。 p 可能为空,且只包含从 a-z 的小写字母,以及字符 . 和 *。

示例 1:

输入: s = "aa" p = "a" 输出: false 解释: "a" 无法匹配 "aa" 整个字符串。

示例 2:

输入: s = "aa" p = "a*" 输出: true 解释: 因为 '*' 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 'a'。因此,字符串 "aa" 可被视为 'a' 重复了一次。

示例 3:

输入: s = "ab" p = "." 输出: true 解释: "." 表示可匹配零个或多个('*')任意字符('.')。

示例 4:

输入: s = "aab" p = "cab" 输出: true 解释: 因为 '*' 表示零个或多个,这里 'c' 为 0 个, 'a' 被重复一次。因此可以匹配字符串 "aab"。

示例 5:

输入: s = "mississippi" p = "misisp*." 输出: false

10.1

看不懂下面的算法

# 回溯
class Solution(object):
    def isMatch(self, text, pattern):
        if not pattern:
            return not text

        first_match = bool(text) and pattern[0] in {text[0], '.'}

        if len(pattern) >= 2 and pattern[1] == '*':
            return (self.isMatch(text, pattern[2:]) or
                    first_match and self.isMatch(text[1:], pattern))
        else:
            return first_match and self.isMatch(text[1:], pattern[1:])
# 动态规划
class Solution(object):
    def isMatch(self, text, pattern):
        memo = {}
        def dp(i, j):
            if (i, j) not in memo:
                if j == len(pattern):
                    ans = i == len(text)
                else:
                    first_match = i < len(text) and pattern[j] in {text[i], '.'}
                    if j+1 < len(pattern) and pattern[j+1] == '*':
                        ans = dp(i, j+2) or first_match and dp(i+1, j)
                    else:
                        ans = first_match and dp(i+1, j+1)

                memo[i, j] = ans
            return memo[i, j]

        return dp(0, 0)

CAP、AR、ROC、AUC、KS 监控模型的区分能力

2020年4月9日 08:00

CAP、AR、ROC、AUC、KS 监控模型的区分能力

AR值(Accuracy Ratio)和KS值(Kolmogorov-Smirnov)主要是为了监控模型的区分能力。

CAP Introduction

The cumulative accuracy profile (CAP) is used in data science to visualize the discriminative power of a model. The CAP of a model represents the cumulative number of positive outcomes along the y-axis versus the corresponding cumulative number of a classifying parameter along the x-axis (i.e. for a point (0.1,0.3) on the cap means that the worst 10% borrower given by the model includes 30% of defautls). The CAP is distinct from the receiver operating characteristic (ROC), which plots the true-positive rate against the false-positive rate.

An example is a model that predicts whether a borrower is default (positive outcome) by each individual from a group of people (classifying parameter) based on factors such as their working status, annual income, loan purpose etc.

If group members would be contacted at random, the cumulative number of defaults would rise linearly toward a maximum value corresponding to the total number of borrowers within the group. This distribution is called the "random" CAP.

A perfect prediction, on the other hand, determines exactly which group members will buy the product, such that the maximum number of products sold will be reached with a minimum number of calls. This produces a steep line on the CAP curve that stays flat once the maximum is reached (contacting all other group members will not lead to more products sold), which is the "perfect" CAP.

The CAP profiles for the perfect, good and random model predicting the default borrowers from a pool of 100 individuals.

A successful model predicts the likelihood of default individuals and ranks these probabilities to produce a list of potential customers to be contacted first. The resulting cumulative number of sold products will increase rapidly and eventually flatten out to the given maximum as more group members are contacted. This results in a distribution that lies between the random and the perfect CAP curves.

Modified by Xuan, By Victoriaweller - Own work, CC BY-SA 4.0, https://commons.wikimedia.org/w/index.php?curid=61434256

Analyse CAP

Analyzing a CAP The CAP can be used to evaluate a model by comparing the curve to the perfect CAP in which the maximum number of positive outcomes is achieved directly and to the random CAP in which the positive outcomes are distributed equally. A good model will have a CAP between the perfect CAP and the random CAP with a better model tending to the perfect CAP.

The accuracy ratio (AR) is defined as the ratio of the area between the model CAP and the random CAP and the area between the perfect CAP and the random CAP. For a successful model the AR has values between zero and one, with a higher value for a stronger model.

Another indication of the model strength is given by the cumulative number of positive outcomes at 50% of the classifying parameter. For a successful model this value should lie between 50% and 100% of the maximum, with a higher percentage for stronger models.

ROC Introduction

The ROC curve is created by plotting the true positive rate (TPR) against the false positive rate (FPR) at various threshold settings. The true-positive rate is also known as sensitivity, recall or probability of detection in machine learning. The false-positive rate is also known as probability of false alarm and can be calculated as (1 − specificity).

To draw an ROC curve, only the true positive rate (TPR) and false positive rate (FPR) are needed (as functions of some classifier parameter). The TPR defines how many correct positive results occur among all positive samples available during the test. FPR, on the other hand, defines how many incorrect positive results occur among all negative samples available during the test.

An ROC space is defined by FPR and TPR as x and y axes, respectively, which depicts relative trade-offs between true positive (benefits) and false positive (costs). Since TPR is equivalent to sensitivity and FPR is equal to 1 − specificity, the ROC graph is sometimes called the sensitivity vs (1 − specificity) plot. Each prediction result or instance of a confusion matrix represents one point in the ROC space.

The best possible prediction method would yield a point in the upper left corner or coordinate (0,1) of the ROC space, representing 100% sensitivity (no false negatives) and 100% specificity (no false positives). The (0,1) point is also called a perfect classification. A random guess would give a point along a diagonal line (the so-called line of no-discrimination) from the left bottom to the top right corners (regardless of the positive and negative base rates). An intuitive example of random guessing is a decision by flipping coins. As the size of the sample increases, a random classifier's ROC point tends towards the diagonal line. In the case of a balanced coin, it will tend to the point (0.5, 0.5).

The diagonal divides the ROC space. Points above the diagonal represent good classification results (better than random); points below the line represent bad results (worse than random). Note that the output of a consistently bad predictor could simply be inverted to obtain a good predictor.

Let us look into four prediction results from 100 positive and 100 negative instances:

Plots of the four results above in the ROC space are given in the figure. The result of method A clearly shows the best predictive power among A, B, and C. The result of B lies on the random guess line (the diagonal line), and it can be seen in the table that the accuracy of B is 50%. However, when C is mirrored across the center point (0.5,0.5), the resulting method C′ is even better than A. This mirrored method simply reverses the predictions of whatever method or test produced the C contingency table. Although the original C method has negative predictive power, simply reversing its decisions leads to a new predictive method C′ which has positive predictive power. When the C method predicts p or n, the C′ method would predict n or p, respectively. In this manner, the C′ test would perform the best. The closer a result from a contingency table is to the upper left corner, the better it predicts, but the distance from the random guess line in either direction is the best indicator of how much predictive power a method has. If the result is below the line (i.e. the method is worse than a random guess), all of the method's predictions must be reversed in order to utilize its power, thereby moving the result above the random guess line.

Area Under the ROC Curve (AUC)

When using normalized units, the area under the curve (often referred to as simply the AUC) is equal to the probability that a classifier will rank a randomly chosen positive instance higher than a randomly chosen negative one (assuming 'positive' ranks higher than 'negative').

  • Meanings: Beacuse we calculate area in 1x1 area, so AUC must between 0 and 1. Assign positive for values over threshold, and negative for below. Randomly choose a positive sample and a negative sample, the AUC is the probability of the classifier successfully classify positive sample than negative sample. To sum up, a classifier with higher AUC will have higher accuracy.

  • Criterion: AUC = 1,是完美分类器,采用这个预测模型时,存在至少一个阈值能得出完美预测。绝大多数预测的场合,不存在完美分类器。 0.5 < AUC < 1,优于随机猜测。这个分类器(模型)妥善设定阈值的话,能有预测价值。 AUC = 0.5,跟随机猜测一样(例:丢铜板),模型没有预测价值。 AUC < 0.5,比随机猜测还差;但只要总是反预测而行,就优于随机猜测。

但是如果只用AUC来判断的话,要注意:AUC can hide performance issuces

notice
notice

KS = max(TPR-FPR)

CAP其他参考文章

Receiver operating characteristic

Cumulative accuracy profile

KS

Lareina Update 1

2020年4月2日 08:00

Lareina Update 1

放在生产环境中,才会发现还有哪些问题。短短一天,就新增(修复)了这么多东西。。。

Updates

  • 优化首页卡片显示效果
  • 新增首页轮播图,显示置顶文章 仿照Velas
  • 修改了评论发表频率(原来是40秒3次评论,现在改成20秒1次)及交互逻辑
  • 支持发表草稿文章(即保存草稿)
  • 新增Moments页面全局开关(可以选择是否对公众显示,当然管理员登陆后自己可以在moments页面查看发表的moments)
  • 文章页面结构化数据
  • 文章页面新增阅读进度显示,和返回顶部按钮融为一体
  • 文章现在有了评论数量统计(写在数据库,尽量减少重复的数据库查询),用在了首页卡片上
  • 前端数学公式现在采用katex后端渲染,又少了一个js,开心。效果没什么区别 -> $\alpha+\beta=\gamma$
  • 增加了一个文字隐藏,好玩。
  • 移除了对 MarkDown 上下标的支持,因为会和katex冲突,但影响不大,一样可以用html好水实现。
  • 新增网站地图(采用缓存,只有在发新文章和删除文章时更新)

Fixed

  • 文章页图片显示会从小跳到大的问题
  • 管理员回复评论也会收到通知邮件的问题
  • 评论中不能含有中文符号~()的问题
  • 评论不会自动分词导致溢出的问题
  • safari下采用Serif字体,评论列表切换器显示错位的问题
  • 黑色模式下,iPad iPhone 在滑动时出现白色背景的问题
  • links 页面在移动设备上显示不佳的问题
  • 文章链接图标判断错误的问题(例如可能将jdeal判断成jd.com)
  • category页面tags上下间距过近的问题

Security Related

看到Velas的博客遭遇的一些安全问题,所以有了以下的安全性更新

  • 登陆次数限制 防止暴力破解 (一个IP在XX分钟内只能尝试登陆两次)
  • API增加防镜像,如果发现是镜像调用,直接400报错,并邮件通知我
  • 增加了一部分API的访问频率限制
  • 后端增加博客信息缓存,便于判断一些和博客设置相关的访问。(例如Moments API)
  • 后端配置了错误追踪系统

To Do

  • 首页?page=2 (防止刷新回到首页/可以点击logo回到首页)
  • 没有sticky的处理(轮播图位置改为显示博客信息)
  • 上下篇同分类(关联性高一点)
  • 代码增加一些padding(太紧了啊)
  • 首页desc中需要文字两端对齐(为了美观)
  • 自动保存草稿(数据无价,防止误操作1.保存在浏览器端/保存在数据库/后台文章编辑增加快捷键)
  • 后端错误优化 强壮性(大坑,没有动力)
  • category 页面上下间距过近(应该简单)

非 root 权限安装 FFmpeg 和 FFmpeg 的常用命令

2020年1月25日 08:00

非 root 权限安装 FFmpeg 和 FFmpeg 的常用命令

这学习发现了学校的High Performance Computing (HPC),没有限制使用时间,也没有限制院系。虽然感觉硬件配置挺差的,但是比我的笔记本还是好了太多。另外,带宽很足,上传下载都能达到30MB/s。所以要是想做一些事情,还是很不错的。也不用心疼自己的电脑,还可以放在后台跑。最最主要的是,虽然本来只能在

学校一共对外提供了5台机器,都是HP Xeon,一台4核,两台6核,一台12核,一台20核。只有20核的安装了Centos 7.5。其他都是很老的Centos 6。因为没有提供root 权限,所以安装软件比较麻烦,只能使用编译好的,或者自己make。但是自己make的坑太多了,一般还是拿别人编译好的 release 来用。

我试着安装过anaconda,也是可以不用root权限的。我主要用的也就是python了。这次我用了FFmpeg来试了一下性能。转一个3840x2160(2160p,4K)视屏从VFR到CFR。我在我的老Mac上差不多只有0.5x的速度。而在20核上面可以达到3.5X。想比去年我在本科学校的电脑i7-7700k上一个1080P视频,大约2x的速度,还是快了很多的。我也试过12核的服务器,速度差不多2.5X(4K)。

对于FFmpeg,只要去下载编译好的release,上传,解压缩,解压完后将路径写入.bashrc就可以直接用ffmpeg命令了,很方便。

补充几个FFmpeg常用的命令:

ffmpeg -i input.mp4 output.mp4 #VFR 转 CFR 使用默认参数
ffmpeg -i in.mp4 -map 0 -c:a copy out.mp4 #VFR 转 CFR 貌似效率更高
ffmpeg -ss 00:02:06 -i in.mp4 -f image2 -y out.jpg #视频截图 秒
ffmpeg -ss 00:30:14 -i Novoland.Eagle.Flag.EP01-56.2019.WEB-DL.2160p.HEVC.AAC-HQC/06.mp4 -r 10 -f image2 %05d.bmp #bmp 格式截图
ffmpeg -ss 00:30:14 -i Novoland.Eagle.Flag.EP01-56.2019.WEB-DL.2160p.HEVC.AAC-HQC/06.mp4 -y -f image2 -s 3840x2160 -pix_fmt rgb48 %05d.tiff #tiff格式截图

添加MathJax: 让你的前端支持数学公式显示

2020年1月3日 08:00

添加MathJax: 让你的前端支持数学公式显示

作为一个经常要和数学打交道的学生,怎么能少了数学公式呢?所以,这次给主题加上了显示LaTeX数学公式的功能。

MathJax演示

先来三条公式,分别是行内公式,跨行公式和超长的跨行公式,来看看显示的效果。 代码如下

$\alpha+\beta=\gamma$
$$\alpha+\beta=\gamma$$
$$\int_{0}^{1}f(x)dx \sum_{1}^{2}\int_{0}^{1}f(x)dx \sum_{1}^{2}\int_{0}^{1}f(x)dx \sum_{1}^{2}\int_{0}^{1}f(x)dx \sum_{1}^{2}\int_{0}^{1}f(x)dx \sum_{1}^{2}\int_{0}^{1}f(x)dx \sum_{1}^{2}\int_{0}^{1}f(x)dx \sum_{1}^{2}\int_{0}^{1}f(x)dx \sum_{1}^{2}\int_{0}^{1}f(x)dx \sum_{1}^{2}\int_{0}^{1}f(x)dx \sum_{1}^{2}$$

显示效果如下(目前采用Katex进行渲染):

这是一条行内公式: $\alpha+\beta=\gamma$

$$\alpha+\beta=\gamma$$

$$\int_{0}^{1}f(x)dx \sum_{1}^{2}\int_{0}^{1}f(x)dx \sum_{1}^{2}\int_{0}^{1}f(x)dx \sum_{1}^{2}\int_{0}^{1}f(x)dx \sum_{1}^{2}\int_{0}^{1}f(x)dx \sum_{1}^{2}\int_{0}^{1}f(x)dx \sum_{1}^{2}\int_{0}^{1}f(x)dx \sum_{1}^{2}\int_{0}^{1}f(x)dx \sum_{1}^{2}\int_{0}^{1}f(x)dx \sum_{1}^{2}\int_{0}^{1}f(x)dx \sum_{1}^{2}$$

应该还不错。

$$\alpha+\beta=\gamma$$

使用MathJax

首先,要引入MathJax的js文件,根据文档,通过CDN引入制定版本,这里选择2.7.5版本:

<script type="text/javascript" async src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=TeX-MML-AM_CHTML"></script>

接下来需要初始化MathJax,加载配置文件(这里是将配置文件封装成了一个函数,只需要运行一次即可,每次运行只会降低性能):

const initMathjaxConfig = () => {
  if (!window.MathJax) {
    return;
  }
  window.MathJax.Hub.Config({
    showProcessingMessages: false, //关闭js加载过程信息
    messageStyle: "none", //不显示信息
    jax: ["input/TeX", "output/HTML-CSS"],
    tex2jax: {
      inlineMath: [["$", "$"], ["\\(", "\\)"]], //行内公式选择符
      displayMath: [["$$", "$$"], ["\\[", "\\]"]], //段内公式选择符
      skipTags: ["script", "noscript", "style", "textarea", "pre", "code", "a"] //避开某些标签
    },
    "HTML-CSS": {
      availableFonts: ["STIX", "TeX"], //可选字体
      showMathMenu: false //关闭右击菜单显示
    }
  });
};

接下来就能够渲染页面了,首先要加载配置

initMathjaxConfig(); //加载配置
// 如果不传入第三个参数,则渲染整个document
// 我文章的容器ID为content,就用了content,加快渲染速度
window.MathJax.Hub.Queue(["Typeset", MathJax.Hub, document.getElementById('content')]);

遇到的问题

  • Nuxt貌似无法用npm安装,因为MathJax里用到了Window对象,而在服务端渲染的话,没有Window对象,会报错。我也没找到只让它在browser运行的代码。(很可能是因为我太菜
  • 基于上一个问题,我只能动态加载js,然后在加载配置,渲染。
  • 本站自带的阅读模式下,无法识别LaTex。(很好解决的一个问题。重新Queue一下就好,下次有空修复)

Vue如何优雅地实现下拉到底部自动加载(无限滚动)

2019年12月30日 08:00

Vue如何优雅地实现下拉到底部自动加载(无限滚动)

今天在制作闲言碎语页面时,想要一个无限滚动的功能。本来想用Element UI自带的无限滚动,但是发现这个貌似需要有一个定高的容器,那就很不适合我了。我想要的是整个页面的无限滚动,而不是容器内的无限滚动。所以在网上找到了一篇很不错的文章,来分享一下,根据自己需求修改就好。

首先在目录下新建一个utils文件夹,在该文件夹下新建一个screen.js,将公用方法写入,你也可以考虑其他的封装方式。

//滚动条在Y轴上的滚动距离
export function getScrollTop(){
  var scrollTop = 0, bodyScrollTop = 0, documentScrollTop = 0;
  if(document.body){
    bodyScrollTop = document.body.scrollTop;
  }
  if(document.documentElement){
    documentScrollTop = document.documentElement.scrollTop;
  }
  scrollTop = (bodyScrollTop - documentScrollTop &gt; 0) ? bodyScrollTop : documentScrollTop;
  return scrollTop;
}

//文档的总高度
export function getScrollHeight(){
  var scrollHeight = 0, bodyScrollHeight = 0, documentScrollHeight = 0;
  if(document.body){
    bodyScrollHeight = document.body.scrollHeight;
  }
  if(document.documentElement){
    documentScrollHeight = document.documentElement.scrollHeight;
  }
  scrollHeight = (bodyScrollHeight - documentScrollHeight &gt; 0) ? bodyScrollHeight : documentScrollHeight;
  return scrollHeight;
}

//浏览器视口的高度
export function getWindowHeight(){
  var windowHeight = 0;
  if(document.compatMode == "CSS1Compat"){
    windowHeight = document.documentElement.clientHeight;
  }else{
    windowHeight = document.body.clientHeight;
  }
  return windowHeight;
}

在要使用的页面中进行引入

import {getScrollHeight,getScrollTop,getWindowHeight} from "../../utils/screen";

将窗口的滚动进行监听,划到底部的时候就进行加载,但是页面关闭的同时,记得将这个监听器关闭,节省性能

mounted(){
    window.addEventListener('scroll', this.load);
},
destroyed(){
    window.removeEventListener('scroll', this.load, false);
},

this.load()就是写的加载数据的方法,到底部的时候先判断下一页是否还有数据,pages就是我从后台拿到的总页数,和当前页进行对比,只有下一页还有数据的时候,我才会拉取后台的接口。

//无限滚动加载
load(){
    let vm=this;
    if(getScrollTop() + getWindowHeight() >= getScrollHeight()){
        if(vm.queryList.pageNum &lt; vm.pages){      //先判断下一页是否有数据  
            vm.queryList.pageNum+=1;         //查询条件的页码+1
            vm.getList();              //拉取接口数据
        }else{
            //到底了
        }
    }
},

WordPress Nuxt 主题 Lareina Version 1.1

2019年12月26日 08:00

WordPress Nuxt 主题 Lareina Version 1.1

唯一的不同,就是处处不同

好久没写博客了,这次来更新一下主题。大体上没有什么变化,但是细节上优化了了很多。

问题修复

  • 修复评论表情不可用问题
  • 修复黑色和棕色模式下显示覆盖不全面的问题
  • 首页、文章页头图动画优化
  • 主体设计语言统一与现代化
  • 文章页设计优化
  • 评论区设计优化
  • 添加一部分图标
  • 部分交互逻辑优化
  • npm依赖包更新
  • html2canvas
  • bug修复
  • 评论区不再显示未审核的评论

功能新增

  • 新增文章、评论内链
  • 新增一套评论表情
  • 图片懒加载
  • 阅读模式
  • 私密评论
  • 闲言碎语功能(写了个简单的php和数据库,还可以发表私密文字。)
  • 在线人数和打开页面数量统计
  • 在线实时推送
  • 添加了评论区issue
  • tracker,用于更好的监测用户评论时出现的exceptions。
  • 新增鼠标滚轮平滑滚动
  • 新增Latex公式显示
  • 新增文章自动加空格,更美观

备忘

更换评论头像挂件:修改default.vue中的图片链接,修改大小,本地npm run build,选择.nuxt, static,package.json,nuxt.config.js四个,本地npm install,然后打包上传即可。pm2 restart all来更新主题。

使用ARP攻击和嗅探局域网内的网络

2019年8月12日 08:00

使用ARP攻击和嗅探局域网内的网络

其实可以有很多方法和软件可以实施ARP攻击,我这个只是其中的一种。

攻击准备

如果是Mac,需要先下载安装 macports, 并更新 macports sudo port -d selfupdate

然后需要安装 nmap(用来扫描端口,非必须)brew install nmap 和 arpspoof(实施ARP攻击,必须)。网上说可以通过 macports 直接安装dsniff(包含了arpspoof)sudo port install dsniff,但是貌似dsniff年久失修,在Majove上并无法安装。所以我在Github上找到了别人提取出来的单独的arpspoof,安装完依赖后就成功装上了。

如果使用kali linux,那么无需这些准备工作。

攻击步骤

一、寻找目标

使用nmap命令扫描局域网,获得主机列表 如果所在局域网路由器地址是 192.168.1.1,可以使用 nmap -sP 192.168.1.1/24 扫描 -sP 表示使用 ping 方式扫描,192.168.1.1/24”表示扫描"192.168.1.1-192.168.1.254"这个网段的所有机器。

二、开启 IP 转发

ARP欺骗一般目的是把自己伪装成网关,但如果不作处理,当被欺骗数据包到达后就会被本机丢弃(因为本机不是网关,不知道如何处理这类数据包),这当然是不允许的。开启IP转发功能可以解决该问题,IP转发负责把该类数据包再转发给真正的网关处理,开启IP转发的方法.

mac 下:

sysctl -w net.inet.ip.forwarding=1

linux 下:

echo 1 /proc/sys/net/ipv4/ip_forward

三、ARP 欺骗

假设被攻击的 IP 是 192.168.1.134,局域网的网关是 192.168.1.1,攻击电脑使用的网卡接口是 en0(可以使用 ifconfig 命令查看), 则欺骗命令如下:

arpspoof -i en0 -t 192.168.1.134 192.168.1.1

四、分析数据

如果 ARP 欺骗成功,则被攻击的设备会把所有数据先传到我们电脑上,接下来可以使用 wireshark 软件来分析数据。 至此,中间人攻击完成。

当然,直接用ettercap也是一个好选择。

使用supervisor后台运行celery

2019年8月2日 08:00

使用supervisor后台运行celery

最近打算上线一个B站相关的网站,采用了 celery 来处理任务队列,在本地的时候,那是可以多开,一直开着终端。但是线上生产环境肯定不行。所以需要使用supervisor。

先安装supervisor

安装命令:

$ pip install supervisor

对supervisor进行配置

先在etc目录下生成一个supervisor文件夹,然后生成默认配置文件:

$ echo_supervisord_conf > /etc/supervisor/supervisord.conf

修改配置文件

用vim修改一下:

$ vim /etc/supervisor/supervisord.conf

在最底下改成:

[include]
files = /etc/supervisor/supervisord.conf.d/*.conf

然后创建并进入supervisord.conf.d文件夹,创建 celeryd_worker.conf 文件并进行如下配置:

[program: ProjectName]
command=celery -A bookstore worker -l info ; 运行程序的命令
directory=/root/Publishing/PublishOutput/ ; 命令执行的目录
autorestart=true ; 程序意外退出是否自动重启
autostart=true ; 是否自动启动
stderr_logfile=/var/log/ProjectName.err.log ; 错误日志文件
stdout_logfile=/var/log/ProjectName.out.log ; 输出日志文件
environment=ASPNETCORE_ENVIRONMENT=Production ; 进程环境变量
user=root ; 进程执行的用户身份
stopsignal=INT
startsecs=1 ; 自动重启间隔 

启动 supervisor

$ supervisord -c /etc/supervisor/supervisord.conf

如果遇到报错信息为端口正在被占用的话运行下面的命令:

$ unlink /var/run/supervisor.sock
# 或者
$ unlink /tmp/supervisor.sock

用 supervisorctl 即可监视状态。

新版BiliBili工具箱

2018年12月28日 08:00

新版BiliBili工具箱

最近重新写了一下工具箱。虽然叫工具箱,但是到目前为止,只有下载普通av号视频的功能。之前基本都是靠php来处理。这次就不一样了。前后端彻底分离了。php就主要负责登陆状态、数据库交互、获取一些b站的数据。

用VUE和Element-UI重写了一遍,UI更好看,Bug更少。最主要的是,现在下载视频不用先下载到服务器上了。现在支持直接导出到aria2下载,或者使用Windows 上的IDM下载(未测试,没有Windows也没有IDM),整体下载流程更顺畅,就看你家的网速了。最重要的是,现在还支持了直接通过浏览器下载。所以,即使你没有aria2或者IDM,也可以下载。不过还是推荐通过软件下载,毕竟文件名和文件扩展名就不用你手动改了。

登陆和注册系统也全部重写了一遍。

有了上次的经验,这次用vue重写就快了好多。这个版本应该以后会上线。先来个预览版。

视频大小约1MB,请放心食用。

演示

❌
❌