正则学习笔记

简介

如果做文本处理,日志运维,数据分析,爬虫等相关工作的同学,一定离不开的一个工具就是正则表达式。正则可以从一个完整的字符串中查找或者提取到具有相同“模式”的字符串子串。

什么是正则表达式

正则就是描述文本规则的代码,例如: *.doc 表示所有以doc后缀结束的文件名,这就是一个简单的正则。

基本模式

正则 含义
[abc] A single character of: a, b or c
[^abc] Any single character except: a, b, or c
[a-z] Any single character in the range a-z
[a-zA-Z] Any single character in the range a-z or A-Z
^ Start of line
$ End of line
\A Start of string
\z End of string
. Any single character
\s Any whitespace character
\S Any non-whitespace character
\d Any digit
\D Any non-digit
\w Any word character (letter, number, underscore)
\W Any non-word character
\b Any word boundary
(...) Capture everything enclosed
(a/b) a or b
a? Zero or one of a
a* Zero or more of a
a+ One or more of a
a{3} Exactly 3 of a
a{3,} 3 or more of a
a{3,6} Between 3 and 6 of a

元字符

元字符是正则中的具有独立完整意义的字符,类似变成语言中的保留字,只不过保留字一般是多字符的。

代码/语法 说明
. 匹配除换行符以外的任意字符
w 匹配字母或数字或下划线或汉字
s 匹配任意的空白符
d 匹配数字
b 匹配单词的开始或结束
^ 匹配字符串的开始
$ 匹配字符串的结束

例子说明:
元字符 ^(和数字6在同一个键位上的符号)和 $ 都匹配一个位置,这和 \b 有点类似。^ 匹配你要用来查找的字符串的开头,$ 匹配结尾。这两个代码在验证输入的内容时非常有用,比如一个网站如果要求你填写的 QQ 号必须为 5 位到 12 位数字时,可以使用:^\d{5,12}$ 。
这里的 {5,12} 和前面介绍过的 {2} 是类似的,只不过 {2} 匹配只能不多不少重复 2 次,{5,12} 则是重复的次数不能少于 5 次,不能多于 12 次,否则都不匹配。
因为使用了 ^ 和 $,所以输入的整个字符串都要用来和 \d{5,12} 来匹配,也就是说整个输入必须是 5 到 12 个数字,因此如果输入的QQ号能匹配这个正则表达式的话,那就符合要求了。

字符转义

如果你想查找元字符本身的话,比如你查找 . 或者 *,就出现了问题:你没办法指定它们,因为它们会被解释成别的意思。这时你就得使用\ 来取消这些字符的特殊意义。因此,你应该使用 \. 和 \* 。当然,要查找 \ 本身,你也得用 \\。
例如:unibetter\.com 匹配 unibetter.com,C:\\Windows 匹配 C:\Windows。

重复

常用的表示符

代码/语法 说明
* 重复零次或更多次
+ 重复一次或更多次
? 重复零次或一次
{n} 重复n次
{n,} 重复n次或更多次
{n,m} 重复n到m次

字符类

要想查找数字,字母或数字,空白是很简单的,因为已经有了对应这些字符集合的元字符,但是如果你想匹配没有预定义元字符的字符集合(比如元音字母a,e,i,o,u),应该怎么办?
很简单,你只需要在方括号里列出它们就行了,像 [aeiou] 就匹配任何一个英文元音字母,[.?!] 匹配标点符号 (. 或 ? 或 !)。
我们也可以轻松地指定一个字符范围,像 [0-9] 代表的含意与 \d 就是完全一致的:一位数字;同理 [a-z0-9A-Z_] 也完全等同于 \w(如果只考虑英文的话)。

下面是一个更复杂的表达式:

\(?0\d{2}[)-]?\d{8}  

英文括号 ( 和 ) 也是元字符,后面的分组节里会提到,所以在这里需要使用转义。

这个表达式可以匹配几种格式的电话号码,像 (010)88886666,或 022-22334455,或 02912345678 等。我们对它进行一些分析吧:
首先是一个转义字符 (,它能出现 0 次或 1 次(?),然后是一个 0,后面跟着 2 个数字 (\d{2}),然后是)或 - 或 空格 中的一个,它出现 1 次或不出现(?),最后是 8 个数(\d{8})。

分支条件

正则1|正则2:从左到右地测试每个条件,如果满足了某个分枝的话,就不会去再管其它的条件了。

分组

