昨晚花了几个小时写了个计算人人网(其实我还是更喜欢叫他校内)当中,任意两个用户的好友之间交集的在线小工具,可以到我的实验室里看看这个小工具:人人网好友交集。
话说能写出这样的工具,主要有赖于我的空间提供商将系统换为 FreeBSD 后可以解析 Python 了,而且也没有限制 CGI,所以就成功了~
由于必须从人人网,而非本地,的用户页面,而非接口,获取数据,速度自然不可能快。所以我才有了那个提示:这一过程可能非常缓慢。不过事实上,经过测试,在我的网站的服务器上,速度还是相当了得的,基本上 2s-5s 就能出结果!想我自己的机子上好的时候都要 7s,不好的时候就根本出不来了……
如果单线程进行获取,那就真可以等死人了……所以用了5线程并发,每个线程维护一个链接……此外还连接的是手机人人网,以换来较小的传输流量,较快的解析速度,和较容易的处理方式……
最后,看代码之前声明一下,这个是以 AGPLv3 协议发布的,根据这一协议,如果你修改了这个代码并且使用你修改过的代码为他人服务,你所修改的代码也必须公开,并且以 AGPLv3 协议发布。
由于前台界面的东西大家都有办法获取,我就不给了,这里直接给出那个后台处理程序:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | #!/usr/bin/env python # - * - coding: UTF-8 - * - # Copyright (C) 2010 Upsuper <quanxunzhen@gmail.com> # License: AGPLv3 from threading import Thread, Lock from httplib import HTTPConnection import urllib import cgi, json import sys, re # 常数设置 THREADS_NUM = 5 # 最大线程数 RENREN_USER = '' # 人人网用户名 RENREN_PWD = '' # 人人网密码 USER_AGENT = 'urenren 0.1' # 提交给人人网的 User-Agent # JSON 输出函数 json_dump = lambda v: json.dump(v, sys.stdout) # 编译匹配用正则表达式 parse_re = re.compile(r'<td><p><a href="[^\?]+\?id=(\d+)[^"]*">' r'<img src="([^"]+)" border="0" /></a></p>' r'<a href="[^"]+">([^<]+)</a></td>') class RequestThread(Thread): def __init__(self, id, sid, friends): Thread.__init__(self) self.__page = '/getfriends.do?curpage=%%d&id=%d&sid=%s' % (id, sid) self.__friends = friends self.__conn = HTTPConnection('3g.renren.com') def run(self): global curpage, stop_sign while not stop_sign: # 获取当前页面 curpage_lock.acquire() page = curpage curpage += 1 curpage_lock.release() # 连接获取数据 conn = self.__conn conn.request('GET', self.__page % (page, ), None, { 'User-Agent': USER_AGENT }) data = conn.getresponse().read() # 处理获取的数据 friend_iter = parse_re.finditer(data) t_stop_sign = True for f in friend_iter: t_stop_sign = False id = int(f.group(1)) self.__friends[id] = (f.group(3), f.group(2)) stop_sign = t_stop_sign def readFriends(id): # 初始化多线程 global curpage, curpage_lock, stop_sign curpage = 0 curpage_lock = Lock() stop_sign = False threads = [] friends = {} # 创建线程 for i in xrange(THREADS_NUM): threads.append(RequestThread(id, sid, friends)) # 开始执行线程 for t in threads: t.start() # 等待线程结束 for t in threads: t.join() return friends def main(): # 初始化CGI输出 print 'Content-Type: text/plain' print # 获取ID信息 form = cgi.FieldStorage() id1, id2 = 0, 0 if form.has_key('id1'): id1 = int(form['id1'].value) if form.has_key('id2'): id2 = int(form['id2'].value) if not (id1 and id2): json_dump({'error': True}) return # 登入人人网 conn = HTTPConnection('3g.renren.com') conn.request('POST', '/login.do', urllib.urlencode({'email': RENREN_USER, 'password': RENREN_PWD}), { 'Content-Type': 'application/x-www-form-urlencoded', 'User-Agent': USER_AGENT, }) response = conn.getresponse() data = response.read() conn.close() match = re.search(r'sid=([0-9a-f]+)', data, re.I) global sid sid = match.group(1) # 读取好友 friends1 = readFriends(id1) friends2 = readFriends(id2) if len(friends1) > len(friends2): friends1, friends2 = friends2, friends1 # 判断交集 intersection = [] for i in friends1.iterkeys(): if i in friends2: intersection.append((i, friends2[i][0], friends2[i][1])) json_dump({'error': False, 'count': len(intersection), 'data': intersection}) if __name__ == '__main__': try: main() except: json_dump({'error': True}) |
代码中的配置常数 RENREN_USER 必须是一个已有的人人网账户的帐户名或邮箱地址,反正就是可以用来登入人人网的东西,RENREN_PWD 也必须是前面的账户对应的密码。因为人人网不允许未登入用户查看各种页面(大约是为了防止搜索引擎的人肉吧……不过其实我们有办法让搜索引擎索引到那些内容的~嘿嘿,突然有了一些邪恶的想法~),所以只能弄一个帐号来登入了。
人人网的手机版为了适应部分手机浏览器不支持 Cookies,用了 SID 的策略,这样我也就可以放心大胆地不管 Cookies 了~这个程序还是蛮好写的说~也只有 Python 能提供如此快速的程序构建~
对这个小工具感兴趣,不过不懂代码,可否传我一份 谢谢 donghao1990@hotmail.com