Showing posts with label Python. Show all posts
Showing posts with label Python. Show all posts

Wednesday, March 16, 2011

Introduction to Inspector

有时我们想要监视某个网页是否有更新, 但网站又没有提供RSS, 这时就只能定期去查看那个网页, 偶尔也会因此忘记. 这种枯燥、乏味的重复行为让人厌倦, 于是我实现了一个简单的小项目, 我把它叫做 Inspector. 输入你想要监视的网址, 选取你感兴趣的网页元素, 然后Inspector就会在有更新时通过Email通知你. It’s simple, huh?

下面是一些使用截图, 首先是选取网页元素:

Inspect

然后是Inspector发给你的通知邮件 (最好把邮件地址先加到联系人里, 一般来说第一次都会被判为垃圾邮件, 至少Gmail是这样):

Inspector Notification

网站只在Firefox、Chrome和Safari中测试过, 访问可能需要翻墙 (由于某种众所周知的原因, 你懂的). 目前项目代码已经托管在GitHub: https://github.com/xiaogaozi/inspector, 如果你有任何问题或者建议可以到 这个页面 提出.

接下来是一些实现这个项目过程中的技术吐槽, 不感兴趣的同学可以直奔 项目主站 围观.

选取网页元素这点是借鉴的Firebug, 同时借鉴的还有它的代码. 在使用JS绑定事件时遇到了 this 关键字引用错误问题, 最后在 “JavaScript: The Definitive Guide” 中找到了答案, 具体可参考书中17.1.5小节. 呵呵, JS基础没学好. “/inspect” 页面最下面的提示要感谢兰姐提醒用 iframe, 对于固定样式很有好处. 修正网页CSS、JS链接这个需要判断是否为绝对地址, 常规的也就是http或者https打头, 不过 The World Clock 网页的CSS引用很奇怪, 是 “//” 打头, 测试大猫的博客时也让我发现一个类似的bug. 像Facebook这样的通过代码动态加载CSS的也没办法, 还有如果是用 frameset 写的页面现在还暂时不支持.

页面抓取现在对于静态页面效果要好一点. 定位网页元素现在比较常用的有CSS selector和XPath, 前者我更熟一点, 不过Python标准库不支持, libxml2可以, 但它所需要的so库不能上传到GAE, 只能作罢, 而且它只能分析那些标准的XML, 遇到稍微不标准的就会出错, 这点和标准库一个德行. 但互联网上充斥着各种不标准, 这由不得开发者挑剔, 要对付它们, Beautiful Soup 是一个不错的选择. 但问题又来了, BS即不支持CSS selector, 也不支持XPath. 后来我找到一个日本人写的 BS扩展, 可以用上XPath. 至于CSS selector, 我也找到 一份Python实现, 需要结合Tidy来处理那些非标准页面, 不过我没试过.

能这么快完成, 也得益于GAE的便捷 (虽然前前后后花了有半个来月). 用户验证、Email服务、数据存储这些都能通过很简单的API搞定, 不过自带的Django模板引擎有点弱智, 一些高级语法支持不了.

页面风格抄的GitHub, 按钮是GitHub和Twitter的结合, 表格抄的GAE, About页面用AsciiDoc写的, 看起来还算凑合. 第一次用Closure Compiler, 第一次用CSSTidy, 压缩了确实不错.

最后, 如果你坚持看到这里的话, 感谢你的支持.

Saturday, November 21, 2009

让IPython支持清屏

IPython是一个增强型的Python Shell, 比Python自带的那个功能更强大. 在普通Python Shell里可以通过"Ctrl+l"来清屏, 可在IPython里这个快捷键就不起作用了. 为了达到这个目的, 我们需要对IPython进行一点设置.

这篇文章中我找到了方法, 需要修改"~/.ipython/ipythonrc"文件, 可现在IPython更推荐通过修改"~/.ipython/ipy_user_conf.py"文件来设置IPython. 打开"ipy_user_conf.py"应该可以看到一些示例, 我们需要做的是调用"parse_and_bind()"函数来增加新的键绑定. 在"main()"函数中添加以下语句, 注意你的配置文件中可能已经包含了部分语句, 只需要去掉注释符号就行了.
def main():   
    ...

    import readline
    readline.parse_and_bind('\C-l: clear-screen')
不过这样还不算完成, 需要检查一下"ipythonrc"文件中是否有相互冲突的配置, 比如在我的文件里包含下面一行:
readline_parse_and_bind "\C-l": possible-completions
注释掉即可, 现在进入IPython去试试吧.

Sunday, November 1, 2009

有趣的数学: Collatz conjecture

今天在TL上看到的讨论, 知道了这么一个猜想, 小研究了一下.

