给PyFetion加入了HTTP代理的支持,HTTP发送方式中。TCP发送方式也可能会加上吧,使用SocksiPy这个库吧,有时间再说。
改PyFetion的时候发现urllib2的一个问题:
先看下面这段代码:
PYTHON:
-
import urllib2
-
headers = [("Content-Type","application/oct-stream"),]
-
opener = urllib2.build_opener()
-
opener.addheaders = headers
-
urllib2.install_opener(opener)
-
print "after install_opener"
-
-
ret = opener.open('http://www.dict.cn',data="word=ss")
-
print ret.read()
抓包发现发送的内容为:
POST / HTTP/1.1
Accept-Encoding: identity
Content-Length: 7
Host: www.dict.cn
Content-Type: application/x-www-form-urlencoded
Connection: close
word=ss
这里我在代码里已经指定了HTTP的header: Content-Type,但是发出去的时候却被改变了。
通过分析urllib2的代码,找到:
PYTHON:
-
if request.has_data(): # POST
-
data = request.get_data()
-
if not request.has_header('Content-type'):
-
request.add_unredirected_header(
-
'Content-type',
-
'application/x-www-form-urlencoded')
-
if not request.has_header('Content-length'):
-
request.add_unredirected_header(
-
'Content-length', '%d' % len(data))
-
-
scheme, sel = splittype(request.get_selector())
-
sel_host, sel_path = splithost(sel)
-
if not request.has_header('Host'):
-
request.add_unredirected_header('Host', sel_host
-
or host)
-
for name, value in self.parent.addheaders:
-
#这里的parent是opener对象
-
name = name.capitalize()
-
if not request.has_header(name):
-
request.add_unredirected_header(name, value)
urllib2发现如果是POST数据的话自己添加了Content-Type,接着才去追加opener对象中的headers,这时已经有Content-Type了,所以opener对象增加的Content-Type就无效了。
解决办法是创建request对象,在request对象中设置Content-Type:
request = urllib2.Request(url,headers=headers,data=body)
不知道是上面的示例代码写法不规范呢?还是算Python的一个小问题呢?
最近发现周董的不少歌挺好听,于是找个脚本来下载周董的歌(从百度mp3中), getsong就进入了眼的视野,不过不支持下载某个歌手的全部歌曲,俺自己加上去,正在下载周董的歌:
[cocobear@cocobear getsong]$ ./getsong.py -a 周杰伦
正在下载第1首(共104首) 歌手:周杰伦 曲名:她的睫毛
已经成功下载《周杰伦 - 她的睫毛》
.......
不过这会儿断了,getsong好像又有bug了,今天就不折腾了,明天再整整吧,看书,睡觉了。
一个是以前写的导出抓虾收藏的工具,增加了保存抓虾上文章的功能,因为只导出链接有些文章地址可能已经失效。
另一个就是pysdcv,前面文章介绍过了,整理了一下,测试了一下,然后放在了google code上面,直接使用星际译王的词典,查一个单词大概0.15秒的时间:
cocobear@0-0 /home/cocobear/Work/pytool/pysdcv $ make
gcc lookup.c -g -I/usr/include/python2.5 -lz -shared -fPIC -o lookup.so
cocobear@0-0 /home/cocobear/Work/pytool/pysdcv $ time ./pysdcv.py test
*[test]
n. 测试, 试验, 化验, 检验, 考验, 甲壳
vt. 测试, 试验, 化验
vi. 接受测验, 进行测试
【医】 试验, 测验
【经】 检验, 试验, 测试
相关词组:
put sth to the test
stand the test
give a test
take a test
test sb's ability
real 0m0.164s
user 0m0.143s
sys 0m0.010s
如果是未使用gzip压缩过的dict文件,则0.05秒左右。
早就看到弥缝发起的活动:一个月培养一个好习惯,也练过那么几天字,不过荒废了,不知道怎么最近看着人家写得漂亮的书法作品很是喜欢,终于下定决定要把钢笔字先写好,别二十多岁的人了,写出来的字还像个小学生一样,太丢人了!
记得小时候还写过一段时间的毛笔字,都不记得后来为什么没坚持下去,为了让自己能坚持下去,使用弥缝介绍的“习惯培养清单”。
发在博客里的目的是希望能更好的监督自己,每月总结一次。
Python QQ是好早以前就有的一个项目,不过因为很久没更新过,以前看到的时候不能登录,所以没怎么关注过,最近代码被开发者放在了google code上面,也做了些改动,目前可以登录,发送,接收消息了,不过不是很稳定。最近看了看这个项目的代码,对整个socket-qq分枝有了全面了解。在代码设计上也学到了不了东西:-)
Python QQ是在Windows上面开发的,所以我在Linux下测试的时候还有不少问题,最近改了改,然后做了个挂QQ的工具给草儿同学,很不完善的,也很不稳定,使用wxPython+threading完成,GUI+多线程确实是很折腾人的,在多线程更新GUI上使用了wx.CallAfter函数,可以查看limodou博客的介绍。
截个图上来:

先不发布了,继续完善Python QQ。
首先安装必要的软件:sendmail,dovecot
sendmail只支持SMTP发信,dovecot可以支持POP3,IMAP等。
把自己的机器名改为一个你想使用的域名,例如coco.com(vi /etc/sysconfig/network)。配置sendmail,修改以/etc/mail/sendmail.mc下一行:
-O DaemonPortOptions=Port=smtp,Addr=127.0.0.1 , Name=MTA
+O DaemonPortOptions=Port=smtp,Addr=192.168.1.112 , Name=MTA
同时修改/etc/mail/access,添加:
Connect:192.168.1 RELAY
编辑/etc/mail/local-host-names文件,添加你想在内部使用的一个域名,如:
coco.com
这样局域网内的用户就可以使用你的SMTP了,别人在发邮件时在SMTP服务器地址中填写你的IP地址,或者域名(需要改更hosts中coco.com的指向)。帐户就直接使用Linux系统的用户,因为在一个公司使用,所以安全问题就不需要考虑了。
安装好后先测试sendmail,可以使用 mail -s ‘your subject' somebody@coco.com命令来发邮件。也可以使用claws-mail等客户端来发邮件。
dovecot安装好后也很简单,启动后就可以使用POP3收邮件了。
公司新拿来几台电脑,头让俺换一台用用,趁这个机会就想装个Archlinux试试,没想到折腾两天了,还是用不起来。
整个硬盘用ghost对拷了一下,Windows分区拷过去都正常,以前用的主Linux分区无法使用,以前这个分区就有问题,使用正常,但从Windows下无法访问,而且引导文件也放在另一个ext3分区上。
用光盘安装的Archlinux,300多M,base很快就安装好了,pacman也用的挺顺手,不过记得要加几个国内速度快一点的源,然后使用rankmirrors来重排一下源的顺序。不过在安装X的时候出问题了(我主板集成HD3200显卡),折腾了好久,手动改了几次xorg.conf文件,最后显卡驱动用xf86-video-ati,在xorg.conf文件里加上了1680x1050这个分辩率。好不容易(openbox+fbpanel+rox)启动后分辩率正常了,也可以自由切换到其它终端,但是安装ibus的时候又出问题了,使用的是yaourt,实在是折腾累了,浏览器里的字体也很不好看,uxvrt不好用,不能粘贴,不能用标签,字体怪怪的也不知道怎么改,问题一大堆啊,有点像刚用Fedora的时候,要真正能使用系统得折腾好久,但现在实在是懒得折腾了,看着人家漂亮的Archlinux桌面,只能叹息了。
俺自己的那台电脑更是郁闷,是Intel GMA 3100的显卡,本来说Linux应该支持挺好的,但在Archlinux里安装xf86-video-intel驱动后,X启动报错,如果使用vesa驱动能进入X,但是切换到终端再切换回X后就花屏了,昨天折腾了一晚上也不搞定,看来只能回到Fedora了。
不过话说回来,Archlinux速度确实挺快的,连启动X估计就10秒多点,Archlinux+openbox这样轻量级的组合确实不错,如果能在细节上完美些的话还真不想换回Fedora了。
切换到Windows下写的这篇文章,一会儿再整整Archlinux,不过俺自己那台机器要能让X在Archlinux上跑起来估计悬!
今天配置一个Bug跟踪的工具,使用Sendmail发邮件时一直出错,先没仔细看,后来仔细看了下,在/var/log/maillog里面有出错提示:
451 DT:SPM mx4, 7lcQrLDL7iJgl6dJktVcJQ--.7531S2, please try again 1235720034 http://mail.163.com/help/help_spam_16.htm?ip=1038939298&hostid=mx4&time=1235720034
在上面的链接中会有详细的提示,这个错误451:
发信人短期内发送了大量信件,超过了网易的限制,该发件人被临时禁止发信。请检查是否有用户发送病毒或者垃圾邮件,并降低该用户发信频率。
郁闷啊,我才手动连着发了几封而已。还有不少地方需要注意。
还有如果直接使用sendmail向gmail发邮件会提示:
http://mail.google.cn/support/bin/answer.py?hl=en&answer=10336
'The IP you're using to send email is not authorized...'
In order to prevent spam, Gmail refuses mail when the sending IP address does not match the sending domain. To send mail from your server to Gmail, we suggest using the SMTP relay provided by your ISP. Please note that we are unable to whitelist IP addresses or otherwise make exceptions at this time.
还是gmail考虑的多些,gmail的垃圾邮件过滤确实做的也不错。
sdcv-0.4.2版本的代码有3480行,而图形界面的星际译王更是有N多的代码,俺也写了一个就十几行的Python代码,速度当然比不上sdcv,能差十倍,不过,请注意,sdcv查一个单词是0.01秒,我这个是0.1秒,我不觉得有人能感觉出来差别。
其实我的核心是C语言写的,之所以在速度上没有sdcv快是没有做那么些优化,俺只写了80多行,而且还有不少代码是用作Python封装的。我以前写文章说过sdcv代码写的很麻烦,用C语言可以很简洁的写出来,现在确实做到了,只是结合了Python。我觉得这种模式挺不错,影响速度的核心使用C语言去写,然后主逻辑框架使用Python写,不仅效率不会受影响,开发的速度也提高了不少。(我这个例子Python并没做什么)
C:
-
static PyObject * lookup(PyObject *self, PyObject *args)
-
{
-
int fd;
-
char *file_prefix;
-
long file_size;
-
long wc;
-
char *word;
-
-
char *data;
-
const char *p;
-
long index_size;
-
int offset;
-
int size;
-
int flag = 0;
-
unsigned char *buf;
-
-
if (!PyArg_ParseTuple(args, "slls:lookup",
-
&file_prefix, &file_size, &wc, &word)) {
-
return NULL;
-
}
-
char file_name[256];
-
strcpy(file_name, file_prefix);
-
strcat(file_name, ".idx");
-
if ((fd = open(file_name, O_RDONLY)) <0) {
-
-
return NULL;
-
}
-
data = (char *)mmap( NULL, file_size, PROT_READ, MAP_SHARED, fd, 0);
-
p = data;
-
int i;
-
for (i=0;i<wc;i++) {
-
index_size = strlen(p) + 1 + 2*sizeof(int);
-
if (strcmp(word, p) == 0) {
-
flag = 1;
-
}
-
if (flag == 1) {
-
offset = ntohl(*(int *)(p + strlen(p) + 1));
-
size = ntohl(*(int *)(p + strlen(p) + 1 + sizeof(int)));
-
//printf("offset=%dnsize%dn",offset,size);
-
/*gzFile zfile;
-
zfile = gzopen("./dic/stardict-langdao-ec-gb-2.4.2/langdao-ec-gb.dict.dz", "rb");
-
gzseek(zfile, offset, SEEK_SET);
-
buf = (unsigned char *)malloc(*size+1);
-
memset(buf, ' ', size+1);
-
gzread(zfile, buf, size);
-
printf("%sn", buf);
-
*/
-
close(fd);
-
-
if ((fd = open("./dic/stardict-langdao-ec-gb-2.4.2/langdao-ec-gb.dict", O_RDONLY)) <0) {
-
return NULL;
-
}
-
lseek(fd, offset, SEEK_SET);
-
buf = (unsigned char *)malloc(size+1);
-
memset(buf, ' ', size+1);
-
read(fd, buf, size);
-
//printf("%sn",buf);
-
close(fd);
-
return Py_BuildValue("s", buf);
-
}
-
p += index_size;
-
}
-
-
return Py_BuildValue("s","");
-
}
-
-
static struct PyMethodDef lookup_methods[] = {
-
{"lookup", lookup, 1, "lookup(file_prefix, file_size, wc, word)"},
-
{NULL, NULL}
-
};
-
-
void initlookup()
-
{
-
(void) Py_InitModule("lookup", lookup_methods);
-
}
使用C语言对Python进行扩展挺方便的,http://gashero.yeax.com/?p=38#id7这里有个不错的文档。
完了俺整理整理也放在google code上去。
上面的代码中dict文件需要是未gzip压缩过的,如果在压缩过的我使用被注释掉的那段代码在seek的时候速度很慢,又没办法用mmap,所以暂时就先只使用未压缩过的。
老爸昨晚上打电话告诉我的。
我自己居然不记得,会不会有一天我都不记得自己出生年月了呢?
很难受,为什么会这样呢?
造化弄人!
libevent是一个异步事件处理软件函式库,以BSD许可证释出。
libevent提供了一组应用程序编程接口(API),让程式设计师可以设定某些事件发生时所执行的函式,也就是说,libevent可以用来取代网络服务器所使用的循环检查架构。
摘自维基百科
http://blog.gslin.info/2005/11/network-programming-using-libevent-i.html
这里介绍了libevent相关的网络编程背景,需要带套访问哦。
以下分析针对libevent-1.4.3-stable。
来看libevent自带的例子:
C:
-
/*
-
* Compile with:
-
* cc -I/usr/local/include -o event-test event-test.c -L/usr/local/lib -levent
-
*/
-
-
-
static void
-
fifo_read(int fd, short event, void *arg)
-
{
-
char buf[255];
-
int len;
-
struct event *ev = arg;
-
-
/* Reschedule this event */
-
event_add(ev, NULL);
-
-
fprintf(stderr, "fifo_read called with fd: %d, event: %d, arg: %pn",
-
fd, event, arg);
-
len = read(fd, buf, sizeof(buf) - 1);
-
-
if (len == -1) {
-
perror("read");
-
return;
-
} else if (len == 0) {
-
fprintf(stderr, "Connection closedn");
-
return;
-
}
-
-
buf[len] = '�';
-
fprintf(stdout, "Read: %sn", buf);
-
}
-
-
int
-
main (int argc, char **argv)
-
{
-
struct event evfifo;
-
struct stat st;
-
const char *fifo = "event.fifo";
-
int socket;
-
-
if (lstat (fifo, &st) == 0) {
-
if ((st.st_mode & S_IFMT) == S_IFREG) {
-
errno = EEXIST;
-
perror("lstat");
-
exit (1);
-
}
-
}
-
-
unlink (fifo);
-
if (mkfifo (fifo, 0600) == -1) {
-
perror("mkfifo");
-
exit (1);
-
}
-
-
/* Linux pipes are broken, we need O_RDWR instead of O_RDONLY */
-
socket = open (fifo, O_RDWR | O_NONBLOCK, 0);
-
-
if (socket == -1) {
-
perror("open");
-
exit (1);
-
}
-
-
fprintf(stderr, "Write data to %sn", fifo);
-
/* Initalize the event library */
-
event_init();
-
-
/* Initalize one event */
-
event_set(&evfifo, socket, EV_READ, fifo_read, &evfifo);
-
-
/* Add it to the active events, without a timeout */
-
event_add(&evfifo, NULL);
-
-
event_dispatch();
-
return (0);
-
}
我把原来代码中WIN平台相关的去掉了,看起来方便一些,这个例子创建了一个pipe,并且使用libevent来监听是否可读,读有数据可读时调用函数fifo_read。
libevent调用比较简单,首先event_init()对event库进行初始化,然后使用event_set()来对某个fd的操作进行监听,接着使用event_add()把这个event激活,这里可以指定超时的时间,最后一步event_dispatch(),开始进行循环。
event_init()里只是对外的一个接口,这个函数调用了event_base_new(),分配了一个event_base类型的空间,设置了一些全局变量,使用detect_monotonic来检测是否支持CLOCK_MONOTONIC类型的时钟,这里不太理解为什么要使用clock_gettime(CLOCK_MONOTONIC, &ts)来获得当前时间,这个与gettimeofday得到的精度是一样的,只是一个是标准的时间(UNIX元年算起),一个是开机时间算起,有什么差别吗?
CLOCK_MONOTONICI测试代码:
C:
-
/* gcc -o ftime ftime.c*/
-
#include<stdio .h>
-
#include<time .h>
-
#include<sys /time.h>
-
-
int main(void)
-
{
-
struct timeval tp;
-
-
gettimeofday(&tp,NULL);
-
printf("sec=%ld\n",tp.
tv_sec);
-
-
struct timespec ts;
-
-
clock_gettime(CLOCK_MONOTONIC,&ts);
-
printf("sec=%ld\n",ts.
tv_sec);
-
return 0;
-
}
结果:
[cocobear@cocobear libevent-1.4.3-stable]$ gcc ftime.c -lrt
[cocobear@cocobear libevent-1.4.3-stable]$ ./a.out
sec=1235372220
sec=4621
接下来就检测可使用的事件检测函数,这里与系统相关的调用被封装成了一个结构eventop:
C:
-
struct eventop {
-
const char *name;
-
void *(*init)(struct event_base *);
-
int (*add)(void *, struct event *);
-
int (*del)(void *, struct event *);
-
int (*dispatch)(struct event_base *, void *, struct timeval *);
-
void (*dealloc)(struct event_base *, void *);
-
/* set if we need to reinitialize the event base */
-
int need_reinit;
-
};
编译时libevent会通过
C:
-
#ifdef HAVE_SELECT
-
&selectops,
-
#endif
来“动态”的确定eventops数组,在定义这个eventops数组时确定了使用这些事件驱动模型的顺序,如果你机子上有多种可用的模式,则可以通过修改改数组来自定义使用的模型。
在event_base_new()的最后调用了event_base_priority_init()初始化了消息的优先级队列。主要就是对activequeues变量进行空间分配。默认是分配了一个event_list给activequeues。