我们已经提到了怎么重复单个字符(直接在字符后面加上限定符就行了);但如果想要重复多个字符又该怎么办?你可以用小括号来指定子表达式(也叫做分组),然后你就可以指定这个子表达式的重复次数了,你也可以对子表达式进行其它一些操作(后面会有介绍)。

(\d{1,3}.){3}\d{1,3} 是一个简单的IP地址匹配表达式。要理解这个表达式,请按下列顺序分析它:\d{1,3} 匹配 1 到 3 位的数字,(\d{1,3}.){3} 匹配三位数字加上一个英文句号(这个整体也就是这个分组)重复 3 次,最后再加上一个一到三位的数字 (\d{1,3})。

不幸的是,它也将匹配256.300.888.999这种不可能存在的IP地址。如果能使用算术比较的话,或许能简单地解决这个问题,但是正则表达式中并不提供关于数学的任何功能,所以只能使用冗长的分组,选择,字符类来描述一个正确的IP地址:

((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)  

理解这个表达式的关键是理解 2[0-4]\d|25[0-5]|[01]?\d\d?,具体含义参看后面的注释章节

反义

有时需要查找不属于某个能简单定义的字符类的字符。比如想查找除了数字以外,其它任意字符都行的情况,这时需要用到反义:

代码/语法 说明
\W 匹配任意不是字母,数字,下划线,汉字的字符
\S 匹配任意不是空白符的字符
\D 匹配任意非数字的字符
\B 匹配不是单词开头或结束的位置
[^x] 匹配除了 x 以外的任意字符
[^aeiou] 匹配除了 aeiou 这几个字母以外的任意字符

例子:
\S+ 匹配不包含空白符的字符串。
\<a[^>]+> 匹配用尖括号括起来的以 a 开头的字符串。

后向引用

使用小括号指定一个子表达式后,匹配这个子表达式的文本(也就是此分组捕获的内容)可以在表达式或其它程序中作进一步的处理。默认情况下,每个分组会自动拥有一个组号,规则是:从左向右,以分组的左括号为标志,第一个出现的分组的组号为1,第二个为2,以此类推。

呃……其实,组号分配还不像我刚说得那么简单:
分组0对应整个正则表达式
实际上组号分配过程是要从左向右扫描两遍的:第一遍只给未命名组分配,第二遍只给命名组分配--因此所有命名组的组号都大于未命名的组号
你可以使用(?:exp)这样的语法来剥夺一个分组对组号分配的参与权.

后向引用用于重复搜索前面某个分组匹配的文本。例如,\1 代表分组 1 匹配的文本。难以理解?请看示例:

\b(\w+)\b\s+\1\b 可以用来匹配重复的单词,像 go go, 或者 kitty kitty。这个表达式首先是一个单词,也就是单词开始处和结束处之间的多于一个的字母或数字 (\b(\w+)\b),这个单词会被捕获到编号为 1 的分组中,然后是 1 个或几个空白符 (\s+),最后是分组1中捕获的内容(也就是前面匹配的那个单词)(\1)。

你也可以自己指定子表达式的组名。要指定一个子表达式的组名,请使用这样的语法:(?\\w+) (或者把尖括号换成 ‘ 也行:(?’Word’\w+)),这样就把 \w+ 的组名指定为 Word了。要反向引用这个分组捕获的内容,你可以使用 \k\,所以上一个例子也可以写成这样:\b(?\\w+)\b\s+\k\\b。

使用小括号的时候,还有很多特定用途的语法。下面列出了最常用的一些:

常用分组语法

分类 代码/语法 说明
捕获 (exp) 匹配exp,并捕获文本到自动命名的组里
(?< name>exp) 匹配exp,并捕获文本到名称为name的组里,也可以写成(?’name’exp)
(?:exp) 匹配exp,不捕获匹配的文本,也不给此分组分配组号
零宽断言 (?=exp) 匹配exp前面的位置
(?<=exp) 匹配exp后面的位置
(?!exp) 匹配后面跟的不是exp的位置
(?<!exp) 匹配前面不是exp的位置
注释 (?#comment) 这种类型的分组不对正则表达式的处理产生任何影响,用于提供注释让人阅读

我们已经讨论了前两种语法。第三个 (?:exp) 不会改变正则表达式的处理方式,只是这样的组匹配的内容不会像前两种那样被捕获到某个组里面,也不会拥有组号。“我为什么会想要这样做?”——好问题,你觉得为什么呢?

零宽断言

接下来的四个用于查找在某些内容(但并不包括这些内容)之前或之后的东西,也就是说它们像 \b , ^ , $ 那样用于指定一个位置,这个位置应该满足一定的条件(即断言),因此它们也被称为零宽断言。最好还是拿例子来说明吧:

断言用来声明一个应该为真的事实。正则表达式中只有当断言为真时才会继续进行匹配。

(?=exp) 也叫零宽度正预测先行断言,它断言自身出现的位置的后面能匹配表达式 exp。比如 \b\w+(?=ing\b),匹配以 ing 结尾的单词的前面部分(除了ing以外的部分),如查找 I’m singing while you’re dancing. 时,它会匹配 sing 和 danc。

(?<=exp) 也叫零宽度正回顾后发断言,它断言自身出现的位置的前面能匹配表达式exp。比如 (?<=\bre)\w+\b 会匹配以 re 开头的单词的后半部分(除了re以外的部分),例如在查找 reading a book 时,它匹配 ading。

假如你想要给一个很长的数字中每三位间加一个逗号(当然是从右边加起了),你可以这样查找需要在前面和里面添加逗号的部分:((?<=\d)\d{3})+\b,用它对 1234567890 进行查找时结果是 234567890。

下面这个例子同时使用了这两种断言:

(?<=\s)\d+(?=\s)

匹配以空白符间隔的数字 (再次强调,不包括这些空白符)。

负向零宽断言

前面我们提到过怎么查找不是某个字符或不在某个字符类里的字符的方法(反义)。但是如果我们只是想要确保某个字符没有出现,但并不想去匹配它时怎么办?例如,如果我们想查找这样的单词–它里面出现了字母 q,但是 q 后面跟的不是字母 u,我们可以尝试这样:

\b\w*q[^u]\w*\b 匹配包含后面不是字母 u 的字母 q 的单词。但是如果多做测试(或者你思维足够敏锐,直接就观察出来了),你会发现,如果q出现在单词的结尾的话,像 Iraq,Benq,这个表达式就会出错。这是因为 [^u] 总要匹配一个字符,所以如果q是单词的最后一个字符的话,后面的 [^u] 将会匹配 q 后面的单词分隔符(可能是空格,或者是句号或其它的什么),后面的 \w*\b 将会匹配下一个单词,于是 \b\w*q[^u]\w*\b 就能匹配整个 Iraq fighting。负向零宽断言能解决这样的问题,因为它只匹配一个位置,并不消费任何字符。现在,我们可以这样来解决这个问题:\b\w*q(?!u)\w*\b。

零宽度负预测先行断言 (?!exp),断言此位置的后面不能匹配表达式exp。例如:\d{3}(?!\d) 匹配三位数字,而且这三位数字的后面不能是数字;\b((?!abc)\w)+\b匹配不包含连续字符串abc的单词。

同理,我们可以用 (?<!exp),零宽度负回顾后发断言来断言此位置的前面不能匹配表达式 exp:(?<![a-z])\d{7} 匹配前面不是小写字母的七位数字。

请详细分析表达式 (?<=<(\w+)>).*(?=<\/\1>),这个表达式最能表现零宽断言的真正用途。

一个更复杂的例子:
(?<=<(\w+)>).*(?=<\/\1>) 匹配不包含属性的简单 HTML 标签内里的内容。(?<=<(\w+)>) 指定了这样的前缀:被尖括号括起来的单词(比如可能是 < xxx> ),然后是 ".*" (任意的字符串),最后是一个后缀 (?=<\/\1>)。注意后缀里的 \/,它用到了前面提过的字符转义;\1 则是一个反向引用,引用的正是捕获的第一组,前面的 (\w+) 匹配的内容,这样如果前缀实际上是 < xxx> 的话,后缀就是 < /xxx> 了。整个表达式匹配的是 < xxx> 和 < /xxx> 之间的内容(再次提醒,不包括前缀和后缀本身)。

注释

小括号的另一种用途是通过语法 (?#comment) 来包含注释。例如:
2[0-4]\d(?#200-249)|25[0-5](?#250-255)|[01]?\d\d?(?#0-199)

要包含注释的话,最好是启用“忽略模式里的空白符”选项,这样在编写表达式时能任意的添加空格,Tab,换行,而实际使用时这些都将被忽略。启用这个选项后,在#后面到这一行结束的所有文本都将被当成注释忽略掉。例如,我们可以前面的一个表达式写成这样:

(?<=    # 断言要匹配的文本的前缀
<(\w+)> # 查找尖括号括起来的字母或数字(即HTML/XML标签)
)       # 前缀结束
.*      # 匹配任意文本
(?=     # 断言要匹配的文本的后缀
<\/\1>  # 查找尖括号括起来的内容:前面是一个"/",后面是先前捕获的标签
)       # 后缀结束

贪婪与懒惰匹配

当正则表达式中包含能接受重复的限定符时,通常的行为是(在使整个表达式能得到匹配的前提下)匹配尽可能多的字符。以这个表达式为例:a.*b,它将会匹配最长的以 a 开始,以 b 结束的字符串。如果用它来搜索 aabab 的话,它会匹配整个字符串 aabab。这被称为贪婪匹配。

有时,我们更需要懒惰匹配,也就是匹配尽可能少的字符。前面给出的限定符都可以被转化为懒惰匹配模式,只要在它后面加上一个问号 ? 。这样 .*? 就意味着匹配任意数量的重复,但是在能使整个匹配成功的前提下使用最少的重复。现在看看懒惰版的例子吧:

a.*?b 匹配最短的,以 a 开始,以 b 结束的字符串。如果把它应用于 aabab的话,它会匹配 aab(第一到第三个字符)和 ab(第四到第五个字符)。

为什么第一个匹配是 aab(第一到第三个字符)而不是 ab(第二到第三个字符)?简单地说,因为正则表达式有另一条规则,比懒惰/贪婪规则的优先级更高:最先开始的匹配拥有最高的优先权——The match that begins earliest wins。

懒惰限定符

代码/语法 说明
*? 重复任意次,但尽可能少重复
+? 重复1次或更多次,但尽可能少重复
?? 重复0次或1次,但尽可能少重复
{n,m}? 重复n到m次,但尽可能少重复
{n,}? 重复n次以上,但尽可能少重复

处理选项

正则支持设置匹配的选项,比如忽略大小写,忽略空白,单行模式,多行模式等

其他语法

代码/语法 说明
\a 报警字符(打印它的效果是电脑嘀一声)
\b 通常是单词分界位置,但如果在字符类里使用代表退格
\t 制表符,Tab
\r 回车
\v 竖向制表符
\f 换页符
\n 换行符
\e Escape
\0nn ASCII代码中八进制代码为nn的字符
\xnn ASCII代码中十六进制代码为nn的字符
\unnnn Unicode代码中十六进制代码为nnnn的字符
\cN ASCII控制字符。比如\cC代表Ctrl+C
\A 字符串开头(类似^,但不受处理多行选项的影响)
\Z 字符串结尾或行尾(不受处理多行选项的影响)
\z 字符串结尾(类似$,但不受处理多行选项的影响)
\G 当前搜索的开头
\p{name} Unicode中命名为name的字符类,例如\p{IsGreek}
(?>exp) 贪婪子表达式
(?< x>-< y>exp) 平衡组
(?im-nsx:exp) 在子表达式exp中改变处理选项
(?im-nsx) 为表达式后面的部分改变处理选项
(?(exp)yes/no) 把exp当作零宽正向先行断言,如果在这个位置能匹配,使用yes作为此组的表达式;否则使用no
(?(exp)yes) 同上,只是使用空表达式作为no
(?(name)yes/no) 如果命名为name的组捕获到了内容,使用yes作为表达式;否则使用no
(?(name)yes) 同上,只是使用空表达式作为no

在线测试

以上这么多知识,边学边练是很有必要的。这里推荐两个有趣的在线网站:
http://rubular.com/
https://www.debuggex.com/

参考

http://blog.jobbole.com/96708/

通过源码将git升级到最新版

因为go-get下载hugo失败,需要升级git到最新版本。

下载最新git代码

wget -O git-master.zip https://github.com/git/git/archive/master.zip

编译

unzip git-master.zip
cd git-master

yum install openssl-devel curl-devel expat-devel perl-ExtUtils-MakeMaker gettext gettext-libs gettext-devel asciidoc xmlto docbook2X

ln -s /usr/bin/db2x_docbook2texi /usr/bin/docbook2x-texi

错误

libgit.a(utf8.o): In function `reencode_string_iconv':
/usr/local/git/utf8.c:463: undefined reference to `libiconv'
libgit.a(utf8.o): In function `reencode_string_len':
/usr/local/git/utf8.c:502: undefined reference to `libiconv_open'
/usr/local/git/utf8.c:521: undefined reference to `libiconv_close'
/usr/local/git/utf8.c:515: undefined reference to `libiconv_open'
collect2: ld returned 1 exit status
make: *** [git-credential-store] Error 1

从错误提示看,是缺少libiconv。

安装libiconv

  1. 下载编译

    cd /usr/local
    wget http://ftp.gnu.org/pub/gnu/libiconv/libiconv-1.14.tar.gz
    tar -zxvf libiconv-1.14.tar.gz
    cd libiconv-1.14
    ./configure –prefix=/usr/local/libiconv && make && make install

  2. 创建一个软链接到/usr/lib

    ln -s /usr/local/lib/libiconv.so /usr/lib
    ln -s /usr/local/lib/libiconv.so.2 /usr/lib

  3. 然后回到git目录继续编译

    cd /usr/local/git
    make configure
    ./configure --prefix=/usr/local --with-iconv=/usr/local/libiconv
    make
    make install

  4. 此时,git应该编译安装成功了,测试如下:

    $ git --version
    git version 2.5.0

centos6下go get卡停问题

go get可以通过选项,直接下载所有依赖项,非常方便。所以在安装hugo时,我们也通过go get 下载源码:

go get -u -v github.com/spf13/hugo 

下载时突然卡住,没有任何反应和提示,日志停留在:

Fetching https://gopkg.in/fsnotify.v1?go-get=1
Parsing meta tags from https://gopkg.in/fsnotify.v1?go-get=1 (status code 200)
get "gopkg.in/fsnotify.v1": found meta tag main.metaImport{Prefix:"gopkg.in/fsnotify.v1", VCS:"git", RepoRoot:"https://gopkg.in/fsnotify.v1"} at https://gopkg.in/fsnotify.v1?go-get=1
gopkg.in/fsnotify.v1 (download) 

根据缺失的库逐一排查,后来根据缺失 gopkg.in/yaml.v1 为线索,网上说是原因git版本太低,需>= 1.7.9.5,而Centos6.7 自带的git 是1.7.1的。

升级到git最新版后,go get 成功!

一步一步教你用hugo搭建博客

Hugo 是一个轻量级的静态网站生成工具,是基于GO语言的模版技术开发而成,因为最近在学习go,就花了时间研究了下,一研究就喜欢上了。
再加上最新wordpress版本有严重的问题,在文章发表后或者再次编辑时,编辑框会丢失所有的格式,这个让使用Markdown的人无法接受。

安装hugo

Hugo官方主页:HUGO
hugo托管在github上,我们可以直接二进制安装也可以源码安装。
这里我们演示源码安装。

  1. 源码安装
    在go里面我们可以直接通过get安装:
    go get -u -v github.com/spf13/hugo
    或者直接git下载
    git clone https://github.com/spf13/hugo.git

  2. 编译
    go build -o hugo main.go
    mv hugo $GOPATH/bin
    终端查看是否成功,mac下可能出现路径没找到的问题,要重新开终端
    $ hugo version
    Hugo Static Site Generator v0.16-DEV BuildDate: 2015-12-14T16:07:24+08:00

生成静态站点

  1. 创建网站
    我们先创建一个空网站.
    $ hugo new site localhost
    $ tree
    .
    ├── archetypes
    ├── config.toml
    ├── content
    ├── data
    ├── layouts
    ├── public
    ├── static
    └── themes  

默认情况下这些目录都是空的,直接运行的话会有ERROR提示

    ERROR: 2015/12/14   =============================================================
    ERROR: 2015/12/14 Your rendered home page is blank: /index.html is zero-length
    ERROR: 2015/12/14  * Did you specify a theme on the command-line or in your
    ERROR: 2015/12/14    "config.toml" file?  (Current theme: "")
    ERROR: 2015/12/14  * For more debugging information, run "hugo -v"
    ERROR: 2015/12/14 =============================================================

看提示说是没有指定theme导致,我们需要下载一个theme。

  1. 安装theme
    我们可以从hugo的网站下载自己喜欢的theme
    $ cd themes
    $ git clone https://github.com/spf13/hyde.git

  2. 测试框架
    安装完theme后,我们体验下效果,使用 hugo server就可以起一个http server,默认监听在1313端口,如果没有在config中配置theme,就要指定theme。

    $ hugo server -t hyde
    0 draft content
    0 future content
    0 pages created
    0 paginator pages created
    0 tags created
    0 topics created
    in 24 ms
    Watching for changes in /Users/alex/run/localhost/{data,content,layouts,static,themes}
    Serving pages from memory
    Web Server is available at http://localhost:1313/ (bind address 127.0.0.1)
    Press Ctrl+C to stop            

hyde 界面

  1. 发表文章
    hugo里面写文章其实就是写markdown文档了。写好文档,hugo会给你自动转成html静态文件。我们通过Hugo创建一个md文档。
    $ hugo new first.md
    /Users/alex/run/localhost/content/first.md created
    运行时在网站根目录下运行,创建的文件默认创建在content目录下。
    +++
    date = "2015-12-15T22:35:22+08:00"
    draft = true
    title = "first"
    
    +++

我们从内容看默认创建的是草稿类型,需要将draft改为true才能看到页面。正常情况下我们会通过Mou或者github编辑文档,只要文件头符合hugo的规范就可以。
第一篇文章

调试部署

  1. 调试
    在开发的过程中,我们需要不断的修改验证,所以hugo支持LiveReload功能,用户修改后,可以实时看到效果。执行hugo server命令时加上-w选项,hugo就可以自动检测本地站点文件的变更。
    $ hugo server -w -t hyde
    注意:在使用server命令时,hugo并没有在public目录下产生相应的静态页面。

  2. 部署
    部署时,我们需要生成静态页面文件,然后就可以随便部署在自己的空间上了。转化时,一个hugo命令就搞定:

    $ hugo -t hyde
    0 draft content
    0 future content
    1 pages created
    0 paginator pages created
    0 topics created
    0 tags created
    in 38 ms

我们看到有一个页面生成了,默认在public目录,实际一起生成的还有其他文件:

    $ ls
    404.html                first
    apple-touch-icon-144-precomposed.png    index.html
    css                 index.xml
    favicon.png             sitemap.xml

把这些文件放到你的空间,你就可以看见你的页面和theme了。

到这里,我们已经有了一个基本的能创建文章并且显示的网站了。

have fun!

记一次移动端frame注入事件

起因

在用手机调试网站效果时,偶尔发现底部出现广告,于是就有了以下的内容。

界面表现


停留一会后自动消失

代码表现

通过提取,我们可以拿到广告出现时和消失后的代码。
出现广告时页面被插入的frame代码
<iframe src="http://i.dreamfull.cn/api/my.jsp?sid=320418129&amp;pn=_QWERJAD_274192119_320418129_1_&amp;sd=api.dreamfull.cn#_maerd_dnegel_=1" style="display: none; border: 0px; width: 0px; height: 0px;"></iframe><div style="display: block; visibility: visible; overflow: hidden; width: 375px; height: 56.25px; margin: 0px; padding: 0px; border: 0px; box-sizing: border-box; z-index: 2147483647; position: fixed; bottom: 0px; left: 0px;"><div style="position: relative; z-index: 0;"><a style="width: 100%; height: 59px; display: none; text-decoration: none; -webkit-box-align: center; color: rgb(0, 0, 0); overflow: hidden; line-height: 59px; font-size: 10px;"></a><script src="http://c3.moogos.com/js/_jssdk.js?aid=s4b7b3f9" type="text/javascript" async="async"></script><div style="left: 0px; font-size: 0px; z-index: 2147483583; position: fixed; bottom: 0px; display: block; width: 100%; height: 59px;" csstext="display:block;left:0;font-size:0;z-index:2147483583;position:fixed;bottom:0;display:block;width:100%;height:59px;"><iframe style="border: 1px; bottom: 0px; display: block; width: 375px; height: 56.25px;" frameborder="0" scrolling="no" border="0" src="http://api.moogos.com/js/index.html?_ts=1450470254933&amp;info=%7B%22domain%22:%22www.goodmemory.cc%22,%22urls%22:%22www.goodmemory.cc/%25E9%2580%259A%25E8%25BF%2587event-hook%25E5%25B0%2586github%25E8%2587%25AA%25E5%258A%25A8%25E9%2583%25A8%25E7%25BD%25B2%25E8%2587%25B3hugo%25E7%25BD%2591%25E7%25AB%2599/%22,%22adslot%22:%22s4b7b3f9%22,%22version%22:%7B%22major%22:2,%22minor%22:26%7D,%22prod%22:1,%22adParentId%22:%22moogos_s4b7b3f9%22,%22inittime%22:1450470254923,%22duration%22:10,%22appId%22:%2202eddf5b%22%7D"></iframe><div style="position:absolute; width:30px; height:30px;top:0;right:0;z-index:2147483584;background:rgba(255,255,255,0);" onclick="var p = this.parentNode;p.parentNode.removeChild(p);p.setAttribute(&quot;close&quot;, &quot;0&quot;);var b = document.getElementById(&quot;blankDivs4b7b3f9&quot;);b &amp;&amp; b.parentNode.removeChild(b);"><i style="position:absolute; width:1px; height:18px; background:#000; top:6px;left:6px; -ms-transform:rotate(45deg); -webkit-transform:rotate(45deg); -moz-transform:rotate(45deg); transform:rotate(45deg); left:15px;"></i><i style="position:absolute; width:1px; height:18px; background:#000; top:6px;left:6px; -ms-transform:rotate(-45deg); -webkit-transform:rotate(-45deg); -moz-transform:rotate(-45deg); transform:rotate(-45deg); left:15px;"></i><i style="position:absolute; width:24px; height:24px; top:3px; left:3px; background:rgba(255,255,255,0.4); border-radius:50%;"></i></div></div><div style="position: absolute; top: -20px; right: -10px; z-index: 99; width: 50px; height: 60px; overflow: hidden; display: block; left: auto !important; background: url(&quot;http://api.dreamfull.cn/s/images/none.png&quot;) repeat transparent;"></div></div><div style="position: absolute; cursor: pointer; bottom: 0px; right: 0px; z-index: 100; width: 35px; height: 20px; overflow: hidden; display: block; left: auto !important; background: url(&quot;http://api.dreamfull.cn/s/images/logo_mini.gif&quot;) repeat transparent;"></div><div style="position: absolute; cursor: pointer; top: 0px; right: 0px; z-index: 100; width: 35px; height: 20px; overflow: hidden; display: block; left: auto !important; background: url(&quot;http://api.dreamfull.cn/s/images/none.png&quot;) repeat transparent;"></div><div style="position: absolute; cursor: pointer; top: 0px; right: 0px; z-index: 100; display: none; width: 86px; height: 20px; overflow: hidden; left: auto !important; background: url(&quot;http://api.dreamfull.cn/s/images/close_long.gif&quot;) repeat transparent;"></div></div>
广告自动消失后的代码变成:
<img src="http://rcv.moogos.com/rtsdk?type=show&amp;version=2.26&amp;urls=www.goodmemory.cc/%E9%80%9A%E8%BF%87event-hook%E5%B0%86github%E8%87%AA%E5%8A%A8%E9%83%A8%E7%BD%B2%E8%87%B3hugo%E7%BD%91%E7%AB%99/&amp;adslot=s4b7b3f9&amp;_ts=1450467804078" style="display:none;">

分析

通过以上的信息,我们得到如下信息:
* http://dreamfull.cn 是广告平台方,根据域名信息查询:


* 广告投放方为http://moogos.com
* 广告代码样本
https://hiproz.github.io/goodmemory.cc/blog/images/2015/12/frame1.txt
https://hiproz.github.io/goodmemory.cc/blog/images/2015/12/frame2.txt
* 其中的js脚本:http://c3.moogos.com/js/_jssdk.js?aid=s4b7b3f9,搜索这个脚本中的".com"和".png"能看到更多细节。
为了避免被清理,做了备份,方便后面举证:
https://hiproz.github.io/goodmemory.cc/blog/images/2015/12/jssdk.js-bak
* 搜索了以下,有很多dreamfull.cn的案例,多和联通有关:

安全

因为注入的原理不是在服务器修改源代码的,可能是在运营商的路由环节,或者我们使用的所谓智能路由器,或者第三方动态加载时被劫持,所以很难从根源上消除,目前能想到的就是先把https做了,还有就是加载安全js插件,用户加载时动态触发检测,不过这个只是计划,目前本人能时间上和能力还做不到。

最后

以上做了这么多细致的工作,是为了拿到更多的证据,方便更多的人去投诉和举报,创造健康的网络环境。

通过event-hook将Github自动部署至Hugo网站

WHAT

hugo是一个轻量高效的博客系统,很适合个人博客。使用hugo,我们只要写作完markdown文档,就可以利用hugo工具,自动生成网页,
变成我们的网站。

我们理想的步骤:

  • 在github上写完markdown文章
  • 提交完后,数秒后就看见了我们的网站页面
  • 在github上修改完网站的配置文件,数秒后我们的网站就变化和更新了。

好爽!

流程

为了完成上面的效果,我们大概分为几步:

  1. 设置github的webservice hook。当完成一篇新的文章或者修改旧的文章后,github就会向目标网站发webservice hook消息。
  2. 目标网站收到消息后git pull,解析消息特征,更新相关的文档,最后调用hugo
  3. hugo将markdown文章转化成html静态页面
  4. 将html页面部署到目标web服务器

HOW

详细请移步:
https://github.com/hiproz/hugo-sync

分分钟就搞定了,一劳永逸!

have fun!

hugo-md文档书写时的格式说明

前言

在决定使用github-hugo自动化流程后,第一个面临的问题就是,这个文档头的格式到底是什么样的,因为现在你要手写,就需要彻底搞清楚, 以便达到正确和高效。

阅读查找了gohugo.io的所有相关页面,找到以下两个相关的内容基本上说的很清楚了:
https://gohugo.io/content/front-matter/
https://gohugo.io/content/archetypes/

头格式

md只是文档的类型,但是里面的内容遵循怎样的格式呢? hugo目前支持3中toml,yaml,json,三种格式的文件头。识别符号分别如下

  • toml: +++
  • yaml: ---
  • json: {}

toml示例:

+++
title = "spf13-vim 3.0 release and new website"
description = "spf13-vim is a cross platform distribution of vim plugins and resources for Vim."
tags = [ ".vimrc", "plugins", "spf13-vim", "vim" ]
date = "2012-04-06"
categories = [
  "Development",
  "VIM"
]
slug = "spf13-vim-3-0-release-and-new-website"
+++

Content of the file goes Here

yaml示例:

---
title: "spf13-vim 3.0 release and new website"
description: "spf13-vim is a cross platform distribution of vim plugins and resources for Vim."
tags: [ ".vimrc", "plugins", "spf13-vim", "vim" ]
date: "2012-04-06"
categories:
  - "Development"
  - "VIM"
slug: "spf13-vim-3-0-release-and-new-website"
---

Content of the file goes Here

json示例:

{
    "title": "spf13-vim 3.0 release and new website",
    "description": "spf13-vim is a cross platform distribution of vim plugins and resources for Vim.",
    "tags": [ ".vimrc", "plugins", "spf13-vim", "vim" ],
    "date": "2012-04-06",
    "categories": [
        "Development",
        "VIM"
    ],
    "slug": "spf13-vim-3-0-release-and-new-website",
}

Content of the file goes Here

字段变量

文档可以包含很多变量。

必要字段

  • tile: 内容的标题
  • description: 内容的描述
  • date:日期
  • taxonomies:分类字段,包括tag和categories

可选字段

  • aliases: 别名
  • draft: 是否是草稿
  • publishdate: 定时未来发布的时间
  • type: 内容的格式,可以从内容自动识别
  • isCJKLanguage:是否时CJK
  • weight: 排序的权重
  • markup: 时markdown格式还是reStructuredText
  • slug: url尾部的token
  • url: 完整的url

hugo markdown引擎的配置

finish!

mac下安装php56

今天调试php的代码,提示需要PHP56,于是看了下php在github上的指导文档

Installation

Setup the homebrew/dupes tap which has dependencies we need:

$ brew tap homebrew/dupes

Setup the homebrew/versions tap which has dependencies we need:

$ brew tap homebrew/versions

Then, run the following in your command-line:

$ brew tap homebrew/homebrew-php

Usage

Note: For a list of available configuration options run:

$ brew options php56

Once the tap is installed, you can install php53, php54, php55, php56, php70, or any formulae you might need via:

$ brew install php56

按照上面的指导操作,出现如下的告警,导致tap失败 ,后继无法install成功:

$ brew tap homebrew/homebrew-php

Warning: Tap homebrew/php already tapped.

解决:先untap,然后重新tap:

$ brew untap homebrew/php

done!

go get引起的terminal prompts disabled错误

执行go get命令时,出现如下的错误提示:

fatal: could not read Username for 'https://github.com': terminal prompts disabled

两种解决方案:

  1. 先通过ssh成功登陆一次git,正确获取到key缓存。
  2. 手动添加key:https://help.github.com/articles/generating-ssh-keys/

然后就可以正常get了。

文章参考:
http://stackoverflow.com/questions/32232655/go-get-results-in-terminal-prompts-disabled-error-for-github-private-repo