Collatz conjecture (考拉兹猜想) 又叫Kakutani's Problem (角谷猜想), 3n + 1 conjecture (3n + 1猜想). 最早由Lothar Collatz在1937年提出, 因此而得名. Collatz conjecture的主要内容是给一个大于0的数n, 如果n是偶数, 则除以2, 否则乘上3再加1, 也就是3n + 1, 如此循环反复, 最终都能等于1. 用Python代码表示如下:
def CollatzConjecture(n):
    """Collatz Conjecture"""
    t = 0
    while n != 1:
        t += 1
        if n % 2 == 0:
            n /= 2
        else:
            n = n * 3 + 1
    return t

2006年, 这个猜想被证明为recursively undecidable (递归不可判定). 下面是将2~9999代入Collatz conjecture得到的最大步骤的图形:

Collatz conjecture

看着还是有那么点规律的, 不知道什么时候才能被证明出来.

Tuesday, October 27, 2009

wxPython & py2exe & Python多线程编程

这两天一直在帮别人弄个小程序, 一个用于辩论赛的计时器. 以前有人做过一个, 但现在规则改了, 估计写程序那人也找不到了, 就打算重新做一个. 开始看了一下程序, 觉得功能挺简单的, 就答应了下来, 事后我还很纳闷地跟小兰同学说我怎么就把这么一个活给接下来了, 照我以前的思维, 这种Window$下的程序我应该直接推掉才是.

不过也管不了那么多了, 既然答应了下来, 就得按照我的风格完成. 首先是GUI库的选择, 他们想要的是那种不用安装就能直接运行的轻量级程序. 这样就最先把C#给淘汰了, 那庞大的.NET Framework是肯定不行的. 其实做这种Win下的小型程序最好的选择就是VC++了, Window$系统对于MFC良好的支持, 使得程序编好之后就可以到处运行. 不过MFC自从大二上学期学过之后, 我就再也没有碰过, 况且我的机器上连VS都没有... 我最想用的其实是Python的GUI库, 主要是我最近也在学这东西, 可以练练手. 如果不用微软的GUI库, 那剩下的选择就主要有wxWidgetsGTK+QtTk这四种了, 它们都是跨平台的, 并且都提供有Python的实现接口. 其中wxWidgets不同于其它三种, 它编写出来的程序具有系统的原生界面 (look and feel native), 而其它的则是在所有系统下都一个样. 于是我选定了wxWidgets作为GUI库, 对应的Python实现是wxPython. 但对于Python这种解释型语言来说最大的限制就在于目标机器上必须也有对应的解释器才行, 这样就跟C#一样了. 不过通过我在网上的进一步了解, 发现了一个叫做py2exe的东西, 它的目的主要就是将你的Python程序打包, 使得在没有安装Python的机器上也能运行. 这不正是我想要的结果吗?! 有了上面的准备, 更让我确定了要用Python去开发的决心.

学习wxPython可以从"How to Learn wxPython"开始, 同时wxPyWiki也有一定的参考价值. 然后就是不断地翻API了, wxPython官方提供的那个Python接口的API我觉得基本没有用处, 虽然全部都是Python的代码, 但由于对于接口的参数没有一点解释, 导致你看了也是白看, 只是知道了这个方法有什么参数而已. 还是看wxWidgets的文档好点, 即使里面是以C++作为实例, 其实很容易转换到Python的. wxWidgets也有类似于GTK+中的Glade的可视化界面绘制工具, 比如wxGlade (这个很明显是模仿的Glade), wxDesigner (这是收费的软件).

在编写过程中还遇到一个小意外, 因为我一直认为这个程序技术上没有什么难点, 只是图形界面我是第一次接触, 可能要花些时间. 结果在第一天就发现这个程序非得用多线程不可... 并且是至少3个线程一起跑. 这就麻烦了, 我根本就没有学过Python的多线程. 怀着搜一搜就会有结果的心态, 找到了Python的threading模块. 以前觉得Python官方的文档挺不错的, 每一个模块写得清清楚楚, 中间还穿插一些示例代码, 让人一下就知道该怎么用了. 但似乎这个threading是个例外, 不仅文字解释模模糊糊, 唯一的几小段示例代码也让人看不懂. 看来这次是碰到硬骨头了, 没办法, 当初自信满满地答应别人一定能在期限之内做完, 硬骨头也得使劲啃. 通常是通过继承threading模块的Thread类来构造线程, 同时你可能还需要重载基类的run()方法. 这些都还比较容易理解, 但我还需要让一个线程中途暂停下来, 等到主进程来激活它. 这个"暂停线程"就把我搞得有点恼火了, 开始一直不能理解为什么其中提供的wait()方法是不带参数的, 没有参数怎么知道该让哪个线程等待呢? 在看过几个例子和自己试验了几次之后, 终于发现wait()是从属于每一个Thread对象的, 看来面向对象的思想还是没有学好... 于是要让一个线程暂停就变得异常简单了:
import threading

