python多线程程序的中断(信号)处理

python程序很容易进行多线程处理,也很好进行signal的处理。但是,在多线程程序中进行信号处理,却不太好搞。我琢磨出了一个可行的办法。
看第一个程序:singlethread.py

#!/usr/bin/python
#coding:utf-8

import signal,os,sys,threading,time
import wk
def sig_exit():
    print "[end time]:"+str(time.time())
    print "exited."
    sys.exit()

def handler(signum, frame):
    print "got an signal",signum,frame
    if signum == 3:
        sig_exit()
    if signum == 2:
        sig_exit()
    if signum == 9:
        sig_exit()
        return None
signal.signal(signal.SIGINT,handler)
signal.signal(signal.SIGTERM,handler)
signal.signal(3,handler)




TIME=3

time.sleep(TIME)
print 'normally exit'

运行这个程序,并马上按上Ctrl+C,发现程序这样输出:
<pre>
^Cgot an signal 2
[end time]:1245852618.36
exited.

而不做干扰,让它正常退出时,是这样的:

normally exit

现在我们改进到多线程版本。先写一个线程类:wk.py

import threading,time
class wk(threading.Thread):
def run(self):
for k in xrange(0,4):
print ‘sleep 1 second,’,self,”:”,k
time.sleep(1)

这个线程类啥也不干,就不断输出一个标识 ,然后再歇一秒。再输出,再歇一秒….
主程序:multithreads.py

#!/usr/bin/python
#coding:utf-8

import signal,os,sys,threading,time
from wk import wk

def sig_exit():
print “[end time]:”+str(time.time())
print “exited.”
sys.exit()

def handler(signum, frame):
print “got an signal”,signum,frame
sig_exit()
if signum == 3:
sig_exit()
if signum == 2:
sig_exit()
if signum == 9:
sig_exit()
return None
signal.signal(signal.SIGINT,handler)
signal.signal(signal.SIGTERM,handler)
signal.signal(3,handler)
signal.signal(signal.SIGALRM, handler)

threads=[]
for a in xrange(0,4):
temp=wk()
temp.setDaemon(True)
temp.setName(“thread:”+str(a))
temp.start()
threads.append(temp)

for thread in threads:
thread.join()
print “thread joined”,thread

print ‘normally exit’

现在我们运行它,并按下CTL+C.输出结果像这样:

sleep 1 second,sleep 1 second, : 0
sleep 1 second, : 0
: 0
sleep 1 second, : 0
sleep 1 second, : 1
sleep 1 second, : 1
sleep 1 second, : 1
sleep 1 second, : 1
^Csleep 1 second, : 2
sleep 1 second, : 2
sleep 1 second, : 2
sleep 1 second, : 2
^Csleep 1 second, : 3
sleep 1 second, : 3
sleep 1 second, : 3
sleep 1 second, : 3
got an signal 2
[end time]:1245852879.37
exited.

在按下CTRL+C后,程序并不是马上退出,而是又过了几秒钟才退出。
而运行时不按CTRL+C干扰它时,是这样的输出:

sleep 1 second, : 0
sleep 1 second, : 0
sleep 1 second, : 0
sleep 1 second, : 0
sleep 1 second, : 1
sleep 1 second, : 1
sleep 1 second, : 1
sleep 1 second, : 1
sleep 1 second, : 2
sleep 1 second, : 2
sleep 1 second, : 2
sleep 1 second, : 2
sleep 1 second, : 3
sleep 1 second, : 3
sleep 1 second, : 3
sleep 1 second, : 3
thread joined
thread joined
thread joined
thread joined
normally exit
</coolcode>
说明在这个程序里,中断信号能被捕获,但是并不即时。

而最后我的多线程程序是:last.py

#!/usr/bin/python
#coding:utf-8

import signal,os,sys,threading,time
from wk import wk

def sig_exit():
print “[end time]:”+str(time.time())
print “exited.”
sys.exit()

def handler(signum, frame):
print “got an signal”,signum,frame
if signum == 3:
sig_exit()
if signum == 2:
sig_exit()
if signum == 9:
sig_exit()
return None
signal.signal(signal.SIGINT,handler)
signal.signal(signal.SIGTERM,handler)
#signal.signal(3,handler)
signal.signal(signal.SIGALRM, handler)

