饭否私信格式分析
May 31st
URL
饭否私信分为两种,一种是我收到的私信,一种是我发出的私信。
- 我收到的私信:http://fanfou.com/privatemsg/p.(1-N)
- 我发出的私信:http://fanfou.com/privatemsg/sent/p.(1-N)
上面的地址中不含饭否ID;需要cookie验证。
结束标志
通过cookie验证后,可以使用数字获得对应页码的私信内容。什么时候是结束呢?假如您的收件箱有1000条私信,每页显示20条,那么当你您输入http://fanfou.com/privatemsg/p.51时,就得不到任何有效的内容了。作为程序,它是寻找如下标志:
<ol class="wa">\s*</ol>
好友列表
在页面代码中,每页都有一个“向XXX发送私信”的combox列表,条目以昵称+ID组成。如果你的好友很多的话(500+),每条好友(昵称+ID)需要20字节(估算)的话,20*500=10K,大约需要多抓取10K的字节量。
收件箱饭否私信的结构
收到的私信分为两种,一种是有回复信息的(回复原文:…),一种是没有回复的。先从简单的入手,看没有回复的。
所有的私信都在<ol class=”wa”>…</ol>之内,以<li></li>分隔
例如下面这一条,就是一则很规范的私信(与发件人相关的信息都使以正则式表示):
<li> <a href="/[^"]+" title="[^"]+" class="avatar"><img src="[^"]+" alt="[^"]+" /></a> 来自<a href="[^"]+">[^"]+</a>: <span class="content">没法比较啊,你得说个具体的值,比如100条以下的算少,1000条以上的算多……</span> <span class="stamp time" title="2008-05-30 17:25">约 15 小时前</span> <span class="op"> <a href="/privatemsg.reply/583520">回复</a> <a href="/privatemsg.del/583520" class="post_act">删除</a></span> </li>
其中,需要记录的信息有:
- 发件人名字;
- 发件人ID;
- 私信内容;
- 时间;
- 私信ID;(便于作删除、回复处理)。
根据以上需求,将上面的私信代码作处理:
<li> <a href="/([^"]+)" title="([^"]+)" class="avatar"><img src="[^"]+" alt="[^"]+" /></a> 来自<a href="[^"]+">[^"]+</a>: <span class="content">([^<]+)</span> <span class="stamp time" title="([^"]+)">[^<]+</span> <span class="op"> <a href="/privatemsg.reply/(\d+)">回复</a> <a href="/privatemsg.del/\d+" class="post_act">删除</a> </span> </li>
从而得到:
$1=fanfou ID; $2=fanfou name; $3=private msg; $4=msg time; $5=msg ID;
再看一下包含“回复原文”的私信的结构(部分内容已作正则处理):
<li> <a href="/([^"]+)" title="([^"]+)" class="avatar"><img src="[^"]+" alt="[^"]+" /></a> 来自<a href="[^"]+">[^"]+</a>: <span class="content">([^<]+)</span> <span class="stamp time" title="([^"]+)">[^<]+</span> <span class="op"> <a href="/privatemsg.reply/(\d+)">回复</a> <a href="/privatemsg.del/\d+" class="post_act">删除</a> </span> <p class="pm-parent">回复原文: 有兴趣就有动力</p> </li>
与前者相比,只是多了<p class=”pm-parent”>.*?</p>这一段。这是不足为虑的。只要整体加上?这个强有力的正则符号,就能与上面的代码片段归纳到一起。两者结合合的代码如下:
<li> <a href="/([^"]+)" title="([^"]+)" class="avatar"><img src="[^"]+" alt="[^"]+" /></a> 来自<a href="[^"]+">[^"]+</a>: <span class="content">([^<]+)</span> <span class="stamp time" title="([^"]+)">[^<]+</span> <span class="op"> <a href="/privatemsg.reply/(\d+)">回复</a> <a href="/privatemsg.del/\d+" class="post_act">删除</a></span> (?:<p class="pm-parent">([^<]+)</p>)? </li>
得到的变量为:
$1=fanfou ID; $2=fanfou name; $3=private msg; $4=msg time; $5=msg ID; $6=parent msg;#回复原文。
发件箱饭否私信的结构
抄代码:
<li> <a href="/([^"]+)" title="([^"]+)" class="avatar"><img src="[^"]+" alt="[^"]+" /></a> 发给<a href="[^"]+">[^"]+</a>: <span class="content">海内的像片是真的。</span> <span class="stamp time" title="2008-05-28 20:24">2008-05-28 20:24</span> <span class="op"><a href="/privatemsg.del/576827" class="post_act">删除</a></span> </li>
这与“我收到的私信”的结构完全一致,只是将原来的“来自”改为“发给”而已。
不出意外,带有“回复原文”的“我收到的私信”的结构是这样的:
<li> <a href="/([^"]+)" title="([^"]+)" class="avatar"><img src="[^"]+" alt="[^"]+" /></a> 发给<a href="[^"]+">[^"]+</a>: <span class="content">在自述部分显示的那个网上。</span> <span class="stamp time" title="2008-05-29 17:00">2008-05-29 17:00</span> <span class="op"><a href="/privatemsg.del/579918" class="post_act">删除</a></span> <p class="pm-parent">回复原文: 我也要试一试。</p> </li>
我们从饭否私信代码上得到的信息就这些。遗憾的是,饭否私信中,关于“回复原文”是以明文内容形式出现,而不是以原私信ID的形式出现。后期处理时通过搜索功能解决此问题并非不能,只是如果饭否官方能够再将此功能完善的话,会省整理者不少力气。
饭否的私信源码分析完毕。至于如何读写cookie,如何写代码,如何以数据库的形式来管理下载的数据,是第二阶段的事情了。待我一一实现。
python正则表达式链接
May 26th
最近迷上了python,对它的三重引号赞不绝口,原来在Perl中一直困扰我的utf8字串问题,在python中得到圆满解决。我指的是一直在写的fanfou应用程序中,发送私信的编码问题。调用饭否API向饭否发送普通消息没有问题,因为它兼容utf8与gb2312;而发送私时,却只允许使用utf8编码。最见效的例子是发“联通”两个字。
闲话打住,切入正题,说一说python中的正则式。推荐两个网址:
- Python正则表达式操作指南,由A.M. Kuchling(amk@amk.ca)原创,由FireHare翻译,发布在ubuntu.org.cn上。该文档可以当作手册来查阅。
- Dive into python中的正则式在线文档,发布在啄木鸟上。该文档深入浅出,以例子入手,适合当作自学教材。
在《mastering regular expressions》一书中perl与php都拿出整整一章来讲解,唯独没有python的单独章节。好在既然已经知道了正则式的大概,剩下的只是查语法就是了,上面的第一个链接足矣。
另外再引用一下《mastering regular expressions》中的原话:
I thought I knew regular expressions until I read Mastering Regular Expressions. Now I do.
神往精通正则表达式的境界。
[转]各种工具之正则表达式语法比较
May 19th
本文转自永恒蓝哲的同名文章。在此表示感谢。
在各种常用的工具中, 正则表达式如此的相似却又不同。 下表列出了一些常用的正则表达式(包括grep, egrep, sed, awk, perl, vim, JavaScript),以及其不同之处。
项目众多,遗漏必有不少,请各位看官不吝指出。 以perl的正则为基准,不同的用法以粉红色标出。
点击看大图:
如果想在本地实现网页格式的列表,可以使用如下HTML代码(点击右侧+/-号展开折叠):
以perl的正则为基准,不同的用法以粉红色标出。
| 转义 | \ | \ | \ | \ | \ | \ | \ |
| 行头 | ^ | ^ | ^ | ^ | ^ | ^ | ^ |
| 行尾 | $ | $ | $ | $ | $ | $ | $ |
| n个 {n} {m,n} {m,} {,n} | \{n\} | {n} | \{n\} | {n}或\{n\} 仅定义 --posix 或 --re-interval有效(要表达}和{,得用\\{和\\} 没有定义--posix或--re-interval时,不能用{n}的语法, \}\{和}{同义 | {n} | \{n\} | {n} |
| {0,} | * | * | * | *或\*, (要表达*,得用\\*) | * | * | * |
| {1,} | \+ | + | \+ | +或\+, (要表达+, 得用\\+) | + | \+ | + |
| {0,1} | \? | ? | \? | ?或\?, (要表达?, 得用\\?) | ? | \? | ? |
| 任意字符 | . | . | . | . 含\n. | . /s修饰后则含\n | . 除\n | . 除\n |
| (pat) 匹配并获结果 | \(pat\) | (pat) | \(pat\) | (pat)或\(pat\) (要表达括号,用\\( \\) ) | (pat) | \(pat\) | (pat) |
| (?:pat) 匹配但不获结果 | 不支持 | 不支持 | 不支持 | 不支持 | (?:pat) | 不支持 | (?:pat) |
| (?=pat) 等于预查 | 不支持 | 不支持 | 不支持 | 不支持 | (?=pat) | 不支持 | (?=pat) |
| (?!pat) 不等预查 | 不支持 | 不支持 | 不支持 | 不支持 | (?!pat) | 不支持 | (?!pat) |
| | 或 | \| | | | \| | |或\| (要表达|,得用\\|) | | | \| | | |
| 其中任意字符 | [xyz] | [xyz] | [xyz] | [xyz] | [xyz] | [xyz] | [xyz] |
| [.ch.] [=ch=] | 不支持 | 不支持 | [.ch.] | 不支持 | 不支持 | 不支持 | 不支持 |
| 单词边界 \b | \b | \b | \b | 不支持 | \b | 不支持 | \b |
| 非单词边界 \B | \B | \B | \B | 不支持 | \B | 不支持 | \B |
| 单词左右边界 <> | \< \> | \< \> | \< \> | 不支持 (><和\>\<和\\>\\<同义 | 不支持(><和\>\<同义 | \< \> | 不支持(><和\>\<同义 |
| 控制字符 /cx | 不支持 | 不支持 | \cx | 不支持 | \cx | 不支持 | \cx |
| 数字\d | 不支持 | 不支持 | 不支持 | 不支持 | \d | \d | \d |
| 非数字\D | 不支持 | 不支持 | 不支持 | 不支持 | \D | \D | \D |
| 换页 \f | 不支持 | 不支持 | 高版本支持 | \f | \f | 另义 \f表示文件名字符 | \f |
| 换行 \n | 不支持 | 不支持 | 不支持 | \n | \n | \n | \n |
| 回车 \r | 不支持 | 不支持 | \r | \r | \r | \r | \r |
| 空白 \s | 不支持 | 不支持 | 不支持 | 不支持 | \s | \s | \s |
| 非空白 \S | 不支持 | 不支持 | 不支持 | 不支持 | \S | \S | \S |
| 制表符 \t | 不支持 | 不支持 | 高版本支持 | \t | \t | \t | \t |
| 垂直制表符 \v | 不支持 | 不支持 | 高版本支持 | \v | \v | 另义 \v表示very magic | \v |
| 单词字符 \w [A-Za-z0-9_] | \w | \w | \w | 不支持 | \w | \w | \w |
| 非单词字符 \W [^A-Za-z0-9] | \W | \W | \W | 不支持 | \W | \W | \W |
| \xn 16进制 | 不支持 | 不支持 | 高版本支持 | \xn | \xn | 另义 \x表示[0-9A-Za-z] | \xn |
| \n 八进制 | 不支持 | 不支持 | 不支持 | \n | \n | 不支持 | \n |
| \n 后向引用 | \n | \n | \n | \n 仅取结果可用 | \n | \n 仅取结果可用 | \n |
| [:alnum:] 字母和数字 | [:alnum:] | [:alnum:] | [:alnum:] | [:alnum:] | [:alnum:] | [:alnum:] | 不支持 |
| [:alpha:] 字母 | [:alpha:] | [:alpha:] | [:alpha:] | [:alpha:] | [:alpha:] | [:alpha:] | 不支持 |
| [:cntrl:] 控制字符 | [:cntrl:] | [:cntrl:] | [:cntrl:] | [:cntrl:] | [:cntrl:] | [:cntrl:] | 不支持 |
| [:digit:] 数字 | [:digit:] | [:digit:] | [:digit:] | [:digit:] | [:digit:] | [:digit:] | 不支持 |
| [:graph:] 可打印字符(不含空格) | [:graph:] | [:graph:] | [:graph:] | [:graph:] | [:graph:] | [:graph:] | 不支持 |
| [:lower:] 小写 | [:lower:] | [:lower:] | [:lower:] | [:lower:] | [:lower:] | [:lower:] | 不支持 |
| [:print:] 可打印字符(含空格) | [:print:] | [:print:] | [:print:] | [:print:] | [:print:] | [:print:] | 不支持 |
| [:punct:] 标点 | [:punct:] | [:punct:] | [:punct:] | [:punct:] | [:punct:] | [:punct:] | 不支持 |
| [:space:] 空格 | [:space:] | [:space:] | [:space:] | [:space:] | [:space:] | [:space:] | 不支持 |
| [:upper:] 大写字母 | [:upper:] | [:upper:] | [:upper:] | [:upper:] | [:upper:] | [:upper:] | 不支持 |
| [:xdigit:] 16进制数字 | [:xdigit:] | [:xdigit:] | [:xdigit:] | [:xdigit:] | [:xdigit:] | [:xdigit:] | 不支持 |
| [:return:] |
不支持 | 不支持 | 不支持 | 不支持 | 不支持 | [:return:] | 不支持 |
| [:tab:] |
不支持 | 不支持 | 不支持 | 不支持 | 不支持 | [:tab:] | 不支持 |
| [:escape:] |
不支持 | 不支持 | 不支持 | 不支持 | 不支持 | [:escape:] | 不支持 |
| [:backspace:] |
不支持 | 不支持 | 不支持 | 不支持 | 不支持 | [:backspace:] | 不支持 |






Comments