class MyThread(threading.Thread):
    ...

    def run(self):
        ...
        self.event = threading.Event()
        self.event.wait()
这里使用的是Event类中的wait()方法. 激活的操作在主进程中进行:
self.thread.event.set()
但Python中不提供彻底终止线程的操作, 每一个线程必须正常执行完之后才能退出. 可以通过设置Thread对象的daemon属性值为True, 使得子线程可以在主进程退出的时候一同结束. 不过这样还是不够"干净", 在主线程没有结束之前, 那些子线程是一直都在运行的, 虽然表面上看不出来, 会白白浪费系统的资源. 于是可以采用让子线程在某种条件下自己结束自己, 具体内容请参考: Python 不支持杀死子线程.

程序写好之后就还剩下打包, 最开始我其实不是用的py2exe, 而是Pyinstaller. 原因在于Pyinstaller可以只生成一个exe文件, 这样程序看起来就会很简洁了. 但用Pyinstaller试过几次之后程序都莫名其妙地无法运行, 只好转战py2exe. 相比于Pyinstaller, py2exe提供了更灵活的配置机制, 但最后生成的文件夹里会包含很多dll和pyd文件, 看着稍微有点乱. 使用py2exe主要是setup.py的编写, 调用的是distutils.core标准库中的setup()函数. setup()函数的参数可以在这里查到, 同时py2exe也附加了一些参数, 其它信息可以参考General Tips and Tricks, 比如怎样自定义程序的图标. 不过故事总会出现波折, 就像公主虽然肯定是和王子在一起, 但也不排除那个王子可能一辈子都是青蛙. 我用py2exe生成的exe文件在没有安装Python的机器上再次出现错误, 在看了"尽量别使用 Py2exe for Python 2.6"这篇文章之后, 才知道是Python 2.6在作怪. 没办法, 为了生成一个通用的exe程序, 只有再去下载Python 2.5, 之后生成的程序就一切OK了, 同时记得把一些py2exe没有复制的dll文件拷过来.

经过这次体验, 深切体会到Window$下的快速GUI开发, 还得靠VS, 拖一拖就都搞定了, 怎一个爽字了得. py2exe虽然也很方便, 但还是尽量在机器上装上Python好点, 毕竟py2exe也不是万能的, 也许会发生某些无法预期的错误. 这个小程序的源代码已经放到了我的Google Code上.

P.S. 在下载Python 2.5的时候, 发现Python官方下载点已经被墙, 只好去其它地方寻觅, #fuckgfw

Saturday, September 26, 2009

批量获取Twitter好友feed的小程序

2010.1.19更新:
现在不推荐使用这种方法来获取Twitter好友的feed, 因为经过试验当好友数达到一定数目时, 获取的feed列表是不完全的.

曾经介绍过怎样通过Google Reader备份和搜索好友的Tweets, 其中关键的一步就是获取所有Twitter好友的feed, 这样就能通过Google Reader订阅了. 今天抽了点时间把以前那个code2string.py稍微改进了下, 现在只需要告诉程序你的Twitter用户名, 它就会自动完成获取feed和转换中文的工作了. 请猛击这里下载fetch_twitter_feed.py, 使用方法如下:
$ python fetch_twitter_feed.py username1 [username2 ...]
支持多用户名, 运行成功后会生成后缀为.opml的文件, 像以前一样, 导入Google Reader就行了.

Tuesday, August 18, 2009

在Google Reader中备份和搜索好友的Tweets

Twitter in Google Reader

2010.1.19更新:
现在不推荐使用这种方法来获取Twitter好友的feed, 因为经过试验当好友数达到一定数目时, 获取的feed列表是不完全的.

来自Lifehacker的文章: Backup and Search Your Friends' Tweets with Google Reader [Twitter], 介绍了怎样在Google Reader中备份和搜索好友的Tweets.

  1. 首先打开这个网址: http://tw.opml.org/get?user=xiaogaozi&folder=1, 把user改为你的Twitter用户名, 然后将网页的源代码保存为.xml或者.opml文件.
  2. 进入Google Reader中的"Manage subscriptions"页面, 在"Import/Export"标签页中将刚才保存的文件上传上去就行了.

这时你所有Following的好友都会显示在Google Reader中, 你能看到他们以往的Tweets, 也能通过Google强大的搜索功能进行检索.

不过如果好友名是中文的话, 就会被转换为Unicode代码, 我写了个脚本可以将Unicode代码转换回字符: code2string.py. 假设你刚才保存的文件名为xiaogaozi_twitter.opml, 那么执行:
$ python code2string.py xiaogaozi_twitter.opml
然后会生成一个trans_xiaogaozi_twitter.opml文件, 上传这个就行了. 如果没法执行这个脚本, 还有一种简便的方法, 直接在Google中搜索那串Unicode代码就行了, Google会自动将它转换为字符, 百度无此功能.