class pause(threading.Thread):
threads=[]
def run(self):
print “pause running”
for thread in self.threads:
thread.join()
print “thread “,thread,” joined”
print “all joined”
signal.alarm(1)

threads=[]
for i in xrange(0,4):
j=wk()
j.setDaemon(True)
j.setName(“thread:”+str(i))
j.start()
threads.append(j)
print “total threads:”,threading.activeCount()

print “current thread:”,threading.activeCount()
print “parent process,main thread,sleeping”

k=pause()
k.setDaemon(True)
k.threads=threads
k.start()
signal.pause()
print “normally exit”

这个程序里,我们的逻辑是这样的:主线程创建了其他工作线程,并且还创建了一个特殊的线程, 这个特殊的线程的任务就着等着其他的工作线程结束(调用了wk类的join方法)。当工作线程都结束后,这个线程就发一个alarm信号。而主线程创建完所有的其他线程后的工作,就是在坐在那儿专门等一个信号。因此当我们按CTRL+C时信号就成功地被捕获了。而没有CTRL+C产生的信号被捕获时,所有工作线程(wk类)结束时也会产生一个alarm信号,也能让程序顺利退出。
这个程序,运行时按下CTRL+C的输出是:

sleep 1 second, : 0
total threads: 2
sleep 1 second, : 0
total threads: 3
sleep 1 second, : 0
total threads: 4
sleep 1 second, : 0
total threads: 5
current thread: 5
parent process,main thread,sleeping
pause running
^Cgot an signal 2
[end time]:1245853073.05
exited.

CTRL+C给程序的信号马上得到了处理。运行这个程序时啥也不做,等着程序结束,输出则是:

sleep 1 second, : 0
total threads: 2
sleep 1 second, : 0
total threads: 3
sleep 1 second, : 0
total threads: 4
sleep 1 second, : 0
total threads: 5
current thread: 5
parent process,main thread,sleeping
pause running
^Cgot an signal 2
[end time]:1245853073.05
exited.
renlu@aragorn:~/Dropbox/Private/devel/python$ py lastprogram.py
sleep 1 second, total threads: : 0
2
sleep 1 second, : 0
total threads: 3
sleep 1 second, : 0
total threads: 4
sleep 1 second, : 0
total threads: 5
current thread: 5
parent process,main thread,sleeping
pause running
sleep 1 second, : 1
sleep 1 second, : 1
sleep 1 second, : 1
sleep 1 second, : 1
sleep 1 second, sleep 1 second, : 2
: 2
sleep 1 second, : 2
sleep 1 second, : 2
sleep 1 second, : 3
sleep 1 second, : 3
sleep 1 second, : 3
sleep 1 second, : 3
thread joined
thread joined
thread joined
thread joined
all joined
got an signal 14
normally exit

好色的程序员:怎么加上彩色显示

问题列表

1.终端如何支持显示彩色
     现代终端基本上都已经支持彩色显示了。【8色的显示屏是什么时候发明的?】 您可以在bash下用这段来试试:<coolcode>
