这个脚本是根据图书的isbn号来抓取对应的图书评分信息的。data.csv是一个包含图书的isbn的列表文件,每一行是一个图书的isbn号。 这个版本只使用了单线程抓取,并且只能从csv文件中读取数据。因为是朋友拜托抓取的,数据量不大,以后如果有需要再慢慢改进好了。 整个Python脚本很简单,主要使用了BeautifulSoup进行html的内容提取。

P.S.最近手头还有几个非常有趣的项目正在制作,希望我能够早日搞定它们 :)

import urllib,urllib2
import re
import BeautifulSoup

def isbn_2_score(isbn):
    url = 'http://www.douban.com/subject_search?search_text='
    try:
        response = urllib2.urlopen(url+isbn)
    except Exception,e:
        return 0.0
    doc = response.read()
    soup = BeautifulSoup.BeautifulSoup(''.join(doc))
    try:
        book_info = soup.find("a",{"class":"nbg"})
    except Exception,e:
        return 0.0
    if isinstance(book_info,BeautifulSoup.Tag):
        url_book_info = book_info['href']
        try:
            response = urllib2.urlopen(url_book_info)
        except Exception,e:
            return 0.0
        book_page = response.read()
        soup = BeautifulSoup.BeautifulSoup(''.join(book_page))
        score_info = soup.find('strong','ll rating_num')
        if isinstance(book_info,BeautifulSoup.Tag):
            score = score_info.string
            return score
        return 0.0
    return 0.0

def read_file(file_name):
    file_handler = open(file_name,'r')
    return file_handler

def return_isbn(file_handler):
    isbn = file_handler.readline()
    return isbn


if __name__ == '__main__':
data = read_file('data.csv')
	f = open('dump','w')
	k = return_isbn(data)
	while k is not None:
    	score = isbn_2_score(k)
    	result = k[0:-1]+":"+str(score)+"\n"
    	print result
    	f.write(result)
    	k=return_isbn(data)
	f.close()

项目地址:https://github.com/quake0day/douban_crawler

美国的新年快到了,国内的同学们已经迎来了新的一年,我正好利用时差赶在这2010的最后一天里面发篇技术文章。

昨晚偶然发现Create Chen写了一篇利用基于HTTP的QQ api实现的QQ登陆的文章。作者给出了实现的C#代码,并对协议做出了详尽的分析。(URL1 URL2

通过HTTP的POST动作控制QQ,就可以开发很多很有意思的应用。比如我们可以开发QQ消息轰炸机(呵呵 :) ),可以制作QQ机器人...等等

我按照文章中所叙述的方法用我所喜欢的Python重新实现了一遍,下面是Python的代码。代码只实现了基本功能,对于返回的信息并没有进行处理,如果大家有兴趣,可以结合Create Chen的文章自行对返回信息进行处理。

下面是代码:

import urllib,httplib,md5,time
from time import sleep
class qq:
    def __init__(self,qq="",pwd=""):
        self.pwd=md5.new(pwd).hexdigest()
        self.headers=""
        self.qq=qq

    def getdata(self):
        self.conn=httplib.HTTPConnection("tqq.tencent.com:8000")
        self.conn.request("POST","",self.headers)
        response=self.conn.getresponse()
        print response.read().decode('utf-8').encode("cp936")
        sleep(1)
        self.conn.close()
    
    def Login(self):
        self.headers=("VER=1.1&CMD;=Login&SEQ;="+\
                       str(int(time.time()*100)%(10**5))+"&UIN;="+\
                       self.qq+"&PS;="+\
                       self.pwd+\
                       "&M5;=1&LC;=9326B87B234E7235")
        self.getdata()

    def GetInfo(self,friend=""):
        self.headers=("VER=1.1&CMD;=GetInfo&SEQ;="+\
                      str(int(time.time()*100)%(10**5))+"&UIN;="+\
                      self.qq+"&LV;=2&UN;="+\
                      friend)
        self.getdata()
        
    def AddToList(self,friend=""):
        self.headers=("VER=1.1&CMD;=AddToList&SEQ;="+\
                      str(int(time.time()*100)%(10**5))+"&UIN;="+\
                      self.qq+"&UN;="+\
                      friend)
        self.getdata()

    #agree_Type = 0 agree
    #agree_Type = 1 deny
    def Ack_AddToList(self,fri_Num,agree_Type):
        self.headers=("VER=1.1&CMD;=Ack_AddToList&SEQ;="+\
                      str(int(time.time()*100)%(10**5))+"&UIN;="+\
                      self.qq+"&UN;="+\
                      fri_Num+"&CD;="+agree_Type+"&RS;=")
        self.getdata()
                     
    def SendMsg(self,friend="",msg=""):
        self.headers=("VER=1.1&CMD;=CLTMSG&SEQ;="+\
                      str(int(time.time()*100)%(10**5))+"&UIN;="+\
                      self.qq+"&UN;="+\
                      friend+"&MG;="+\
                      msg.decode("cp936").encode('utf-8'))
        self.getdata()
                     
    def GetMsg(self):
        self.headers=("VER=1.1&CMD;=GetMsgEx&SEQ;="+\
                      str(int(time.time()*100)%(10**5))+"&UIN;="+\
                      self.qq)
        self.getdata()

    def Query_Stat(self):
        self.headers=("VER=1.1&CMD;=Query_Stat&SEQ;="+\
                      str(int(time.time()*100)%(10**5))+"&UIN;="+\
                      self.qq+"&TN;=50&UN;=0")
        self.getdata()

    def List(self):
        self.headers=("VER=1.1&CMD;=List&SEQ;="+\
                      str(int(time.time()*100)%(10**5))+"&UIN;="+\
                      self.qq+"&TN;=160&UN;=0")
        self.getdata()

    #stat = 10 online
    #stat = 20 offline
    #stat = 30 busy
    def Change_Stat(self,stat=""):
        self.headers=("VER=1.1&CMD;=Change_Stat&SEQ;="+\
                      str(int(time.time()*100)%(10**5))+"&UIN;="+\
                      self.qq+"&ST;="+stat)
        self.getdata()
    
    def Logout(self):
        self.headers=("VER=1.1&CMD;=Logout&SEQ;="+\
                      str(int(time.time()*100)%(10**5))+"&UIN;="+\
                      self.qq)
        self.getdata()



test = qq('9918xxxx','xxxxx')
test.Login()
print "OK"
#i =0 
#while i<5:
 #   print i
  #  sleep(2)
 #   test.SendMsg('xxxxxx',"I am robot")
#    i = i+1
i = 0
while i<10:
    print i
    test.Change_Stat('10')
    sleep(1)
    test.Change_Stat('30')
    sleep(1)
    test.Change_Stat('20')
    sleep(1)
    i = i + 1
test.Logout()

代码下载地址:http://www.darlingtree.com/download/qqlogin.tar.gz 祝大家新年快乐!

今天用cocobear的PyFetion搭建了一个激动人心的崭新的应用——将飞信转换成E-mail!当然对于正常在TG地盘混的各位同学来说这个应 用就是个渣,因为它把方便快捷的短信换成了笨拙的E-mail,但是,对于即将飞往大洋彼岸的我来说,这个应用有其独特的价值——我可以保留原有的手机 号,继续和大家通过飞信保持联系。

这个东西的工作原理是这样:模拟我登录飞信,接收一切发过来的飞信并保存到文本文档中,每隔一段时间,将文本文档中的内容通过E-mail发送到我的秘密邮箱中。

这个东西必须要配合一个智能手机来使用,智能手机通过设置,每隔一段时间查询秘密邮箱中的E-mail,这样我就可以半实时的接收到飞信了。

为什么要多此一举呢?

第一、该死的iphone飞信没法常驻后台,只能现用现打开,这样平时没有打开的时候没法知道谁发送了飞信给我。 第二、通过E-mail来处理飞信,可以保证不会遗漏。 第三、除了iphone,可以用任何支持E-mail的终端来查看飞信。

另外这个应用还只开发到了一半,目前还只能接收,还不能通过E-mail回复飞信。这个留在以后开发。因为可以通过登录飞信软件来回复。

