<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>observer专栏杂记</title>
	<atom:link href="http://69.163.35.68/?feed=rss2" rel="self" type="application/rss+xml" />
	<link>http://69.163.35.68</link>
	<description>Just another WordPress site</description>
	<lastBuildDate>Sat, 04 Sep 2010 05:55:43 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>终于搬好家了+新高性价比VPS发现</title>
		<link>http://69.163.35.68/?p=671</link>
		<comments>http://69.163.35.68/?p=671#comments</comments>
		<pubDate>Sat, 04 Sep 2010 05:51:45 +0000</pubDate>
		<dc:creator>observer</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[VPS相关]]></category>
		<category><![CDATA[站长]]></category>

		<guid isPermaLink="false">http://69.163.35.68/?p=671</guid>
		<description><![CDATA[五月以来，hostmonster就没停止过给我找麻烦，又是密码不安全重设，又是要上传有照片的ID，又是说我用了代理软件封禁，后来又说我CPU占用过大封禁；另一方面ramhost虽然没出问题，但是因为我的多个站点挤在一起，不知哪个站被GFW看不顺眼，直接被封IP了orz。 踏上重新安家之路，经过长时间的血泪史，这次终于搬好了家，到了一家很奇怪的vps公司，directspace，它家的价格白菜得简直令人不敢相信，2刀一月就有这种配置： DSVPS1 1 20GB 512MB 1GB 2TB 1 $2/m 硬盘，内存，流量无一不极品，性能可能会缩水，但是流量不会啊，罕见的2TB流量，代理翻墙分流VPN干什么都够了；官方宣称硬盘是RAID10，相比磁盘性能也该不俗；另外同样是openvz，user_beancounters也是什么都不限制 unixbench 4.1.2-wht-2 成绩也很亮眼 但从成绩上看是不输给ramhost的，实际使用中从编译性能的体验来说也是很强。 客服态度和反应时间也很快，搬了半个月了出了一次网络问题，纠结了一个下午也算是帮我搞定了。 不尽如人意的地方：网络，习惯了ramhost的40MBits的网络，再回到10MBits就觉得有点慢了，尤其是连接ubuntu镜像的速度显得不够快，其他倒是差不了太多。 另外没有额外备份也在一定程度上降低了这个plan的实用性，不过2刀的价格，比共享主机都便宜，要求就不要太多了，呵呵。 总之directspace这家性价比超高，时间长了会出现啥状况如何不好说，但是就目前来看相当地nice，这么个性能有那么白菜的价格实在是不容易，准备一系列搬迁行动了。^_^ 搬家顺便升级了wordpress到3.1，发现这个主题不错就放弃老版本了，原来的站点在 http://69.163.35.68/old/，什么都没有改。]]></description>
			<content:encoded><![CDATA[<p>五月以来，hostmonster就没停止过给我找麻烦，又是密码不安全重设，又是要上传有照片的ID，又是说我用了代理软件封禁，后来又说我CPU占用过大封禁；另一方面ramhost虽然没出问题，但是因为我的多个站点挤在一起，不知哪个站被GFW看不顺眼，直接被封IP了orz。</p>
<p>踏上重新安家之路，经过长时间的血泪史，这次终于搬好了家，到了一家很奇怪的vps公司，directspace，它家的价格白菜得简直令人不敢相信，2刀一月就有这种配置：</p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td width="128" height="39" align="left" valign="left" bgcolor="#f0f0f0"><strong><span style="color: #000000;">DSVPS1</span></strong></td>
<td width="178" align="center" valign="middle" bgcolor="#f0f0f0">1</td>
<td width="100" align="center" valign="middle" bgcolor="#f0f0f0">20GB</td>
<td width="65" align="center" valign="middle" bgcolor="#f0f0f0">512MB</td>
<td width="95" align="center" valign="middle" bgcolor="#f0f0f0">1GB</td>
<td width="114" align="center" valign="middle" bgcolor="#f0f0f0">2TB</td>
<td width="70" align="center" valign="middle" bgcolor="#f0f0f0">1</td>
<td width="77" align="center" valign="middle" bgcolor="#f0f0f0">$2/m</td>
</tr>
</tbody>
</table>
<p><span id="more-671"></span><br />
硬盘，内存，流量无一不极品，性能可能会缩水，但是流量不会啊，罕见的2TB流量，代理翻墙分流VPN干什么都够了；官方宣称硬盘是RAID10，相比磁盘性能也该不俗；另外同样是openvz，user_beancounters也是什么都不限制</p>
<p><a href="http://69.163.35.68/wp-content/uploads/2010/09/limits.png"><img class="size-medium wp-image-672 alignnone" title="limits" src="http://69.163.35.68/wp-content/uploads/2010/09/limits-183x300.png" alt="" width="183" height="300" /></a></p>
<p>unixbench 4.1.2-wht-2 成绩也很亮眼</p>
<p><a href="http://69.163.35.68/wp-content/uploads/2010/09/limits1.png"><img class="aligncenter size-full wp-image-673" title="limits" src="http://69.163.35.68/wp-content/uploads/2010/09/limits1.png" alt="" width="607" height="270" /></a></p>
<p>但从成绩上看是不输给ramhost的，实际使用中从编译性能的体验来说也是很强。</p>
<p>客服态度和反应时间也很快，搬了半个月了出了一次网络问题，纠结了一个下午也算是帮我搞定了。</p>
<p>不尽如人意的地方：网络，习惯了ramhost的40MBits的网络，再回到10MBits就觉得有点慢了，尤其是连接ubuntu镜像的速度显得不够快，其他倒是差不了太多。</p>
<p>另外没有额外备份也在一定程度上降低了这个plan的实用性，不过2刀的价格，比共享主机都便宜，要求就不要太多了，呵呵。</p>
<p>总之directspace这家性价比超高，时间长了会出现啥状况如何不好说，但是就目前来看相当地nice，这么个性能有那么白菜的价格实在是不容易，准备一系列搬迁行动了。^_^</p>
<p>搬家顺便升级了wordpress到3.1，发现这个主题不错就放弃老版本了，原来的站点在 <a href="http://69.163.35.68/old/">http://69.163.35.68/old/</a>，什么都没有改。</p>
]]></content:encoded>
			<wfw:commentRss>http://69.163.35.68/?feed=rss2&amp;p=671</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Hostmonster真是!@#$%</title>
		<link>http://69.163.35.68/?p=668</link>
		<comments>http://69.163.35.68/?p=668#comments</comments>
		<pubDate>Mon, 17 May 2010 08:04:30 +0000</pubDate>
		<dc:creator>observer</dc:creator>
				<category><![CDATA[未分类]]></category>

		<guid isPermaLink="false">http://69.163.35.68/old/?p=666</guid>
		<description><![CDATA[前一段时间忙于考试，突然有人跟我说你的主页挂了，我一看还真是，然后发现自己的帐号登录不了ssh，一怒之下去web一看却能登录，然后原来是改版。 - 要弄回ssh登录还得给他一个什么有照片的合法证件，然后我打电话问问他干吗禁用我主页，得到的回复居然是我用了什么代理软件。 - 扯皮了半天，我圈圈了个叉叉的，居然是我自己写的一个搜代理的小脚本名字叫atproxy，而且我只是放在那里做为备份（因为hostmonster号称无限空间么），你还能再过分一点么？ 我说太雷了，好吧你们的规则我实在不能理解，只能接受，那你帮我删除一下得了，我登不了ssh；他居然回复说我建议你备份所有文件然后他可以帮我清空目录。 - 我当时就要抓狂想砍他们了，那是上周的事，我没空搞，想着等我搞个ID扫描了抢回ssh再收拾他们，结果今天一看，好嘛，居然又能上了。 - 这服务商一脑残真是怎么都挡不住啊。]]></description>
			<content:encoded><![CDATA[<p>前一段时间忙于考试，突然有人跟我说你的主页挂了，我一看还真是，然后发现自己的帐号登录不了ssh，一怒之下去web一看却能登录，然后原来是改版。<br />
-<br />
要弄回ssh登录还得给他一个什么有照片的合法证件，然后我打电话问问他干吗禁用我主页，得到的回复居然是我用了什么代理软件。<br />
-<br />
扯皮了半天，我圈圈了个叉叉的，居然是我自己写的一个搜代理的小脚本名字叫atproxy，而且我只是放在那里做为备份（因为hostmonster号称无限空间么），你还能再过分一点么？<br />
<span id="more-668"></span><br />
我说太雷了，好吧你们的规则我实在不能理解，只能接受，那你帮我删除一下得了，我登不了ssh；他居然回复说我建议你备份所有文件然后他可以帮我清空目录。<br />
-<br />
我当时就要抓狂想砍他们了，那是上周的事，我没空搞，想着等我搞个ID扫描了抢回ssh再收拾他们，结果今天一看，好嘛，居然又能上了。<br />
-<br />
这服务商一脑残真是怎么都挡不住啊。</p>
]]></content:encoded>
			<wfw:commentRss>http://69.163.35.68/?feed=rss2&amp;p=668</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>SimpleCD桌面版0.1.2更新</title>
		<link>http://69.163.35.68/?p=661</link>
		<comments>http://69.163.35.68/?p=661#comments</comments>
		<pubDate>Sun, 04 Apr 2010 16:38:44 +0000</pubDate>
		<dc:creator>observer</dc:creator>
				<category><![CDATA[未分类]]></category>

		<guid isPermaLink="false">http://69.163.35.68/old/?p=661</guid>
		<description><![CDATA[我只想说：写Windows程序真tmd麻烦啊，不知道哪里的bug，折腾个半天，打包个半天，测试个半天。有谁能告诉我Windows程序该怎么写自动测试脚本？55555~~~ ` 才做了一点点小修改，浪费了我一天的时间啊时间，哭了@@ ` 所以我说嘛，像python这种无需编译直接执行的脚本语言真是最好了，跨平台直接用web，做GUI程序什么的最讨厌了…… ` 啊忘了说了，按照惯例更新在桌面版主页： http://desktop.simplecd.org，以及google code的download页面：http://code.google.com/p/simplecd/downloads/list ` 一些更新说明： ` 1.因为Windows(?)的Bug，取消了容易导致内部错误的摘要栏 ` 2.默认更新方式改为VeryCD方式，一方面减轻服务器压力，另一方面这样一次更新太多也不会更新时出错退出。（话说0.1.1出错退出的童鞋，建议你可以加内存了） ` 3.SimpleCD更新方式删除了非常占资源非常慢的评论数据库，因为sqlite的数据插入性能问题，更新评论会非常非常耗时耗力，干脆删了，主站也已经不用sqlite作为评论数据库了。而VeryCD更新方式因为更新速度本来就够慢了，插入性能反而不是问题，所以是都会更新的：） ` 4.增加了从文件更新数据库的功能，暂时可能用不到，以后大概每月更新一个增量数据库包，就不用天天更新了。 ============== 再发些牢骚： ` 纠结啊，因为代码写得烂，所以改起来开始有点费劲了，可是让我花大量时间来写“容易维护”的代码，我又没那么多美国时间。毕竟写程序又不是我的主业，上课更是一门和计算机相关的课程都没有，这只是我的一个兴趣，更准确的说只是我兴趣之一。 ` 嘛，本来因为复活节宅在家里，看acfun最近迷上了AKB48，想在周末之前把啊酱、大小姐、麻油油等省电侠的视频全部学习一遍的说，结果半天网站，一天桌面版，明天开始虽然还是假期但是计划好复习期中考试和写大作业了，欲哭无泪啊，我真不应该有那么多兴趣的，~~~>.]]></description>
			<content:encoded><![CDATA[<p>我只想说：写Windows程序真tmd麻烦啊，不知道哪里的bug，折腾个半天，打包个半天，测试个半天。有谁能告诉我Windows程序该怎么写自动测试脚本？55555~~~<br />
`<br />
才做了一点点小修改，浪费了我一天的时间啊时间，哭了@@<br />
`<br />
所以我说嘛，像python这种无需编译直接执行的脚本语言真是最好了，跨平台直接用web，做GUI程序什么的最讨厌了……<br />
`<br />
<span id="more-661"></span><br />
啊忘了说了，按照惯例更新在桌面版主页：<br />
<a href="http://desktop.simplecd.org">http://desktop.simplecd.org</a>，以及google code的download页面：<a href="http://code.google.com/p/simplecd/downloads/list">http://code.google.com/p/simplecd/downloads/list</a><br />
`<br />
一些更新说明：<br />
`<br />
1.因为Windows(?)的Bug，取消了容易导致内部错误的摘要栏<br />
`<br />
2.默认更新方式改为VeryCD方式，一方面减轻服务器压力，另一方面这样一次更新太多也不会更新时出错退出。（话说0.1.1出错退出的童鞋，建议你可以加内存了）<br />
`<br />
3.SimpleCD更新方式删除了非常占资源非常慢的评论数据库，因为sqlite的数据插入性能问题，更新评论会非常非常耗时耗力，干脆删了，主站也已经不用sqlite作为评论数据库了。而VeryCD更新方式因为更新速度本来就够慢了，插入性能反而不是问题，所以是都会更新的：）<br />
`<br />
4.增加了从文件更新数据库的功能，暂时可能用不到，以后大概每月更新一个增量数据库包，就不用天天更新了。<br />
==============<br />
再发些牢骚：<br />
`<br />
纠结啊，因为代码写得烂，所以改起来开始有点费劲了，可是让我花大量时间来写“容易维护”的代码，我又没那么多美国时间。毕竟写程序又不是我的主业，上课更是一门和计算机相关的课程都没有，这只是我的一个兴趣，更准确的说只是我兴趣之一。<br />
`<br />
嘛，本来因为复活节宅在家里，看acfun最近迷上了AKB48，想在周末之前把啊酱、大小姐、麻油油等省电侠的视频全部学习一遍的说，结果半天网站，一天桌面版，明天开始虽然还是假期但是计划好复习期中考试和写大作业了，欲哭无泪啊，我真不应该有那么多兴趣的，~~~>.<~~~<br />
`<br />
呃，发现说了很多无关话题，不管了，晚上和一帮小朋友喝酒聊天喝得有点晕晕乎乎的，更新完走人。</p>
]]></content:encoded>
			<wfw:commentRss>http://69.163.35.68/?feed=rss2&amp;p=661</wfw:commentRss>
		<slash:comments>25</slash:comments>
		</item>
		<item>
		<title>python验证码之Discuz(四)：暂时的终结</title>
		<link>http://69.163.35.68/?p=651</link>
		<comments>http://69.163.35.68/?p=651#comments</comments>
		<pubDate>Fri, 26 Mar 2010 09:15:48 +0000</pubDate>
		<dc:creator>observer</dc:creator>
				<category><![CDATA[Discuz]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[编程]]></category>
		<category><![CDATA[验证码]]></category>

		<guid isPermaLink="false">http://69.163.35.68/old/?p=651</guid>
		<description><![CDATA[看书看得好烦，想想还是把python验证码系列暂停掉好了，其实这部分代码早几周就结束了，不过想着既然说是Discuz验证码，破解了去Discuz注册一堆马甲乱灌水一通才有快感啊。 ` 哪晓得Discuz论坛管理员们早就预见了我会这么干，早早把验证码改成了中文的，没得玩了，或者说没时间来研究怎么玩了，真是太遗憾了。 ` 总之后来我下了Discuz的程序自己建了个论坛，试着批量注册自己的论坛@@ ` 其实剩下的步骤也仅仅就是写个脚本和discuz的register.php来互动而已。这种脚本早就驾轻就熟，甚至我都写过两篇文章，一篇脚本抓取，一篇VeryCD灌水机，都是差不多的内容，我也懒得废话了。 ` 总之，以下的代码实现了批量注册论坛帐号，因为网络速度和命中率的关系注册速度比较悲剧，大概是1分钟5个帐号吧，后来换了个网速好的地方测了一下可以到一分钟10多个。就这样吧，哪天我实在闲的发慌了也许会研究一下中文注册码怎么办的问题。 ` 代码： seccode4.tar 使用方法：python register.py即可，要注册新帐号的话注意把main的那段给改了，这些帐号我已经测试注册过了。]]></description>
			<content:encoded><![CDATA[<p>看书看得好烦，想想还是把python验证码系列暂停掉好了，其实这部分代码早几周就结束了，不过想着既然说是Discuz验证码，破解了去Discuz注册一堆马甲乱灌水一通才有快感啊。<br />
`<br />
哪晓得Discuz论坛管理员们早就预见了我会这么干，早早把验证码改成了中文的，没得玩了，或者说没时间来研究怎么玩了，真是太遗憾了。<br />
`<br />
<span id="more-651"></span><br />
<a href="http://69.163.35.68/wp-content/uploads/2010/03/Selection_0061.png"><img src="http://69.163.35.68/wp-content/uploads/2010/03/Selection_0061.png" alt="Selection_006" title="Selection_006" width="600" height="310" class="alignnone size-full wp-image-656" /></a><br />
总之后来我下了Discuz的程序自己<a href="http://69.163.35.68/old/crackdz">建了个论坛</a>，试着批量注册自己的论坛@@<br />
`<br />
其实剩下的步骤也仅仅就是写个脚本和discuz的register.php来互动而已。这种脚本早就驾轻就熟，甚至我都写过两篇文章，一篇脚本抓取，一篇VeryCD灌水机，都是差不多的内容，我也懒得废话了。<br />
`<br />
总之，以下的代码实现了批量注册论坛帐号，因为网络速度和命中率的关系注册速度比较悲剧，大概是1分钟5个帐号吧，后来换了个网速好的地方测了一下可以到一分钟10多个。就这样吧，哪天我实在闲的发慌了也许会研究一下中文注册码怎么办的问题。<br />
`<br />
代码：<br />
<a href='http://69.163.35.68/wp-content/uploads/2010/03/seccode4.tar.gz'>seccode4.tar</a><br />
使用方法：python register.py即可，要注册新帐号的话注意把main的那段给改了，这些帐号我已经测试注册过了。</p>
]]></content:encoded>
			<wfw:commentRss>http://69.163.35.68/?feed=rss2&amp;p=651</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Ubuntu10.04beta尝鲜感想体会：很PP很诱人</title>
		<link>http://69.163.35.68/?p=636</link>
		<comments>http://69.163.35.68/?p=636#comments</comments>
		<pubDate>Mon, 22 Mar 2010 10:49:14 +0000</pubDate>
		<dc:creator>observer</dc:creator>
				<category><![CDATA[其他]]></category>
		<category><![CDATA[ubuntu]]></category>

		<guid isPermaLink="false">http://69.163.35.68/old/?p=636</guid>
		<description><![CDATA[今天看到Ubuntu 10.04出beta了，作为一个死忠，怎能不马上下载了来试用？ ` 安装 开玩笑，自从ubuntu 9.10引入grub2以后，尝试新系统还用得着安装？ 刻录Live CD？制作启动U盘？太落伍了，俺们要直接从iso镜像文件启动 · 在/boot/grub/grub.cfg中加入一个启动项如下： menuentry &#34;Ubuntu 10.04 beta1 desktop&#34; &#123; loopback loop /boot/iso/ubuntu-10.04-beta1-desktop-i386.iso linux &#40;loop&#41;/casper/vmlinuz boot=casper iso-scan/filename=/boot/iso/ubuntu-10.04-beta1-desktop-i386.iso noeject noprompt -- initrd &#40;loop&#41;/casper/initrd.lz &#125; 重新启动后选择的启动项即可。 不知道是不是我的特例，启动时黑屏了很久，我还以为失败了，结果多等了会就进去了，害得我还以为grub项没写对很是折腾了会。 使用 ` 感谢祖国，感谢CCAV，最后感谢一下canonical，主界面终于改了，便便色的主题用了n年终于换了 ` 文件夹，发现果然把关闭按钮等很奇葩地放到了左上角 ` Terminal，这个配色是怎样！我太感动了，终于不是刺眼的白底了，我就说嘛，terminal必须得是深色底白色字用着才舒服啊，canonical太赞了，就冲这shell的配色我就对10.04迫不及待了。注意看，terminal居然还是半透明的，很pp啊 ` vim语法高亮的效果图，这看着真是太愉悦了 &#8230; <a href="http://69.163.35.68/?p=636">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>今天看到Ubuntu 10.04出beta了，作为一个死忠，怎能不马上下载了来试用？<br />
`</p>
<h2><span style="color: #808000;">安装</span></h2>
<p>开玩笑，自从ubuntu 9.10引入grub2以后，尝试新系统还用得着安装？<br />
刻录Live CD？制作启动U盘？太落伍了，俺们要直接从iso镜像文件启动<br />
·<br />
在/boot/grub/grub.cfg中加入一个启动项如下：</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">menuentry <span style="color: #ff0000;">&quot;Ubuntu 10.04 beta1 desktop&quot;</span> <span style="color: #7a0874; font-weight: bold;">&#123;</span>
        loopback loop <span style="color: #000000; font-weight: bold;">/</span>boot<span style="color: #000000; font-weight: bold;">/</span>iso<span style="color: #000000; font-weight: bold;">/</span>ubuntu-<span style="color: #000000;">10.04</span>-beta1-desktop-i386.iso
        linux <span style="color: #7a0874; font-weight: bold;">&#40;</span>loop<span style="color: #7a0874; font-weight: bold;">&#41;</span><span style="color: #000000; font-weight: bold;">/</span>casper<span style="color: #000000; font-weight: bold;">/</span>vmlinuz <span style="color: #007800;">boot</span>=casper iso-scan<span style="color: #000000; font-weight: bold;">/</span><span style="color: #007800;">filename</span>=<span style="color: #000000; font-weight: bold;">/</span>boot<span style="color: #000000; font-weight: bold;">/</span>iso<span style="color: #000000; font-weight: bold;">/</span>ubuntu-<span style="color: #000000;">10.04</span>-beta1-desktop-i386.iso noeject noprompt <span style="color: #660033;">--</span>
        initrd <span style="color: #7a0874; font-weight: bold;">&#40;</span>loop<span style="color: #7a0874; font-weight: bold;">&#41;</span><span style="color: #000000; font-weight: bold;">/</span>casper<span style="color: #000000; font-weight: bold;">/</span>initrd.lz
<span style="color: #7a0874; font-weight: bold;">&#125;</span></pre></div></div>

<p>重新启动后选择的启动项即可。<br />
不知道是不是我的特例，启动时黑屏了很久，我还以为失败了，结果多等了会就进去了，害得我还以为grub项没写对很是折腾了会。<br />
<span id="more-636"></span></p>
<h2><span style="color: #808000;">使用</span></h2>
<p>`<br />
感谢祖国，感谢CCAV，最后感谢一下canonical，主界面终于改了，便便色的主题用了n年终于换了<br />
<a href="http://69.163.35.68/wp-content/uploads/2010/03/Workspace-1_0021.png"><img class="alignnone size-full wp-image-638" title="Workspace 1_002" src="http://69.163.35.68/wp-content/uploads/2010/03/Workspace-1_0021.png" alt="Workspace 1_002" width="600" height="360" /></a><br />
`<br />
文件夹，发现果然把关闭按钮等很奇葩地放到了左上角<br />
<a href="http://69.163.35.68/wp-content/uploads/2010/03/Selection_001.png"><img class="alignnone size-full wp-image-639" title="Selection_001" src="http://69.163.35.68/wp-content/uploads/2010/03/Selection_001.png" alt="Selection_001" width="537" height="355" /></a><br />
`<br />
Terminal，这个配色是怎样！我太感动了，终于不是刺眼的白底了，我就说嘛，terminal必须得是深色底白色字用着才舒服啊，canonical太赞了，就冲这shell的配色我就对10.04迫不及待了。注意看，terminal居然还是半透明的，很pp啊<br />
<a href="http://69.163.35.68/wp-content/uploads/2010/03/Selection_004.png"><img class="alignnone size-full wp-image-640" title="Selection_004" src="http://69.163.35.68/wp-content/uploads/2010/03/Selection_004.png" alt="Selection_004" width="600" height="332" /></a><br />
`<br />
vim语法高亮的效果图，这看着真是太愉悦了<br />
<a href="http://69.163.35.68/wp-content/uploads/2010/03/Selection_005.png"><img class="alignnone size-full wp-image-641" title="Selection_005" src="http://69.163.35.68/wp-content/uploads/2010/03/Selection_005.png" alt="Selection_005" width="507" height="251" /></a><br />
`<br />
默认的中文字体好像改了？看着也蛮舒服的`<br />
<a href="http://69.163.35.68/wp-content/uploads/2010/03/Selection_006.png"><img class="alignnone size-full wp-image-642" title="Selection_006" src="http://69.163.35.68/wp-content/uploads/2010/03/Selection_006.png" alt="Selection_006" width="600" height="322" /></a><br />
`</p>
<h2><span style="color: #808000;">我燃了</span></h2>
<p>`<br />
从9.10开始，ubuntu就不停地给我惊喜，虽然才是用了下&#8221;Live iso&#8221;，10.04给我感觉已经是远超我的预期了，本来听说争议很大还以为被他们改了个什么四不像出来呢，呵呵。<br />
`<br />
Ubuntu 10.04, 我燃了</p>
]]></content:encoded>
			<wfw:commentRss>http://69.163.35.68/?feed=rss2&amp;p=636</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>西厢计划很好很强大，GFW还能拿什么来应对？</title>
		<link>http://69.163.35.68/?p=615</link>
		<comments>http://69.163.35.68/?p=615#comments</comments>
		<pubDate>Sat, 20 Mar 2010 23:44:45 +0000</pubDate>
		<dc:creator>observer</dc:creator>
				<category><![CDATA[翻墙]]></category>
		<category><![CDATA[西厢计划]]></category>

		<guid isPermaLink="false">http://69.163.35.68/old/?p=615</guid>
		<description><![CDATA[好久没看google reader了，险些错过了极其凶猛的一个翻墙工具“西厢计划”。 ` 西厢计划原理的进一步思考 ` 我看了西厢计划项目的wiki还是云里雾里的，好在youxu大大的西厢计划原理小解解释的非常清楚，在这里我也简单归纳一下，西厢计划就是在TCP建立连接之初通过插入发送完全符合协议的TCP包欺骗GFW连接已经终结，来达到穿墙的目的。原理据说如下图。 ` 根据“原理”的说法，张生同学实际上是利用了GFW本身的特性。（事实上，墙可以视作一种网络入侵检测系统，有着所有NIDS系统的弱点。）因为性能原因，墙作为一个旁观者，它无法做到对每一条TCP包都进行检查来过滤敏感词汇。tek4小组的大牛们，据说也就是西厢计划的探索者，经过长时间对墙的研究，发现墙很聪明地只会在连接发起的时候进行检测。 ` 这样做是可以说的通的。中国的出国带宽应该早已破T，如果墙需要检查所有的TCP包，会是很大的负担。所以墙才会检查TCP会话的状态，当发现是死老虎状态时，直接放跑所有后继TCP会话。 ` 然而我有一个百思不得其解的问题让我怀疑墙是否只在连接发起时进行过滤。我做了两个测试： 我建立了一个网页http://test.app-base.com/link.html，这个网页中含有falun词汇，并且有一个链接访问&#8221;/?falun&#8221; 测试1. 访问link.html。结论正常。 测试2. 访问link.html，再点击falun链接，结果被重置。 ` 测试一结束后，我和服务器已经完成了三次握手，并且有来有回地发送了GET和获取了网页，这个时候我还有keep-alive属性，根据我对http 1.1的理解，这时候并没有终止TCP连接，而是立即回应了另一个请求，但是这个请求却被墙重置了，所以墙只在TCP连接发起时进行过滤这个说法我觉得有问题。当然我对网络协议还是很一知半解的，有说错的话请及时更正我，谢谢。 ` 按照我的上述实验来看，墙并非只在TCP连接发起时进行过滤，而是对每一个TCP包都做检查，发现是GET请求就进行过滤，发现是回应则PASS（至少测试1没有被过滤，如果它扫描的话那就说明基于不同规则。） ` 然而西厢计划的成功实施，说明墙是保存连接信息的，所以它在记录两个主机的连接“终止”以后，放跑了所有这两个主机间的流量。那么关键的问题是，如果墙取消这么一个设定，不“放跑”已经“终止”的两个主机之间的流量，会对墙造成很大负担吗？ ` 如果会，那么墙想要干掉西厢计划还是很困难的；如果不会，那么修正这个破绽简直轻而易举。 ` 上述问题等价于：实际应用中，是否有很多正常的应用会在RST终止以后还是罗里罗嗦地发送一堆包？出于网络延时我觉得RST以后确实可能会发送一堆包，但是这个数量相对于正常应用的TCP包来说应该是小巫见大巫。那么为什么墙要做那么一个古怪的限制呢？为什么墙要记录TCP连接状态，阻断连接以后就直接无视后续的包呢？ ` 思索了半天，我所能想到的唯一一个理由就是墙那么做是为了避免DDoS攻击。如果墙还要继续监控数据包，这可能会造成潜在的攻击点：攻击者不断地发送同样的数据包，墙必须一次又一次地拦截并且发送RST给攻击者，大规模地攻击下，墙的过滤系统资源将被耗尽，从而无法进行正常的运作。而如果墙能够确认连接已经重置，就算仅仅是出于性能优化考虑，判断主机IP决定是否过滤也要高效得多。 ` 如果我是墙，会怎么应对？ 1. 无视reset信号，不记录主机间的连接状态，所有TCP包都加入过滤。 ` 这是最容易的一种处理方法，但是也是（据我推测）有安全和性能隐患的一种处理方式，理由见前文。 ` 2. 记录三次握手序号，封锁西厢计划。 ` &#8230; <a href="http://69.163.35.68/?p=615">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>好久没看google reader了，险些错过了极其凶猛的一个翻墙工具“<a href="http://code.google.com/p/scholarzhang/">西厢计划</a>”。<br />
`</p>
<h2><span style="color: #99cc00;"> 西厢计划原理的进一步思考</span></h2>
<p>`<br />
我看了<a href="http://code.google.com/p/scholarzhang/wiki/README">西厢计划项目的wiki</a>还是云里雾里的，好在youxu大大的<a href="http://blog.youxu.info/2010/03/14/west-chamber/">西厢计划原理小解</a>解释的非常清楚，在这里我也简单归纳一下，西厢计划就是在TCP建立连接之初通过插入发送完全符合协议的TCP包欺骗GFW连接已经终结，来达到穿墙的目的。原理据说如下图。<br />
<span id="more-615"></span><br />
<a href="http://69.163.35.68/wp-content/uploads/2010/03/xixiang.png"><img class="alignnone size-full wp-image-616" title="xixiang" src="http://69.163.35.68/wp-content/uploads/2010/03/xixiang.png" alt="xixiang" width="550" height="413" /></a><br />
`<br />
根据“原理”的说法，张生同学实际上是利用了GFW本身的特性。（事实上，墙可以视作一种<a href="http://en.wikipedia.org/wiki/Network_intrusion_detection_system">网络入侵检测系统</a>，有着所有<a href="http://book.51cto.com/art/200708/54659.htm">NIDS系统的弱点</a>。）因为性能原因，墙作为一个旁观者，它无法做到对每一条TCP包都进行检查来过滤敏感词汇。<a href="http://gfwrev.blogspot.com/2010/03/blog-post.html">tek4小组</a>的大牛们，据说也就是西厢计划的探索者，经过长时间对墙的研究，发现墙很聪明地只会在连接发起的时候进行检测。<br />
`<br />
这样做是可以说的通的。中国的<a href="http://en.wikipedia.org/wiki/CUCN_(cable_system)">出国带宽</a>应该<a href="http://tech.sina.com.cn/t/2007-05-24/00181524112.shtml">早已破T</a>，如果墙需要检查所有的TCP包，会是很大的负担。所以墙才会检查TCP会话的状态，当发现是死老虎状态时，直接放跑所有后继TCP会话。<br />
`<br />
然而<span style="color: #0000ff;">我有一个百思不得其解的问题</span>让我怀疑<span style="color: #0000ff;">墙是否只在连接发起时进行过滤</span>。我做了两个测试：<br />
我建立了一个网页http://test.app-base.com/link.html，这个网页中含有falun词汇，并且有一个链接访问&#8221;/?falun&#8221;<br />
测试1. 访问link.html。结论正常。<br />
测试2. 访问link.html，再点击falun链接，结果被重置。<br />
`<br />
测试一结束后，我和服务器已经完成了三次握手，并且有来有回地发送了GET和获取了网页，这个时候我还有keep-alive属性，根据我对http 1.1的理解，这时候并没有终止TCP连接，而是立即回应了另一个请求，但是这个请求却被墙重置了，所以墙只在TCP连接发起时进行过滤这个说法我觉得有问题。当然<span style="color: #ff0000;">我对网络协议还是很一知半解的，有说错的话请及时更正我，谢谢。</span><br />
`<br />
按照我的上述实验来看，墙并非只在TCP连接发起时进行过滤，而是对每一个TCP包都做检查，发现是GET请求就进行过滤，发现是回应则PASS（至少测试1没有被过滤，如果它扫描的话那就说明基于不同规则。）<br />
`<br />
然而西厢计划的成功实施，说明墙是保存连接信息的，所以它在记录两个主机的连接“终止”以后，放跑了所有这两个主机间的流量。那么关键的问题是，如果墙取消这么一个设定，不“放跑”已经“终止”的两个主机之间的流量，会对墙造成很大负担吗？</p>
<p>`</p>
<p>如果会，那么墙想要干掉西厢计划还是很困难的；如果不会，那么修正这个破绽简直轻而易举。</p>
<p>`<br />
上述问题等价于：实际应用中，是否有很多正常的应用会在RST终止以后还是罗里罗嗦地发送一堆包？出于网络延时我觉得RST以后确实可能会发送一堆包，但是这个数量相对于正常应用的TCP包来说应该是小巫见大巫。那么为什么墙要做那么一个古怪的限制呢？<span style="color: #0000ff;">为什么墙要记录TCP连接状态，阻断连接以后就直接无视后续的包呢？</span><br />
`<br />
思索了半天，我所能想到的唯一一个理由就是<span style="color: #0000ff;">墙那么做是为了避免DDoS攻击</span>。如果墙还要继续监控数据包，这可能会造成潜在的攻击点：攻击者不断地发送同样的数据包，墙必须一次又一次地拦截并且发送RST给攻击者，大规模地攻击下，墙的过滤系统资源将被耗尽，从而无法进行正常的运作。而如果墙能够确认连接已经重置，就算仅仅是出于性能优化考虑，判断主机IP决定是否过滤也要高效得多。<br />
`</p>
<h2><span style="color: #99cc00;">如果我是墙，会怎么应对？</span></h2>
<p><span style="color: #0000ff;">1. 无视reset信号，不记录主机间的连接状态，所有TCP包都加入过滤。</span><br />
`<br />
这是最容易的一种处理方法，但是也是（据我推测）有安全和性能隐患的一种处理方式，理由见前文。<br />
`<br />
<span style="color: #0000ff;"> 2. 记录三次握手序号，封锁西厢计划。</span><br />
`<br />
西厢计划骗墙认为receiver已经死老虎是通过发送一个序号不对的RST给sender，sender会无视，墙却会中招以为是死老虎。<br />
`<br />
如果墙想逮住&#8221;死老虎&#8221;，他必须记录sender给receiver的SYN/ACK握手的序号，并且和receiver再次返回给sender的ACK序号进行比对才能确定这个是否是西厢计划的一次欺骗。从数据结构上来看，需要多保存一个SEQ号，这是一个32bit的数，相比原来多占1/3的空间；而且如果墙是硬件实现的话（我觉得是），改变数据结构可不容易；从性能上来看，要多做一次主机的匹配和ACK序号的比对，性能开销也不小；而且墙一旦这么做了，隐患更大，之前因为不太了解墙的工作模式，想攻击都没法攻击，既然你用这么个办法来堵漏洞，那我就有的放矢直接灌水各种RST信号，那么墙就真的悲剧了。<br />
`<br />
看起来我觉得这种补救方式更加得不偿失。<br />
`<br />
<span style="color: #0000ff;"> 3. 直接用IP封锁。（黑名单）</span><br />
`<br />
好像很犀利，不过可以直接通过代理绕过去，以前用代理因为有关键字过滤所以也没什么用，有了西厢计划，再加上代理，封IP就变得非常徒劳了。至于什么代理？什么都可以，squid、web代理、反向代理等、只要在墙外就行。<br />
`<br />
<span style="color: #0000ff;"> 4. 白名单。</span><br />
`<br />
真的是大中华局域网了么，@@<br />
这种情况下除非有白名单中包含国外主机，那还能通过代理出去，否则，就一个局域网怎么翻墙？这是最狠的一招，我觉得不太可能发生，但是永远不要低估某些人的脑残程度，万一……<br />
`</p>
<h2><span style="color: #99cc00;">为何不直接对内容过滤？从TCP协议的角度来看</span></h2>
<p>`<br />
根据我自己的测试，GFW似乎没有对内容进行过滤。不考虑处理能力的问题，理论上GFW似乎可以通过随机检查TCP包，然后发送阻断包终止连接。<br />
`<br />
然而他真的能发送阻断包吗？扫了一下TCP/IP协议，我发现它能干的事情有限。这是因为一旦连接建立，慢启动过后，TCP就开始高速运转了，sender同时会发送n个包给receiver，墙截取了当前的包，发觉有问题，这时候他想干扰传输有两个选择，要么发RST，要么发FIN(<a href="http://tools.ietf.org/html/rfc1122#page-87">RFC1122,4.2.2.13</a>)。可是这时候墙无法推测下一个序列号，因为他无法知道该死的sender到底同时发了多少个包给receiver，只能靠猜。<br />
`<br />
但是并不是说墙没有办法，根据TCP协议的<a href="http://en.wikipedia.org/wiki/Transmission_Control_Protocol#Flow_control">Flow Control部分</a>，receiver会给sender发一个windows size的参数，规定sender只能发那么多数据，之后要等待receiver的ACK表示收到。发送ACK表示收到的同时，receiver还会告诉sender它的acknownledge number，表示他预期下一个序列号是什么，这时候墙就能能乘虚而入伪造RST/FIN数据给receiver了。<br />
`<br />
这又是一个复杂的实现+性能和安全隐患，我想这也是为啥墙一般不直接做内容过滤，而是更倾向于url扫描和dns污染的另一个理由吧。<br />
`</p>
<h2><span style="color: #99cc00;">结论</span></h2>
<p>西厢计划很好很强大，让我看到了墙的脆弱性，随着对墙的越来越深入的了解，越来越多牛人加入翻墙的研究，墙终究会变得不堪一击。道高一尺，魔高一丈。通过这次对墙的原理的学习，我突然觉得墙其实还是很千疮百孔的，并没有我原来想象的那么强大。就那么个破东西，国家花了无数纳税人的钱，只为了阻止纳税人们了解真实的世界，唉，很是无语啊。<br />
`<br />
最后补充一下，我本人对TCP的了解仅限于学得很是不行的“计算机网络”的知识，而且因为从来不用基本忘光，如果狗屁不通错误连篇也请谅解，如果能指出错误就更好了。<br />
`</p>
<h2><span style="color: #99cc00;">3月22日更新</span></h2>
<p>首先，tek4小组在完成西厢计划后已经解散，解散之后他们发布了对GFW和西厢计划的一个总结，更宏观地解释了GFW的应对方式，证实了西厢计划并没有那么强大，见参考资料11。<br />
`<br />
其次，由西厢计划得到启发，结合“第八组第三次作业”中提到的忽略RST穿墙法（参考资料12），西厢计划似乎可以简化实现，并且由于实施单方面忽略RST而不是企图欺骗墙的关系，性能和无解程度会大大增强。如果和参考资料10的判断IDS结合起来，说不定会有很好的效果。<br />
`<br />
最后，第一次感觉不在墙内真是遗憾，那么有趣的技术没有办法实践，sigh&#8230;<br />
`</p>
<h2><span style="color: #99cc00;">3月23日更新</span></h2>
<p>西厢计划出“<a href="http://code.google.com/p/west-chamber-season-2/">第二季</a>”了，其实和西厢计划没什么关系，可以说应该只是受到了西厢计划的启发。<br />
`<br />
这是一个很赞的实现，充分展示了墙的脆弱性，真是千疮百孔啊，未来没准第三季、第四季还会陆续有来。<br />
`</p>
<h2><span style="color: #99cc00;">参考资料</span></h2>
<p>1. <a href="http://en.wikipedia.org/wiki/Transmission_Control_Protocol#Flow_control">TCP协议</a><br />
2. <a href="http://tools.ietf.org/html/rfc1122#page-82">RFC1122(TCP部分,82-92)</a><br />
3. <a href="http://en.wikipedia.org/wiki/IPv4#Data">IPv4</a><br />
4. <a href="http://blog.youxu.info/2010/03/14/west-chamber/">西厢计划原理小解</a><br />
5. 西厢计划提到的<a href="http://docs.google.com/viewer?a=v&#038;q=cache:FWedN_jw8qwJ:www.creangel.com/papers/Eluding%2520Network%2520Intrusion%2520Detection.pdf+Insertion,+Evasion,+and+Denial+of+Service:+Eluding+Network+Intrusion+Detection&#038;hl=zh-CN&#038;gl=cn&#038;pid=bl&#038;srcid=ADGEEShziRA5_CDojJzt2hOkfgLdFST44K1-PKli3w_vW-kP0emw4tmLwJhpu5FK5br6rTSvnj65WaAt87dAvpbzl-G0ll48MKD2KUS3ROjarH-AjCYM8uA9U-CZCD2TChhkGefPrF1S&#038;sig=AHIEtbRWLNJqvK_b06ebtZ60iPedrpXOJQ">那篇论文</a>，我扫了一眼居然发现了和我看了协议以后做的推理一样的某些段落，不禁小得意一下。<br />
6. <a href="http://www.tektalk.org/2010/02/25/%E7%A9%BF%E8%B6%8Agfw%E7%9A%84%E6%8A%80%E6%9C%AF/">穿越GFW的技术</a>，很不错的总结。<br />
7. <a href="http://gfwrev.blogspot.com/2009/11/gfw_10.html">GFW技术，研究与工具</a>这个博客上面的内容都很有趣，值得一读。<br />
8. <a href="http://gfwrev.blogspot.com/2010/02/gfw.html">深入理解GFW，内部结构</a>也是上面这个博客的内容，很强大。<br />
9. <a href="http://book.51cto.com/art/200708/54659.htm">NIDS网络入侵检测系统的缺陷</a>。GFW作为一个NIDS，我们可以利用的漏洞都有总结，有些可以用，有些不行。<br />
10. 一篇关于<a href="http://docs.google.com/viewer?a=v&#038;q=cache:aIJG_HTUc18J:www.icir.org/vern/papers/reset-injection.ndss09.pdf+TCP+RST&#038;hl=en&#038;pid=bl&#038;srcid=ADGEEShR0c1_H2nqOL2F5Ra9PAO3h1rSV5_LXSYu52t9iDZyJnlHJ0MNOfXrV_-fcNZprjbvfb7InP4FVIu2bzNw9Qsme623rrpIlETLQThU85uNh4StCJ7vMbQWSrsJXj4rcZNxNven&#038;sig=AHIEtbSeUheI_XXc9x1K1TFnuoXAW8W3kg">如何发现虚假RST攻击</a>的论文，扫了一下觉得挺有用，以后可能用得到。<br />
11. <a href="http://gfwrev.blogspot.com/2010/03/gfw.html#more">原班人马对西厢计划弱点和GFW应对的解释</a>，很有趣，我也算是千分之一了吧。<br />
12. <a href="http://go2.wordpress.com/?id=725X1342&#038;site=xijie.wordpress.com&#038;url=http%3A%2F%2Fcourse.ccert.edu.cn%2Fwiki%2Findex.php%2FTalk%3AGroup8">第八组第三次作业</a>中提出了<a href="http://course.ccert.edu.cn/2009/wiki/index.php/Talk:Group8#5.E3.80.81.E5.85.B6.E4.BB.96.E7.A9.BF.E8.B6.8AGFW.E7.9A.84.E6.8A.80.E6.9C.AF">直接忽略GFW的RST包也可以完成翻墙</a>，结合参考文章10，我觉得西厢计划可以有更简单和更强大的实现。</p>
]]></content:encoded>
			<wfw:commentRss>http://69.163.35.68/?feed=rss2&amp;p=615</wfw:commentRss>
		<slash:comments>20</slash:comments>
		</item>
		<item>
		<title>SQL vs NoSQL：数据库并发写入性能比拼</title>
		<link>http://69.163.35.68/?p=610</link>
		<comments>http://69.163.35.68/?p=610#comments</comments>
		<pubDate>Thu, 18 Mar 2010 12:47:18 +0000</pubDate>
		<dc:creator>observer</dc:creator>
				<category><![CDATA[其他]]></category>
		<category><![CDATA[性能测试]]></category>
		<category><![CDATA[数据库]]></category>

		<guid isPermaLink="false">http://69.163.35.68/old/?p=610</guid>
		<description><![CDATA[最近听说了很多关于NoSQL的新闻，比如之前Sourceforge改用MongoDB，Digg改用Cassandra等等。再加上之前做数据库比较时有人推荐我mongodb，所以也搜索了一下NoSQL，觉得NoSQL可能真的是未来的趋势。 ` NoSQL vs SQL 传统SQL数据库为了实现ACID(atomicity, consistency, isolation, durability)，往往需要频繁应用文件锁，这使得其在现代的web2.0应用中越来越捉襟见肘。现在SNS网站每一个点击都是一条/多条查询，对数据库写的并发要求非常之高，而传统数据库无法很好地应对这种需求。而仔细想来SNS中大部分需求并不要求ACID，比如Like/Unlike投票等等。 NoSQL吸取了教训，比如有些NoSQL采用了eventually consistency的概念，在没有Update操作一段时间后，数据库将最终是“consistency”的，显然这样的数据库将能更好的支持高并发读写。 ` SQL数据库是基于schema的，这对时时刻刻更新着的web2.0应用开发者来说是个噩梦：随时随地有新的应用出现，旧的数据库无法适应新的应用，只能不停地更新schema，或者做补丁表，如此一来要么schema越发混乱，要么就是数据库频繁升级而耗时耗力耗钱。 ` NoSQL一般就没有schema这种概念，大部分NoSQL都直接保存json类的Row，比如一个记录可以是 { &#8216;id&#8217; = 1, name = &#8216;Bob&#8217;, phone = 38492839 }，这样扩展升级非常方便，比如需要地址信息直接加入 address=&#8217;blahblah&#8217; 即可。 ` 传统SQL很难进行分布式应用，即使可以也往往代价高昂。而NoSQL则很好地解决了这个问题：他们一般都直接从分布式系统中吸取了Map/Reduce方法，从而很容易就可以处理规模急速增加的问题。 ` 推荐robbin牛的NoSQL数据库探讨之一 － 为什么要用非关系数据库？一文，介绍了主流的一些NoSQL系统，还有这个站http://nosql-database.org/收集了基本上目前所有的NoSQL系统。 ` 总结一下我对NoSQL的看法，NoSQL出现的目的就是为了解决高并发读写的问题，而高并发应用往往需要分布式的数据库来实现高性能和高可靠性，所以NoSQL的关键字就是concurrency和scalability。 ` 我的瓶颈 我之前主要关注数据库的select性能也就是read性能，在读性能方面SQL数据库并没有明显的劣势，应该说纯粹高并发读的性能的话往往要优于NoSQL数据库，然而一旦涉及写，事情就不一样了。 ` &#8230; <a href="http://69.163.35.68/?p=610">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>最近听说了很多关于NoSQL的新闻，比如之前Sourceforge改用MongoDB，Digg改用Cassandra等等。再加上之前做数据库比较时有人推荐我mongodb，所以也搜索了一下NoSQL，觉得NoSQL可能真的是未来的趋势。<br />
`</p>
<h1><span style="color: #008000;"> NoSQL vs SQL</span></h1>
<p>传统SQL数据库为了实现ACID(atomicity, consistency, isolation, durability)，往往需要频繁应用文件锁，这使得其在现代的web2.0应用中越来越捉襟见肘。现在SNS网站每一个点击都是一条/多条查询，对数据库写的并发要求非常之高，而传统数据库无法很好地应对这种需求。而仔细想来SNS中大部分需求并不要求ACID，比如Like/Unlike投票等等。<br />
<span id="more-610"></span><br />
NoSQL吸取了教训，比如有些NoSQL采用了eventually consistency的概念，在没有Update操作一段时间后，数据库将最终是“consistency”的，显然这样的数据库将能更好的支持高并发读写。<br />
`<br />
SQL数据库是基于schema的，这对时时刻刻更新着的web2.0应用开发者来说是个噩梦：随时随地有新的应用出现，旧的数据库无法适应新的应用，只能不停地更新schema，或者做补丁表，如此一来要么schema越发混乱，要么就是数据库频繁升级而耗时耗力耗钱。<br />
`<br />
NoSQL一般就没有schema这种概念，大部分NoSQL都直接保存json类的Row，比如一个记录可以是<br />
{ &#8216;id&#8217; = 1, name = &#8216;Bob&#8217;, phone = 38492839 }，这样扩展升级非常方便，比如需要地址信息直接加入 address=&#8217;blahblah&#8217; 即可。<br />
`<br />
传统SQL很难进行分布式应用，即使可以也往往代价高昂。而NoSQL则很好地解决了这个问题：他们一般都直接从分布式系统中吸取了Map/Reduce方法，从而很容易就可以处理规模急速增加的问题。<br />
`<br />
推荐robbin牛的<a href="http://robbin.javaeye.com/blog/524977">NoSQL数据库探讨之一 － 为什么要用非关系数据库？</a>一文，介绍了主流的一些NoSQL系统，还有这个站<a href="http://nosql-database.org/">http://nosql-database.org/</a>收集了基本上目前所有的NoSQL系统。<br />
`<br />
总结一下我对NoSQL的看法，NoSQL出现的目的就是为了解决高并发读写的问题，而高并发应用往往需要分布式的数据库来实现高性能和高可靠性，所以NoSQL的关键字就是<strong>concurrency</strong>和<strong>scalability</strong>。<br />
`</p>
<h1><span style="color: #008000;">我的瓶颈</span></h1>
<p>我之前主要关注数据库的select性能也就是read性能，在读性能方面SQL数据库并没有明显的劣势，应该说纯粹高并发读的性能的话往往要优于NoSQL数据库，然而一旦涉及写，事情就不一样了。<br />
`<br />
我本来以为自己不会遇到大量写的问题，后来发现即使在simplecd这种简单的应用环境下也会产生大量的并发写：这就是爬VC用户评论的时候。事实上，sqlite3在处理这个问题上非常的力不从心，所以我产生了换个数据库的想法。<br />
`<br />
既然我是要求能高并发读写，干脆就不用SQL了，但是同时我也想测试一下其他SQL的写性能。<br />
我的数据有180万条，总共350M，测试用了10个线程，每个线程做若干次100个数据的bulk写入，然后记录总共耗时。结果如下。<br />
`<br />
innodb:    15.19<br />
myiasm:  14.34<br />
pgsql:       23.41<br />
sqlite3:      锁住了<br />
sqlite3(单线程):   300+<br />
mongodb:  3.82<br />
couchdb:    90<br />
couchdb(单线程）:66<br />
`<br />
作为一个MySQL黑，看到这组测试数据我表示压力很大。在SQL数据库中，mysql意外地取得了最佳的成绩，好于pgsql，远好于sqlite。更令人意外的是myisam居然优于号称insert比较快的innodb。不管如何，对我的应用来说，用mysql保存评论数据是一个更为明智的选择。我对mysql彻底改观了，我宣布我是mysql半黑。以后select-intensive的应用我还是会选择sqlite，但是insert/update-intensive的应用我就会改用mysql了。<br />
`<br />
MongoDB和CouchDB同为NoSQL，表现却截然相反，MongoDB性能很高，CouchDB的并发性能我只能ORZ，这种性能实在太抱歉了。<br />
`</p>
<h1><span style="color: #008000;">NoSQL的碎碎念</span></h1>
<p>其实我本来还打算测试cassandra的，可是cassandra用的是java，这首先让我眉头一皱，内存大户我养不起啊，其次看了cassandra的文档，立刻崩溃，这简直就是没有文档么。（BTW，CouchDB也好不到哪里去，我都是用python-couchdb然后help(couchdb.client)看用法的）<br />
`<br />
至于CouchDB，可能是因为采用http方式发送请求，所以并发性能糟糕的一塌糊涂，很怀疑它是否有存在的理由。<br />
`<br />
MongoDB是我用下来最讨人喜欢的一个NoSQL。不但文档丰富，使用简单，性能也非常好，它的Map/Reduce查询(很多NoSQL都有)让我惊叹，数据库可以非常简单地就扩大规模，完全不用理会什么分区分表之类繁琐的问题，可惜这方面我暂时没有需求。但是MongoDB有两大致命问题。<br />
`<br />
第一是删除锁定问题，当批量删除记录时，数据库还是会锁定不让读写。这意味着进行数据清理时会让网站应用失去响应。见<a href="http://go2.wordpress.com/?id=725X1342&#038;site=boxedice.wordpress.com&#038;url=http%3A%2F%2Fgroups.google.com%2Fgroup%2Fmongodb-user%2Fbrowse_thread%2Fthread%2F810f410f1c997a52">locking problems</a><br />
`<br />
第二是内存占用问题，MongoDB用了操作系统的内存文件映射，这导致操作系统会把所有空闲内存都分配给MongoDB，当MongoDB有这个需要时。更可怕的是，MongoDB从来不主动释放已经霸占的内存，它只会滚雪球一样越滚越大，除非重启数据库。这样的上下文环境下，MongoDB只适合一台主机就一个数据库，而没有其他应用的环境，否则一会儿功夫MongoDB就会吃光内存，然后你都fork不出新进程，彻底悲剧。见<a href="http://groups.google.com/group/mongodb-user/browse_thread/thread/2646a52c4f41d832/c5579706d733c21a?lnk=gst&#038;q=memory#c5579706d733c21a">memory limit</a><br />
`<br />
总之NoSQL虽然让我眼前一亮，可是目前尝试的一些产品都让人望而生畏，现在的NoSQL都把目光放在了巨型网站上，而没有一个小型的，可以在VPS里面应用的高性能NoSQL，令我有点失望。NoSQL尚未成熟，很期待它的将来发展，目前来说MySQL还是更好的选择。</p>
<h1><span style="color: #008000;">推荐一些相关文章</span></h1>
<p>详细介绍了为何从MySQL转移到MongoDB的一篇文章，还指出了迁移过程中遇到的问题和他们的解决方法： http://blog.boxedice.com/2009/07/25/choosing-a-non-relational-database-why-we-migrated-from-mysql-to-mongodb/</p>
<p>MySQL,MongoDB,Tokyo Cabinet的性能测试对比，写得很好： http://jayant7k.blogspot.com/2009/08/document-oriented-data-stores.html</p>
<p>从概念上比较了MongoDB和CouchDB实现的不同： http://www.mongodb.org/display/DOCS/Comparing+Mongo+DB+and+Couch+DB</p>
]]></content:encoded>
			<wfw:commentRss>http://69.163.35.68/?feed=rss2&amp;p=610</wfw:commentRss>
		<slash:comments>13</slash:comments>
		</item>
		<item>
		<title>python验证码识别之Discuz(三)</title>
		<link>http://69.163.35.68/?p=597</link>
		<comments>http://69.163.35.68/?p=597#comments</comments>
		<pubDate>Fri, 12 Mar 2010 06:14:51 +0000</pubDate>
		<dc:creator>observer</dc:creator>
				<category><![CDATA[Discuz]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[编程]]></category>
		<category><![CDATA[验证码]]></category>

		<guid isPermaLink="false">http://69.163.35.68/old/?p=597</guid>
		<description><![CDATA[继续验证码的码字，上次把去噪的算法重新写了一下，并指出了可以改进的部分，不过我是安于现状的人，弄了个60%左右成功率的就沾沾自喜了，姑且就进入下一步吧。 ` 这期间由于discuz官方论坛不知出于何种原因突然改变了验证码的生成方式搞了个中文验证码，我本来计划着扩充测试库到100+个，好好测一下成功率的，可是他们这么一搞无奈只好自己根据Discuz7.2　UTF-8版架了个专门用于破解验证的论坛，把设置重现为Discuz官方论坛以前的设置：http://69.163.35.68/old/crackdz/　。至于中文验证码，虽然感觉也能做，就不知道有没有时间来研究中文验证码了。 其中验证码设置如下: ` 配置好环境以后，继续开始切块吧。 1.切块 其实我觉得最好的切块算法就是前文提及的聚类算法，在去噪正确的前提下，用聚类算法一聚就完了。因为我之前没想到聚类算法，使用了很直观的直接按列扫描划分的办法，但是后来发现Discuz有部分验证码这样是无法划分的，必须得聚类，again我偷懒了，因为已经有能用的现成代码，效果虽然差了点，也能过得去了，于是就懒得改了。 ` 这部分代码写得很难看，但是能用，就不贴出来丢人现眼了。 ` 2.规范化 所谓规范化其实就是旋转。为什么要旋转？因为旋转能够极大地减少样本库的个数，提升比对速度。之前有童鞋建议说可以采样不同角度的字母作为样本，可是我上哪找不同角度旋转后的样本？自己旋转经常会旋转得不伦不类的，要是直接采样那得采集多少个样本才能覆盖完整啊，1千个？1万个？想想就不高兴弄了，所以为了让我的生成特征库的工作省点力气，我决定还是规范化一下来得省力。 ` 怎样旋转一个字母才算是转正了呢？我用了一个很土的算法：先旋转，再去掉边缘的空白边，然后计算该图形的“密度”（字体部分与整个像素之比），我定义密度最小的那个旋转体就是“规范化”的字母。事实上也可以这样定义：定义上下高度最窄的旋转体为规范化体。这两种定义应该是差不多等效的。 ` 规范化代码如下 def normalize&#40;im&#41;: # divide im regions = imdiv&#40;im&#41; # 这是划分方法1 if len&#40;regions&#41;!=4: regoins = imdiv2&#40;im&#41; # 这是划分方法2 &#160; for k in range&#40;len&#40;regions&#41;&#41;: regions&#91;k&#93; &#8230; <a href="http://69.163.35.68/?p=597">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>继续验证码的码字，上次把去噪的算法重新写了一下，并指出了可以改进的部分，不过我是安于现状的人，弄了个60%左右成功率的就沾沾自喜了，姑且就进入下一步吧。<br />
`<br />
这期间由于discuz官方论坛不知出于何种原因突然改变了验证码的生成方式搞了个中文验证码，我本来计划着扩充测试库到100+个，好好测一下成功率的，可是他们这么一搞无奈只好自己根据Discuz7.2　UTF-8版架了个专门用于破解验证的论坛，把设置重现为Discuz官方论坛以前的设置：http://69.163.35.68/old/crackdz/　。至于中文验证码，虽然感觉也能做，就不知道有没有时间来研究中文验证码了。<br />
<span id="more-597"></span><br />
其中验证码设置如下:<br />
<a href="http://69.163.35.68/wp-content/uploads/2010/03/settings.png"><img class="alignnone size-full wp-image-598" title="settings" src="http://69.163.35.68/wp-content/uploads/2010/03/settings.png" alt="settings" width="500" height="720" /></a><br />
`<br />
配置好环境以后，继续开始切块吧。</p>
<h1><span style="color: #808000;">1.切块</span></h1>
<p>其实我觉得最好的切块算法就是前文提及的聚类算法，在去噪正确的前提下，用聚类算法一聚就完了。因为我之前没想到聚类算法，使用了很直观的直接按列扫描划分的办法，但是后来发现Discuz有部分验证码这样是无法划分的，必须得聚类，again我偷懒了，因为已经有能用的现成代码，效果虽然差了点，也能过得去了，于是就懒得改了。<br />
`<br />
这部分代码写得很难看，但是能用，就不贴出来丢人现眼了。<br />
`</p>
<h1><span style="color: #808000;">2.规范化</span></h1>
<p>所谓规范化其实就是旋转。为什么要旋转？因为旋转能够极大地减少样本库的个数，提升比对速度。之前有童鞋建议说可以采样不同角度的字母作为样本，可是我上哪找不同角度旋转后的样本？自己旋转经常会旋转得不伦不类的，要是直接采样那得采集多少个样本才能覆盖完整啊，1千个？1万个？想想就不高兴弄了，所以为了让我的生成特征库的工作省点力气，我决定还是规范化一下来得省力。<br />
`<br />
怎样旋转一个字母才算是转正了呢？我用了一个很土的算法：先旋转，再去掉边缘的空白边，然后计算该图形的“密度”（字体部分与整个像素之比），我定义密度最小的那个旋转体就是“规范化”的字母。事实上也可以这样定义：定义上下高度最窄的旋转体为规范化体。这两种定义应该是差不多等效的。<br />
`<br />
规范化代码如下</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">def</span> normalize<span style="color: black;">&#40;</span>im<span style="color: black;">&#41;</span>:
    <span style="color: #808080; font-style: italic;"># divide im</span>
    regions = imdiv<span style="color: black;">&#40;</span>im<span style="color: black;">&#41;</span>  <span style="color: #808080; font-style: italic;"># 这是划分方法1</span>
    <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #008000;">len</span><span style="color: black;">&#40;</span>regions<span style="color: black;">&#41;</span><span style="color: #66cc66;">!</span>=<span style="color: #ff4500;">4</span>:
        regoins = imdiv2<span style="color: black;">&#40;</span>im<span style="color: black;">&#41;</span> <span style="color: #808080; font-style: italic;"># 这是划分方法2</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">for</span> k <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">&#40;</span><span style="color: #008000;">len</span><span style="color: black;">&#40;</span>regions<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>:
        regions<span style="color: black;">&#91;</span>k<span style="color: black;">&#93;</span> = dorotate<span style="color: black;">&#40;</span>regions<span style="color: black;">&#91;</span>k<span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>  <span style="color: #808080; font-style: italic;">#干的就是旋转的事</span>
        regions<span style="color: black;">&#91;</span>k<span style="color: black;">&#93;</span> = purify<span style="color: black;">&#40;</span> docrop<span style="color: black;">&#40;</span>regions<span style="color: black;">&#91;</span>k<span style="color: black;">&#93;</span><span style="color: black;">&#41;</span> <span style="color: black;">&#41;</span>  <span style="color: #808080; font-style: italic;">#切边，然后把涂上预定义颜色</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">return</span> regions</pre></div></div>

<p>`<br />
这个正规化算法看起来没几行，其实干的事情挺复杂的，几乎调用了一遍里面所有的函数，比如getcrop(),docrop()是用来切除白边的；dorotate调用了rotate(),density()等一系列函数，干什么的根据名字就可以推断出来了，因为是比较直观但是却繁琐的“体力劳动”代码，我就不贴出来了。<br />
`<br />
规范化以后效果如下：<br />
<a href="http://69.163.35.68/wp-content/uploads/2010/03/normalized.png"><img class="alignnone size-full wp-image-601" title="normalized" src="http://69.163.35.68/wp-content/uploads/2010/03/normalized.png" alt="normalized" width="100" height="50" /></a><br />
`</p>
<h1><span style="color: #808000;">3.训练样本库</span></h1>
<p>这里所谓的训练样本库，其实就是让程序显示一个规范化后的字母，然后告诉它这应该是啥，让它记住，仅此而已。<br />
`<br />
训练的代码如下：</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">def</span> train<span style="color: black;">&#40;</span>im<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">global</span> samples
    <span style="color: #ff7700;font-weight:bold;">try</span>:
        samples = <span style="color: #dc143c;">pickle</span>.<span style="color: black;">load</span><span style="color: black;">&#40;</span><span style="color: #008000;">open</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'samples.pk'</span>,<span style="color: #483d8b;">'rb'</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">except</span>:
        samples = <span style="color: black;">&#123;</span><span style="color: black;">&#125;</span>
        <span style="color: #dc143c;">pickle</span>.<span style="color: black;">dump</span><span style="color: black;">&#40;</span>samples,<span style="color: #008000;">open</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'samples.pk'</span>,<span style="color: #483d8b;">'wb'</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
&nbsp;
    regions = normalize<span style="color: black;">&#40;</span>im<span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">for</span> region <span style="color: #ff7700;font-weight:bold;">in</span> regions:
        printregion<span style="color: black;">&#40;</span>region<span style="color: black;">&#41;</span>
        smps = loadsamples<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">print</span> match<span style="color: black;">&#40;</span>region,smps<span style="color: black;">&#41;</span>.<span style="color: black;">upper</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">'enter [0-9a-z] to add to library: '</span>
        ans = <span style="color: #008000;">raw_input</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #008000;">len</span><span style="color: black;">&#40;</span>ans<span style="color: black;">&#41;</span> == <span style="color: #ff4500;">1</span>:
            key = <span style="color: black;">&#40;</span>region.<span style="color: black;">size</span>,region.<span style="color: black;">tostring</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
            samples<span style="color: black;">&#91;</span>key<span style="color: black;">&#93;</span> = ans<span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span>
            <span style="color: #dc143c;">pickle</span>.<span style="color: black;">dump</span><span style="color: black;">&#40;</span>samples,<span style="color: #008000;">open</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'samples.pk'</span>,<span style="color: #483d8b;">'wb'</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span></pre></div></div>

<p>`<br />
首先第一部分运用到了python的pickle模块，这是一个类似序列化工具的东东，用来读取/保存样本库，也就是samples.pk文件。<br />
`<br />
其次载入验证码图片，并规范化之，这时候会生成4个规范化字母，程序将会打印出规范化字母的样子，并且打印出目前特征库对他的比对（通过match函数，过会讲）。如果特征比对有错，则输入正确的值，让特征库记住它；否则就很好很强大继续比对下一个值。<br />
`<br />
这里面比较有疑问的就是 smps = loadsamples()， 和 match(region,smps).upper() 了，前一个函数做的事情就是把samples从序列化的strings还原成regions（Image模块的一个图形类别）。后一个函数就是比对函数。<br />
`</p>
<h1><span style="color: #808000;">4.比对</span></h1>
<p>虽然我口口声声说要训练特征库，但是实际上我却没有那么做。因为一般的特征库都会降维，从而不可避免地会影响比对准确性，比如人脸识别的著名的PCA技术就是如此。PCA相比这个东东有个优势就是它的图像是彩色的，距离函数还好定一点；我们这个字体都是单色的，这个比对因为旋转的原因东少一块西缺一块，本来就悬乎了，更别说还要降维了，还嫌不够准么@@<br />
`<br />
其实和前文的判断samecolor类似，比对这个东西，最核心就是“距离函数”，怎样定义字母图形之间的“距离”，使得“距离”最短极为最相似。距离函数搞好了，那么就准了，搞烂了，那么就烂了。<br />
`<br />
我这个特殊的例子中，由于经历了偷懒版去噪算法和偷懒版旋转算法，所得的规范化字母往往东缺一块西少一块，普通的p-范数类的距离函数还真搞不定它，往往把字母都给搞混了。<br />
`<br />
经过调试，我定义了这样的一个距离函数，准确率还算可以：</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">def</span> distance<span style="color: black;">&#40;</span>r1,r2<span style="color: black;">&#41;</span>:
    den1 = density<span style="color: black;">&#40;</span>r1<span style="color: black;">&#41;</span>
    den2 = density<span style="color: black;">&#40;</span>r2<span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff4500;">1.0</span><span style="color: #66cc66;">*</span>den1/den2<span style="color: #66cc66;">&amp;</span>gt<span style="color: #66cc66;">;</span><span style="color: #ff4500;">1</span>:
        <span style="color: black;">&#40;</span>den1,den2<span style="color: black;">&#41;</span> = <span style="color: black;">&#40;</span>den2,den1<span style="color: black;">&#41;</span>
    r1 = r1.<span style="color: black;">resize</span><span style="color: black;">&#40;</span>r2.<span style="color: black;">size</span><span style="color: black;">&#41;</span>
    d1 = r1.<span style="color: black;">getdata</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    d2 = r2.<span style="color: black;">getdata</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    same = <span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span>,<span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span>
    total = <span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span>,<span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span>
    <span style="color: #ff7700;font-weight:bold;">for</span> i <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">xrange</span><span style="color: black;">&#40;</span><span style="color: #008000;">len</span><span style="color: black;">&#40;</span>d1<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">if</span> d1<span style="color: black;">&#91;</span>i<span style="color: black;">&#93;</span> <span style="color: #66cc66;">!</span>= BACKGROUND:
            total<span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span> += <span style="color: #ff4500;">1</span>
            <span style="color: #ff7700;font-weight:bold;">if</span> d1<span style="color: black;">&#91;</span>i<span style="color: black;">&#93;</span> == d2<span style="color: black;">&#91;</span>i<span style="color: black;">&#93;</span>:
                same<span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span> += <span style="color: #ff4500;">1</span>
        <span style="color: #ff7700;font-weight:bold;">if</span> d2<span style="color: black;">&#91;</span>i<span style="color: black;">&#93;</span> <span style="color: #66cc66;">!</span>= BACKGROUND:
            total<span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span> += <span style="color: #ff4500;">1</span>
            <span style="color: #ff7700;font-weight:bold;">if</span> d1<span style="color: black;">&#91;</span>i<span style="color: black;">&#93;</span> == d2<span style="color: black;">&#91;</span>i<span style="color: black;">&#93;</span>:
                same<span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span> += <span style="color: #ff4500;">1</span>
    <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #ff4500;">1</span> - <span style="color: #ff4500;">1.0</span><span style="color: #66cc66;">*</span>same<span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span>/total<span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span> <span style="color: #66cc66;">*</span> <span style="color: #ff4500;">1.0</span><span style="color: #66cc66;">*</span>same<span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span>/total<span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span> <span style="color: #66cc66;">*</span> <span style="color: #ff4500;">1.0</span><span style="color: #66cc66;">*</span>den1/den2</pre></div></div>

<p>`<br />
首先我想计算出相似度，我认为相似度可以一分为三：<br />
1.相似的字母其密度一定相似，所以den1/den2越接近于1越相似<br />
2.对r1来说，r1的点中在r2出现的频率越高越好，same[0]/total[0]越大越相似<br />
3.反之对r2来说，…… same[1]/total[1]越大越相似<br />
以上三点综合考虑，相似度即为以上这三个值相乘，距离即为1-相似度，于是就是上面那个函数。<br />
`<br />
其他辅助函数就不做说明了。<br />
`</p>
<h1><span style="color: #808000;">5.破解</span></h1>
<p>其实到上面那一步，有点技术含量（或者说“技术含量可以有（但我不一定使用了）”）的部分就结束了，留下了一堆函数残渣，还需要一个“方便”的接口，输入验证码，输出4个字母。就是如下</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">def</span> crackcode<span style="color: black;">&#40;</span>im<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">global</span> samples
    regions = normalize<span style="color: black;">&#40;</span>im<span style="color: black;">&#41;</span>
    s = <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span>
    ans = <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span>
    <span style="color: #ff7700;font-weight:bold;">for</span> r <span style="color: #ff7700;font-weight:bold;">in</span> regions:
        s.<span style="color: black;">append</span><span style="color: black;">&#40;</span>match<span style="color: black;">&#40;</span>r,samples<span style="color: black;">&#41;</span>.<span style="color: black;">upper</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
    messup = <span style="color: black;">&#91;</span><span style="color: #483d8b;">'TFY'</span>,<span style="color: #483d8b;">'FE'</span>,<span style="color: #483d8b;">'38'</span>,<span style="color: #483d8b;">'72YT'</span>,<span style="color: #483d8b;">'CQGR6'</span>,<span style="color: #483d8b;">'G6'</span>,<span style="color: #483d8b;">'XK'</span>,<span style="color: #483d8b;">'HK'</span>,<span style="color: #483d8b;">'89B'</span>,<span style="color: #483d8b;">'YV'</span><span style="color: black;">&#93;</span>
    <span style="color: #ff7700;font-weight:bold;">for</span> i <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">&#40;</span><span style="color: #008000;">len</span><span style="color: black;">&#40;</span>s<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">for</span> mess <span style="color: #ff7700;font-weight:bold;">in</span> messup:
            <span style="color: #ff7700;font-weight:bold;">if</span> s<span style="color: black;">&#91;</span>i<span style="color: black;">&#93;</span> == mess<span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span>:
                s<span style="color: black;">&#91;</span>i<span style="color: black;">&#93;</span> = mess
    <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #008000;">len</span><span style="color: black;">&#40;</span>s<span style="color: black;">&#41;</span> <span style="color: #66cc66;">!</span>= <span style="color: #ff4500;">4</span>:
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #483d8b;">'failed'</span>
    <span style="color: #ff7700;font-weight:bold;">else</span>:
        <span style="color: #ff7700;font-weight:bold;">for</span> s1 <span style="color: #ff7700;font-weight:bold;">in</span> s<span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span>:
            <span style="color: #ff7700;font-weight:bold;">for</span> s2 <span style="color: #ff7700;font-weight:bold;">in</span> s<span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span>:
                <span style="color: #ff7700;font-weight:bold;">for</span> s3 <span style="color: #ff7700;font-weight:bold;">in</span> s<span style="color: black;">&#91;</span><span style="color: #ff4500;">2</span><span style="color: black;">&#93;</span>:
                    <span style="color: #ff7700;font-weight:bold;">for</span> s4 <span style="color: #ff7700;font-weight:bold;">in</span> s<span style="color: black;">&#91;</span><span style="color: #ff4500;">3</span><span style="color: black;">&#93;</span>:
                        t = s1+s2+s3+s4
                        ans.<span style="color: black;">append</span><span style="color: black;">&#40;</span>t<span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">return</span> ans</pre></div></div>

<p>这个函数需要说明一下那个messup数组，因为有些顽疾比如程序总是喜欢把8认作3等等，所以列了一个表用来给出辅助的可能答案。<br />
`</p>
<h1><span style="color: #808000;">6.测试</span></h1>
<p>我从新建立的crackdz论坛上面采集了240个验证码，120个用来补充原先80个训练样本，构成总共200个训练样本，120个用来做测试。<br />
`<br />
结果如下：<br />
直接命中：40个<br />
messup数组的补充命中：14个<br />
分块成功但是识别错误：27个<br />
去噪或分块失败： 39个<br />
`<br />
可以看到识别率在33%-45%，还行，可以实战了，下一步就是写个注册机直接把我自己的论坛给淹了，哇哈哈哈哈。<br />
`</p>
<h1><span style="color: #808000;">7.源码</span></h1>
<p>根据惯例附上源码<br />
这次代码要说明一下，为了加速使用了psyco，请安装先psyco或者屏蔽一开始的psyco调用<br />
还有训练的用法是：<br />
python seccode3.py 目录名 #有两个目录，train和train2<br />
识别的用法直接  python seccode3.py 即可，注意要把terminal字体缩放到很小，才能正常显示。<br />
里面还有ttf字体版本的遗留代码，但是应该不能正常工作（？)，无视就好了。<br />
包含了训练库所以有15M之巨的源码：<a href="http://69.163.35.68/old/crackdz/seccode3.tar.gz">seccode3.tar.gz</a></p>
]]></content:encoded>
			<wfw:commentRss>http://69.163.35.68/?feed=rss2&amp;p=597</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>Discuz你太狠了，在下甘拜下风</title>
		<link>http://69.163.35.68/?p=593</link>
		<comments>http://69.163.35.68/?p=593#comments</comments>
		<pubDate>Tue, 09 Mar 2010 02:56:04 +0000</pubDate>
		<dc:creator>observer</dc:creator>
				<category><![CDATA[Discuz]]></category>
		<category><![CDATA[验证码]]></category>

		<guid isPermaLink="false">http://69.163.35.68/old/?p=593</guid>
		<description><![CDATA[昨天正做着Discuz验证码的在线破解，测试到一半突然发现全错了，非常沮丧之际猛然发现是因为Discuz的验证码改成中文的了，不知道是不是我最近更新验证码破解的文章的缘故，如果是的话那也太荣幸了@@ ` 不过其实我觉得中文的验证码难度其实并没有提高太多，因为都是方块字了，旋转定位要容易得多了。我之前最头痛的还是英文字体很多，宽瘦不一，很多字母还是圆形的很难转正了比较。转不正的话很多判断都没法做，或者说做起来比较困难，而且英文比如CG6等等其实很像，旋转的不太好就会造成误判很多，中文的话当然也有这种情况，但是相对来说感觉好搞一点。 ` 说到底，你不扭曲的话验证码效果不会很好的，也就是再多花点时间破解一下中文验证码而已。]]></description>
			<content:encoded><![CDATA[<p>昨天正做着Discuz验证码的在线破解，测试到一半突然发现全错了，非常沮丧之际猛然发现是因为Discuz的验证码改成中文的了，不知道是不是我最近更新验证码破解的文章的缘故，如果是的话那也太荣幸了@@<br />
`<br />
不过其实我觉得中文的验证码难度其实并没有提高太多，因为都是方块字了，旋转定位要容易得多了。我之前最头痛的还是英文字体很多，宽瘦不一，很多字母还是圆形的很难转正了比较。转不正的话很多判断都没法做，或者说做起来比较困难，而且英文比如CG6等等其实很像，旋转的不太好就会造成误判很多，中文的话当然也有这种情况，但是相对来说感觉好搞一点。<br />
`<br />
说到底，你不扭曲的话验证码效果不会很好的，也就是再多花点时间破解一下中文验证码而已。</p>
]]></content:encoded>
			<wfw:commentRss>http://69.163.35.68/?feed=rss2&amp;p=593</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>python验证码识别之Discuz(二)</title>
		<link>http://69.163.35.68/?p=572</link>
		<comments>http://69.163.35.68/?p=572#comments</comments>
		<pubDate>Sat, 06 Mar 2010 23:11:28 +0000</pubDate>
		<dc:creator>observer</dc:creator>
				<category><![CDATA[Discuz]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[编程]]></category>
		<category><![CDATA[验证码]]></category>

		<guid isPermaLink="false">http://69.163.35.68/old/?p=572</guid>
		<description><![CDATA[承接上文，上文写道去噪，本文应该接着从切块开始写，然后到比对的。然则上文写完之后开始收集训练样本，把样本用去噪算法一做，惨不忍睹；再加上上文之后有很多人留言提了不少意见，所以有了新的方向。 ` 首先骨头兄提议说可以用tesseract来做OCR，这个确实是一个很好的建议。很多验证码其实很简单，根本用不到高级的东东，直接用tesseract就可以摆平。 ` 然而经过实验，效果不尽如人意，如图： ` 还有关于选取多角度多采样的建议，后来发现会导致样本容量太大，性能受到影响，所以也作出了一定的调整，目前来说验证码识别的部分算是写完了，绝对识别率在10%左右，如果可接受其他候选结果的话，识别率在50%左右，不能识别的主要因素是去噪效果不理想和没有成功切块。 ` 最后还有关于Discuz验证码使用反色阴影的建议，虽然可能可以大幅提高去噪成功率，但是因为不够通用被我舍弃了。 ` 我不喜欢直接写结论，我会把我的探索过程和绕圈子过程一起写出来，因为这样看了才有收获，否则也就是&#8221;又一个注册码破解器&#8221;这种东东而已，能写出来多少就写多少吧。 重新去噪 基于距离的去噪方式，后来可耻地失败了，所以兜兜转转绕回了色板方案：也就是说把相同颜色的色块挑出来，然后挑出最像验证码的色块。这里我绕了第一个圈子。 ` 先稍微讲解一下gif的文件格式，众所周知gif可以有256种颜色，可是这256种颜色却是可以随便在RGB24位色中选择的，也就是说用得好的话其实gif效果和RGB效果也差不了太多。所以GIF是有调色板这么个概念的。 ` 调色板有个问题就是不同的调色板可以存放同一种颜色。这样你就要判断哪两个调色板的颜色是相同的，然后再进行去噪。因为任意一个调色板都有可能存放字体颜色，而验证码色块颜色是非常随机的，可是去噪必须得设置背景色块，这样设置背景色块的调色板号成了一个难题。 ` 这个难题后来被我用Image.convert()绕过去了，我把GIF格式转为了RGB模式，然后把纯白色(255,255,255)作为背景，一下子就解决了这个问题。 ` 第二个问题是去背景，因为背景是固定的，所以可以通过gif图片不同帧的比较来挑出相同帧，一般即为背景。 ` 如何判断颜色相同是另一个问题，因为视觉上的相同和RGB位色的相同是不一样的，比如白色可以是(255,255,255)，然则你用(254,254,254)显示的也还是几乎白色，肉眼很难看出区别。 ` 我用了这么个方法：把颜色&#8221;矩阵&#8221;的1-norm（1-范数）值和一个预定义阈值做比较，在阈值范围内我就认为是相同的颜色。当然也可以取其他范数比如2-范数，我不知道人眼是怎么区分位色的，随便挑了个范数，觉得效果还行。 ` 到此，去噪的第一部分完成了。 #选取验证码帧和背景帧 im = Image.open&#40;fname&#41; frame = None bkframe = &#91;&#93; for i in &#8230; <a href="http://69.163.35.68/?p=572">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>承接上文，上文写道去噪，本文应该接着从切块开始写，然后到比对的。然则上文写完之后开始收集训练样本，把样本用去噪算法一做，惨不忍睹；再加上上文之后有很多人留言提了不少意见，所以有了新的方向。<br />
`<br />
首先骨头兄提议说可以<a href="http://li2z.cn/2009/12/29/tesseract-ocr/">用tesseract来做OCR</a>，这个确实是一个很好的建议。很多验证码其实很简单，根本用不到高级的东东，直接用tesseract就可以摆平。<br />
`<br />
然而经过实验，效果不尽如人意，如图：<a href="http://69.163.35.68/wp-content/uploads/2010/03/result.png"><img class="alignright size-full wp-image-573" title="ocr-result" src="http://69.163.35.68/wp-content/uploads/2010/03/result.png" alt="ocr-result" width="230" height="170" /></a><br />
`<br />
还有关于选取多角度多采样的建议，后来发现会导致样本容量太大，性能受到影响，所以也作出了一定的调整，目前来说验证码识别的部分算是写完了，<strong>绝对识别率在10%左右</strong>，如果可接受其他候选结果的话，<strong>识别率在50%左右</strong>，不能识别的主要因素是<strong>去噪效果不理想</strong>和<strong>没有成功切块</strong>。<br />
`<br />
最后还有关于Discuz验证码使用反色阴影的建议，虽然可能可以大幅提高去噪成功率，但是因为不够通用被我舍弃了。<br />
`<br />
我不喜欢直接写结论，<strong>我会把我的探索过程和绕圈子过程一起写出来</strong>，因为这样看了才有收获，否则也就是&#8221;又一个注册码破解器&#8221;这种东东而已，能写出来多少就写多少吧。<br />
<span id="more-572"></span></p>
<h1><span style="color: #00ff00;"> 重新去噪</span></h1>
<p>基于距离的去噪方式，后来可耻地失败了，所以兜兜转转绕回了色板方案：也就是说把相同颜色的色块挑出来，然后挑出最像验证码的色块。这里<span style="color: #0000ff;">我绕了第一个圈子。</span><br />
`<br />
先稍微讲解一下gif的文件格式，众所周知gif可以有256种颜色，可是这256种颜色却是可以随便在RGB24位色中选择的，也就是说用得好的话其实gif效果和RGB效果也差不了太多。所以GIF是有调色板这么个概念的。<br />
`<br />
调色板有个问题就是不同的调色板可以存放同一种颜色。这样你就要判断哪两个调色板的颜色是相同的，然后再进行去噪。因为任意一个调色板都有可能存放字体颜色，而验证码色块颜色是非常随机的，可是去噪必须得设置背景色块，这样设置背景色块的调色板号成了一个难题。<br />
`<br />
这个难题后来被我用Image.convert()绕过去了，我把GIF格式转为了RGB模式，然后把纯白色(255,255,255)作为背景，一下子就解决了这个问题。<br />
`<br />
<span style="color: #0000ff;">第二个问题是去背景</span>，因为背景是固定的，所以可以通过gif图片不同帧的比较来挑出相同帧，一般即为背景。<br />
`<br />
<span style="color: #0000ff;">如何判断颜色相同</span>是另一个问题，因为视觉上的相同和RGB位色的相同是不一样的，比如白色可以是(255,255,255)，然则你用(254,254,254)显示的也还是几乎白色，肉眼很难看出区别。<br />
`<br />
我用了这么个方法：把颜色&#8221;矩阵&#8221;的<a href="http://zh.wikipedia.org/zh-cn/%E7%9F%A9%E9%98%B5%E8%8C%83%E6%95%B0">1-norm（1-范数）</a>值和一个预定义阈值做比较，在阈值范围内我就认为是相同的颜色。当然也可以取其他范数比如2-范数，我不知道人眼是怎么区分位色的，随便挑了个范数，觉得效果还行。<br />
`<br />
到此，去噪的第一部分完成了。</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">    <span style="color: #808080; font-style: italic;">#选取验证码帧和背景帧</span>
    im = Image.<span style="color: #008000;">open</span><span style="color: black;">&#40;</span>fname<span style="color: black;">&#41;</span>
    frame = <span style="color: #008000;">None</span>
    bkframe = <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span>
    <span style="color: #ff7700;font-weight:bold;">for</span> i <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">10</span><span style="color: black;">&#41;</span>:
        im.<span style="color: black;">seek</span><span style="color: black;">&#40;</span>i<span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">if</span> im.<span style="color: black;">info</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">'duration'</span><span style="color: black;">&#93;</span><span style="color: #66cc66;">&amp;</span>gt<span style="color: #66cc66;">;</span>DURATION:
            im = im.<span style="color: black;">convert</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'RGB'</span><span style="color: black;">&#41;</span>
            frame = im.<span style="color: black;">load</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
            <span style="color: #ff7700;font-weight:bold;">break</span>
        <span style="color: #ff7700;font-weight:bold;">else</span>:
            bkframe.<span style="color: black;">append</span><span style="color: black;">&#40;</span>im.<span style="color: black;">convert</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'RGB'</span><span style="color: black;">&#41;</span>.<span style="color: black;">load</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
    <span style="color: #808080; font-style: italic;">#背景比较，去噪</span>
    <span style="color: #ff7700;font-weight:bold;">for</span> k <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">&#40;</span><span style="color: #008000;">len</span><span style="color: black;">&#40;</span>bkframe<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">for</span> i <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">xrange</span><span style="color: black;">&#40;</span>WIDTH<span style="color: black;">&#41;</span>:
            <span style="color: #ff7700;font-weight:bold;">for</span> j <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">xrange</span><span style="color: black;">&#40;</span>HEIGHT<span style="color: black;">&#41;</span>:
                <span style="color: #ff7700;font-weight:bold;">if</span> samecolor<span style="color: black;">&#40;</span> bkframe<span style="color: black;">&#91;</span>k<span style="color: black;">&#93;</span><span style="color: black;">&#91;</span>i,j<span style="color: black;">&#93;</span> , frame<span style="color: black;">&#91;</span>i,j<span style="color: black;">&#93;</span> <span style="color: black;">&#41;</span>:
                    frame<span style="color: black;">&#91;</span>i,j<span style="color: black;">&#93;</span> = BACKGROUND</pre></div></div>

<p>然后是samecolor函数判断是否同色</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">def</span> samecolor<span style="color: black;">&#40;</span>rgb1,rgb2<span style="color: black;">&#41;</span>:
    diff = <span style="color: #ff4500;">0</span>
    <span style="color: #ff7700;font-weight:bold;">for</span> i <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">3</span><span style="color: black;">&#41;</span>:
        diff += <span style="color: #008000;">abs</span><span style="color: black;">&#40;</span>rgb1<span style="color: black;">&#91;</span>i<span style="color: black;">&#93;</span>-rgb2<span style="color: black;">&#91;</span>i<span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">if</span> diff <span style="color: #66cc66;">&amp;</span>lt<span style="color: #66cc66;">;</span> COLORDIFF:
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">True</span>
    <span style="color: #ff7700;font-weight:bold;">else</span>:
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">False</span></pre></div></div>

<p>`<br />
这一步过后，图形去噪如下：<br />
<a href="http://69.163.35.68/wp-content/uploads/2010/03/denoise1-2.png"><img class="size-full wp-image-579" title="去噪第一步" src="http://69.163.35.68/wp-content/uploads/2010/03/denoise1-2.png" alt="去噪第一步" width="100" height="50" /></a><br />
`<br />
可以看到大部分背景已经去掉了，剩下的都是些线条什么的。<br />
`<br />
<span style="color: #0000ff;">接下来就要用到聚类算法</span>了，这应该是模式识别里面的范畴，著名算法有k-means和其衍生品<a href="http://en.wikipedia.org/wiki/Cluster_analysis#QT_clustering_algorithm">QT算法</a>等，k-means要求预定义聚类个数，扔了；QT算法实现起来很复杂，我懒得实现，更懒得去研究那些<a href="http://en.wikipedia.org/wiki/Locality-sensitive_hashing">locality sensitive hashing</a>和什么<a href="http://en.wikipedia.org/wiki/Formal_concept_analysis">Formal concept analysis</a>了。<br />
`<br />
因为偷懒，这里我用了最土鳖的聚类算法，直接用samecolor扫一遍，一样的标记为同组，不一样的就跳过。这个土鳖算法有个问题就是它会选定某些点作为中心点，所有和这些点的1-范数距离小于阈值（也就是符合samecolor条件）的点都当作同色，而显然聚类算法的中心点不一定会是样本点，万一这个算法不小心选了个边缘点，聚类就直接聚错了，会比较悲剧。这里可能也是导致最后去噪效果仍不尽如人意的原因之一吧。MARK之，有时间以后改进。<br />
`<br />
聚类并且把颜色最多的色块挑出来，代码如下：</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">    <span style="color: #808080; font-style: italic;"># denoize2: group similar color</span>
    cgrp = <span style="color: black;">&#123;</span><span style="color: black;">&#125;</span>
    <span style="color: #ff7700;font-weight:bold;">for</span> k <span style="color: #ff7700;font-weight:bold;">in</span> d.<span style="color: black;">keys</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
        cgrp<span style="color: black;">&#91;</span>k<span style="color: black;">&#93;</span> = k
    <span style="color: #ff7700;font-weight:bold;">for</span> k1 <span style="color: #ff7700;font-weight:bold;">in</span> cgrp.<span style="color: black;">keys</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">for</span> k2 <span style="color: #ff7700;font-weight:bold;">in</span> cgrp.<span style="color: black;">keys</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
            <span style="color: #ff7700;font-weight:bold;">if</span> k1 <span style="color: #66cc66;">!</span>= k2 <span style="color: #ff7700;font-weight:bold;">and</span> cgrp<span style="color: black;">&#91;</span>k1<span style="color: black;">&#93;</span> == k1 <span style="color: #ff7700;font-weight:bold;">and</span> samecolor<span style="color: black;">&#40;</span>k1,k2<span style="color: black;">&#41;</span>:
                cgrp<span style="color: black;">&#91;</span>k2<span style="color: black;">&#93;</span>=k1
    d = <span style="color: black;">&#123;</span><span style="color: black;">&#125;</span>
    <span style="color: #ff7700;font-weight:bold;">for</span> i <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">xrange</span><span style="color: black;">&#40;</span>WIDTH<span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">for</span> j <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">xrange</span><span style="color: black;">&#40;</span>HEIGHT<span style="color: black;">&#41;</span>:
            k = frame<span style="color: black;">&#91;</span>i,j<span style="color: black;">&#93;</span>
            k = cgrp<span style="color: black;">&#91;</span>k<span style="color: black;">&#93;</span>  <span style="color: #808080; font-style: italic;"># 选取聚类色，单独类就是本身</span>
            <span style="color: #ff7700;font-weight:bold;">if</span> d.<span style="color: black;">has_key</span><span style="color: black;">&#40;</span>k<span style="color: black;">&#41;</span>:
                d<span style="color: black;">&#91;</span>k<span style="color: black;">&#93;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span> += <span style="color: #ff4500;">1</span>
                d<span style="color: black;">&#91;</span>k<span style="color: black;">&#93;</span>.<span style="color: black;">append</span><span style="color: black;">&#40;</span><span style="color: black;">&#40;</span>i,j<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
            <span style="color: #ff7700;font-weight:bold;">else</span>:
                d<span style="color: black;">&#91;</span>k<span style="color: black;">&#93;</span> = <span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span>,<span style="color: black;">&#40;</span>i,j<span style="color: black;">&#41;</span><span style="color: black;">&#93;</span>
    topd = <span style="color: #008000;">sorted</span><span style="color: black;">&#40;</span>d.<span style="color: black;">items</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>,<span style="color: #008000;">cmp</span>=<span style="color: #ff7700;font-weight:bold;">lambda</span> x,y:<span style="color: #008000;">cmp</span><span style="color: black;">&#40;</span>y<span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span>,x<span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span></pre></div></div>

<p>`<br />
有些噪音点数也很多，很难通过色块所包含点的数量来判断是否是噪音，还得<span style="color: #0000ff;">根据其聚类性来做辅助判断来进一步去噪。</span><br />
`<br />
这个函数也很难写，我写的不太好，最后效果也就不太好，有些圆弧形噪音和正常字符特征相似，唯一不一样的就是“粗细”较细，可是如何判断“粗细”也是个伤脑筋的问题，因为色块中还有噪音的存在……总之目前这个版本大概能有60-70%的去噪成功率，可以去噪出人眼能识别的验证码，我就凑活着继续用下去了。<br />
`</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">def</span> isnoise<span style="color: black;">&#40;</span>points<span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">''</span><span style="color: #483d8b;">'Determine if the points are noisy points'</span><span style="color: #483d8b;">''</span>
    <span style="color: #808080; font-style: italic;">#如果点的个数小于10，过滤之</span>
    <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #008000;">len</span><span style="color: black;">&#40;</span>points<span style="color: black;">&#41;</span><span style="color: #66cc66;">&amp;</span>lt<span style="color: #66cc66;">;</span><span style="color: #ff4500;">10</span>:         <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">False</span>     <span style="color: #808080; font-style: italic;"># center point     left = 255     right = 0     upper = 255     lower = 0     center = [0,0]     for point in points:         center[0]+=point[0]         center[1]+=point[1]     center[0]/=len(points)     center[1]/=len(points)     distance = 0     farpoints = 0     for point in points:         dd = (point[0]-center[0])*(point[0]-center[0])+(point[1]-center[1])*(point[1]-center[1])         distance += dd         if dd &amp;gt; DIAMETER*DIAMETER:</span>
            farpoints += <span style="color: #ff4500;">1</span>
        <span style="color: #ff7700;font-weight:bold;">if</span> point<span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span>right:
            right = point<span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span>
        <span style="color: #ff7700;font-weight:bold;">if</span> point<span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span>lower:
            lower = point<span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span>
    <span style="color: #808080; font-style: italic;">#算区域面积</span>
    area = <span style="color: #008000;">abs</span><span style="color: black;">&#40;</span>left-right<span style="color: black;">&#41;</span><span style="color: #66cc66;">*</span><span style="color: #008000;">abs</span><span style="color: black;">&#40;</span>upper-lower<span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;">#如果有5个以上离中心太远的点，并且点在其所在区域的密度小于0.2，则为噪音</span>
    <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: black;">&#40;</span>farpoints<span style="color: #66cc66;">&amp;</span>gt<span style="color: #66cc66;">;</span><span style="color: #ff4500;">5</span> <span style="color: #ff7700;font-weight:bold;">and</span> <span style="color: black;">&#40;</span>area==<span style="color: #ff4500;">0</span> <span style="color: #ff7700;font-weight:bold;">or</span> <span style="color: #ff4500;">1.0</span><span style="color: #66cc66;">*</span><span style="color: #008000;">len</span><span style="color: black;">&#40;</span>points<span style="color: black;">&#41;</span>/area<span style="color: #66cc66;">&amp;</span>lt<span style="color: #66cc66;">;</span><span style="color: #ff4500;">0.2</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">True</span>
    <span style="color: #ff7700;font-weight:bold;">else</span>:
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">False</span></pre></div></div>

<p>`<br />
接下来没有多少步骤了，去噪和去独立噪音点</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">    found = <span style="color: #ff4500;">0</span>
    <span style="color: #ff7700;font-weight:bold;">for</span> <span style="color: black;">&#40;</span>k,v<span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">in</span> topd<span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span>:<span style="color: black;">&#93;</span>:
        <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> isnoise<span style="color: black;">&#40;</span>v<span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span>:<span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>:
            <span style="color: #ff7700;font-weight:bold;">for</span> point <span style="color: #ff7700;font-weight:bold;">in</span> v<span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span>:<span style="color: black;">&#93;</span>:
                frame<span style="color: black;">&#91;</span>point<span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span>,point<span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span><span style="color: black;">&#93;</span>=k
            found += <span style="color: #ff4500;">1</span>
        <span style="color: #ff7700;font-weight:bold;">if</span> found == <span style="color: #ff4500;">4</span>:
            <span style="color: #ff7700;font-weight:bold;">break</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># denoize again</span>
    <span style="color: #ff7700;font-weight:bold;">for</span> i <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">xrange</span><span style="color: black;">&#40;</span>WIDTH<span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">for</span> j <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">xrange</span><span style="color: black;">&#40;</span>HEIGHT<span style="color: black;">&#41;</span>:
            <span style="color: #ff7700;font-weight:bold;">if</span> frame<span style="color: black;">&#91;</span>i,j<span style="color: black;">&#93;</span> <span style="color: #66cc66;">!</span>= BACKGROUND:
                count = <span style="color: #ff4500;">0</span>
                <span style="color: #ff7700;font-weight:bold;">for</span> m <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">xrange</span><span style="color: black;">&#40;</span> <span style="color: #008000;">max</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">0</span>,i-<span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span>, <span style="color: #008000;">min</span><span style="color: black;">&#40;</span>i+<span style="color: #ff4500;">2</span>,WIDTH<span style="color: black;">&#41;</span> <span style="color: black;">&#41;</span>:
                    <span style="color: #ff7700;font-weight:bold;">for</span> n <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">xrange</span><span style="color: black;">&#40;</span> <span style="color: #008000;">max</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">0</span>,j-<span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span>, <span style="color: #008000;">min</span><span style="color: black;">&#40;</span>j+<span style="color: #ff4500;">2</span>,HEIGHT<span style="color: black;">&#41;</span> <span style="color: black;">&#41;</span>:
                        <span style="color: #ff7700;font-weight:bold;">if</span> frame<span style="color: black;">&#91;</span>m,n<span style="color: black;">&#93;</span> <span style="color: #66cc66;">!</span>= BACKGROUND:
                            count += <span style="color: #ff4500;">1</span>
                <span style="color: #ff7700;font-weight:bold;">if</span> count <span style="color: #66cc66;">&amp;</span>lt<span style="color: #66cc66;">;</span>= <span style="color: #ff4500;">1</span>:
                    frame<span style="color: black;">&#91;</span>i,j<span style="color: black;">&#93;</span> = BACKGROUND</pre></div></div>

<p>`<br />
大功告成：<br />
<a href="http://69.163.35.68/wp-content/uploads/2010/03/denoise2.png"><img class="alignnone size-full wp-image-582" title="去噪" src="http://69.163.35.68/wp-content/uploads/2010/03/denoise2.png" alt="去噪" width="100" height="50" /></a><br />
`<br />
这个算法到这里成功率已经大概有60-70%，（基于我的样本库：80个discuz随机生成的验证码）。今天先写到这里吧，后面的代码到验证码破解部分都已经完成了，<strong>贴文速度就取决于我的码字速度了</strong>，因为开学了所以也别太抱期望就是了。<br />
`<br />
按照惯例提供源码打包下载：<br />
<a href='http://69.163.35.68/wp-content/uploads/2010/03/seccode2.tar.gz'>seccode2.tar.gz</a></p>
]]></content:encoded>
			<wfw:commentRss>http://69.163.35.68/?feed=rss2&amp;p=572</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
	</channel>
</rss>