echo -e “33[40;32mcolored text33[0;0m”
</coolcode>
如果这里出来的是彩色文字,那就说明您的终端是支持彩色显示的。如是没有出来彩色文字,那就,改您的终端类型。一般情况下您的终端软件可选的有vt100,vt102,vt220,vt320,linux,xterm等几种。我一般选linux.
2.终端颜色和颜色表:
   要在终端中显示彩色文字,需要这样输出<coolcode>
ESC[attr;frontcolor;backgroundcolor;m

要加彩色的文字。
</coolcode>

ESC是ESCAPE键,它的ASC码是十进制的27,八进制的33.所以我们用echo -e “33…”来输入。attr是要给文字设置的属性代号,frontcolor是文字颜色代号,backgroundcolor是文字背景色代码。这些代号及意义如下:
<coolcode>

  前景             背景              颜色 
   ————————————— 
   30                40               黑色 
   31                41               紅色 
   32                42               綠色 
   33                43               黃色 
   34                44               藍色 
   35                45               紫紅色 
   36                46               青藍色 
   37                47               白色 

   代码              意义 
   ————————- 
   0                 OFF 
   1                 高亮显示 
   4                 underline 
   5                 闪烁 
   7                 反白显示 
   8                 不可见 
</coolcode> 
2.PS1,ls ,grep 如何加彩色
  PS1是bash一个特殊的变量,用来指定提示符。我们只要修改它的值就可以指定提示符如何显示。在这里h表示主机名,u表示当前用户,w表示当前目录。我的设置是:
<coolcode>export PS1=”[e[36;1m]u@[e[32;1m]H>:w>[e[0m]”</coolcode> 

这里bash的提示符已经变成彩色了,但是ls的输出还不是彩色的。也很简单,man一下就知道了,在linux下,

<coolcode>alias ls=”ls –color=auto”</coolcode>就行了。在FreeBSD下,<coolcode>alias ls=”ls -G”</coolcode>即可。

grep也是支持彩色的:
<coolcode>
alias grep=”grep –color=auto”
</coolcode> 
好了,现在我们可以把~/.bashrc文件 改一下:
<coolcode>
export PS1=”[e[36;1m]u@[e[32;1m]H>:w>[e[0m]”
alias ls=”ls –color=auto”
alias grep=”grep –color=auto”
</coolcode> 
 

2.svn diff怎么加彩色:
我写了一个脚本,放在http://xurenlu.googlecode.com/files/svncolor.py这里。只需要svn diff|python svncolor.py即可把svn diff的结果转成彩色的。

3.git diff怎么加彩色
git diff加颜色非常简单:修改.git/config,加上
<coolcode>
[diff]
    color = auto 
</coolcode> 

4.vim 怎么支持彩色
非常简单,如果是php文件:
<coolcode>
:set ft=php
:set syntax
</coolcode> 
就行了。 

6.怎么去彩色
为什么要去颜色呢,因为有时候我们需要把一些输出重定向到vim中间,这时在vim中会出现很多控制字符。所以我们要再转成黑底白字。
我这里也有一个脚本。 
<coolcode>
#!/usr/bin/python
import sys,re
PESC=chr(27)
nocolor_re=re.compile(PESC+'[[d;]+m’)
if __name__==”__main__”:
    while(True):
        line=sys.stdin.readline()
        if not line: break
        line=line.replace(“n”,””)
        print nocolor_re.sub(“”,line)
</coolcode>

PHP注释查看器

今天用着pydoc用的特爽,特希望能够找到一个php下的查看注释的工具。php生成文档倒是有一个phpdocument,另外还有一个给c/c++生成文档的doxygen也能用来给php生成文档。可是就是都太麻烦。于是我的用py写了一个查看php的注释的,能够逐行读入php代码,区分用/*来给出的注释。目前只能查看phpdocument方式的注释,另外,类里面的函数不能正确给出类名。不过在很多情况下够用了。关键是小巧。 

请在http://xurenlu.googlecode.com/files/phpcomment.py 这里下载。使用很简单。用 phpcomment.py < phpfile  或 phpcomment.py phpfile即可。

 

hyer开发笔记之:最大页面数的获取

搞垂直搜索(好吧,我承认我说得有点太高雅:搞网页采集)时,一般分这样两步:

  • 搞到列表页,读出所有待抓取的内容页面。
  • 挨个读取内容页面,解析出需要的信息,比如做购特搜索时需要采集商品名称,价格,描述,商品图片等

现在首先要解决的搞到列表页。拿到列表页时,有的同志很认真,就顺着那个下一页又一次地地抓取所有列表页面。其实不是特别必要。比如,bj.58.com的租房信息列表页,分了10000多页,光遍历这些列表页就需要好久。
hyer的处理方式是,把取到列表面的最大页面数做为一个任务,单独拿出来由一个专有模块去执行。逻辑是这样的:
首页你需要人工分析出列表面的URL的特征。58的那个很简单,一下子就能出来:
<coolcode>
http://bj.58.com/zufang/pn%d/
</coolcode>
%d是分页数。
其次我们要找的时,找出一个字串,在页面数过大(就是一直翻下一页,发现没有下一页可以翻的那个识别串)。这里58的页面估计是用.net的控件拖的,所以就是访问一个不存在的分页,比如第10万页,他也有下一页的链的妆可以翻。这时我们取的是列表项里的一个字符串:
<coolcode>
“onmouseout=”this.style.background=’#F7F9FE”
</coolcode>
[顺便说一下,58的前端设计同学,这样写javascript不太好呀。].
接下来,我们需要一页页地翻。(您要有疑问了,咦,说白了您也还是一页页地翻啊!)
NO!
我们是用二分法的思维来试!首先我们一次跳2048页,从第一页,依次试了1,2048,4097等页.
<coolcode>
page: 1  is not max page
page: 2049  is not max page
page: 4097  is not max page
page: 6145  is not max page
page: 8193  is not max page
page: 10241  is not max page
page number  12289  too big..
<coolcode>
然后发现12289页无法找到这个特征字串。现在我们知道,这个最大页码至少是10241了。接下来,我们一次跳过512个页码,分别访问10753,11265..页。

如此类推,一直到每次跳过1页时,就能很精确地找到最大页码了。好了,接下来我们的任务管理器就根据这个最大页码,决定启动多少个fake browser来要假装用户去读这1w多个列表页了。

未完,待续。

hyer开发笔记之:HTMLParser不支持中文问题

在开发hyer的过程中,我发现beautifulsoup解析中文网页总是有问题。于是乎,我决定用tidy来在beautifulsoup之前做一个fix.没想到tidy的python极其不雅,首先tidy的强大在python版的uTidylib也完全感觉不到了,而且安装的时候时候还有错误。

等tidy问题终于修正之后,发现大量的网页已经能用美味的汤[beautifulsoup]来解决了,但是仍然有一部分网页 不行。

最终经过跟踪发现是在这里:(/usr/lib/python2.6/HTMLParser.py):

<coolcode>
attrfind = re.compile(
r’s*([a-zA-Z_][-.:a-zA-Z_0-9]*)(s*=s*’
r'(‘[^’]*’|”[^”]*”|[-a-zA-Z0-9./,:;+*%?!&$()_#=~@]*))?’)
</coolcode>

正则表达式的第二行在遇到形如

<coolcode>
alt=我是中国人 height=60 src=’images/go.gif’>
</coolcode>

这样内容为中文不加引号的属性时无法处理。于是稍做修改(我是个大老粗,改得很偷懒,但是好像很管用)

<coolcode>
attrfind = re.compile(
r’s*([a-zA-Z_][-.:a-zA-Z_0-9]*)(s*=s*’
r'(‘[^’]*’|”[^”]*”|[^s^’^”]*))?’)
</coolcode>
就能解析中文属性了。

pythoni学习笔记之:中文乱码相关

我在python中使用的碰到的中文相关的问题有:
1.执行是出现“SyntaxError: Non-ASCII character ‘xe6’ in file 2.py on line 2, but no encoding declared; “类似的错误 。
2.打印时或是将输出重定向时结果是乱码。
搜集了一些相关资料:

1.可在文件首行加上:

# -*- coding: UTF-8 -*-

2.可设置pythonq解析引擎的默认编码:

import sys
encoding=sys.getdefaultencoding()
print encoding
reload(sys)
sys.setdefaultencoding(“UTF-8”)
encoding=sys.getdefaultencoding()
print encoding


3.可针对字符串解码再编码:

print “有了你世界是不同”.decode().encode(“GBK”)

beautiful Soup in Python

“美味的汤”是一个python的HTML/XML解板器,主要特性是:
1.美味的汤在你给出的标记不正确时不会阻赛。它尽可能地处理地和原文档近似。这样通常足够你收集信息。
2.美味的汤提供了少量几个方法(但却是python味道十足的方法)以足够你浏览,搜索,修改一棵解析树:一个能拼接出或是解析出你所要的的工具。你不需要为每个应用创建一个定制的解析器。
3.美味的汤能够自动将任何格式的输入换成Unicode,并在输出时转成UTF-8.除非文档没有指定编码而美味的汤又不能自动探测到这个编码。遇到这种情况你只需要指定原始编码就行了。
美味的汤解析你所给的任何东西,并且为你做遍历工作。你可以告诉它,”去找到所有链接”,或“找到所有带有一个叫externalLink的class的链接,或是”找到链接地址是foo.com的链接,或“找到表头是加粗文本的表格,并把这个文本返回给我”.
现在你可以处理那样设计很糟糕的网站的数据了。过去需要半天的工作现在有了美味的汤只需要几分钟了.
原始文档地址:
http://www.crummy.com/software/BeautifulSoup/documentation.html