另外就是和google voice相连接,当发送过来飞信后,自动通过google voice发送提示短信,提示我查看E-mail。或者直接将飞信的内容转换为汉语拼音(或者用google translate的api转换成为英文),发送到我手机上——这个目前尚且不急,美国如果没有定制相关plan,接收短信也要花不少钱,所以不如e- mail划算。所以这个可以放在最后做。

我的飞信从此就是24小时在线了。 源代码整理后放出。

之前做的那个自动查分系统的一个缺陷就是没法准确提取所有人的成绩和学分。之后某天爬山的时候突然脑海里灵光一闪....想出来了一个绝妙的方法。等我回学校之后测试了一下,发现这个方法果然可行。为了进一步应用它,我单独制作了这个GPA自动计算系统,也算是将去年用Javascript写的GPA计算系统升级了。

整套系统架设在美国的VPS主机上面,现在这个主机除了翻墙外又多了一个功能:) 该系统展现给用户的部分是用Javascript+PHP构成的,当然后续版本会使用jQuery,现在学艺不精先不用它采用jQuery增强了用户体验。这次依然是数据库作为桥梁,后台是用Python+sed来完成成绩的提取计算,并将最终结果返回给数据库。总的来说这套系统也算是一个小型的B/S系统了。

本人已经毕业,没法继续维护这个系统,故本系统已经下线。非常抱歉。

项目地址:https://github.com/quake0day/calGPA

访问地址:http://www.darlingtree.com/gpa

半年前我曾经制作过一个自动查分系统,这次我利用课程设计的机会,将这个系统重新制作成为了一个可以在浏览器中自助注册添加用户的B/S版查分系统。

除了功能上面增强了以外,我还将底层全部重写了一遍。这次使用的是可可熊的PyFetion来制作的短信发送功能,抓取功能由原来的PHP curl库改成用Python的urllib库。因此整个系统的功能实现只采用Python,降低了系统部署的难度。在功能提升的同时,我还利用PHP写了一个简单的注册系统。将注册系统部署好后,别的同学可以通过网络浏览器自己注册并使用该系统,免去了人工配置的麻烦。

整体系统设计方案

整个系统通过Python结合urllib库模拟用户登陆学生URP教务系统,并访问指定页面获取本学期该学生的成绩信息。并将包含成绩信息的HTML代码全部抓取下来。之后通过控制Python调用Linux下面Sed文本编辑器,对抓取的HTML信息代码进行剪裁提取,获得具有可读性的成绩信息。之后通过Python结合MySQL库将成绩保存到数据库中。

Python程序还将判断这次抓取后的成绩是否和上次抓取的相一致,如果不一致,则调用PyFetion库登陆飞信发短信报警。用户信息保存在MySQL数据库中,用户的添加是通过PHP程序来操控MySQL数据库实现的。

网页端采用了XHTML+CSS+Javascript的JQuery库来实现。因为采用了AJAX技术,使得该系统可以给予用户良好的操作体验。

整个系统启动需要启动Apache,MySQL和judge.py,其中Apache和MySQL是提供网页的运行环境,而judge.py则是采用轮循的方式对数据库中每个注册的用户进行扫描,调用ScoreScanner.py程序来判断是否需要发送短信。

因为课设时间紧,整个系统目前还有很多不足和亟待改进之处,已知的有:

    1. 现在用户注册时候必须提供自己的飞信密码,实现自己给自己发飞信。这样做的缺点是个人飞信密码容易泄露,同时查分时会干扰正常的飞信使用(会导致异常断线)。下一个版本应该改进为由某个用户统一给同学发飞信。
    1. 定时查分程序较为简陋。这次因为在使用Crontab做延时的时候总是出现一些问题,最后只好舍弃Crontab,改用Python自己的延时来实现。
    1. 分数转换部分的sed脚本还存在问题。在遇到某些特殊的课程的时候会导致转换错误。
    1. 缺乏用户自助管理系统,注册完后用户资料没法修改。

项目地址:http://code.google.com/p/caufetion/