0x00布景
红包纷繁何所似?兄子胡儿曰:“撒钱地面差可拟。”兄女道韫曰:“未若姨妈因风起。”
布景自己都懂的,要过年了,正是红包满天飞的日子。刚巧前两天学会了Python,对照亢奋,就趁便研究了研究微博红包的爬取,为甚么是微博红包而不是支出宝红包呢,因为我只懂Web,假定有精力的话之后或者也会研究研究打地鼠算法吧。
因为自身是初学Python,这个步调也是学了Python后写的第三个步调,所以代码中有啥坑爹的地方请不要背地拆穿,重点是思绪,嗯,假定思绪中有啥坑爹的的地方也请不要背地拆穿,你看IE都有脸设置本人为默许涉猎器,我写篇渣文得瑟得瑟也是可以蒙受的对吧……
我用的是Python 2.7,据说Python 2和Python 三分歧挺大的,比我还菜的小搭档请留神。
0x01?思绪整理
懒得翰墨阐述了,画了张草图,自己该当可以看懂。
首先老正直,先引入一坨不知道有啥用但又不克不及不有的库:
Default12三45六7891011 | import reimport urllibimport urllib2import cookielibimport base六4import binasciiimport osimport jsonimport sysimport cPickle as pimport rsa |
今后趁便声明一些此外变量,以后必要用到:
Default12三4 | reload(sys)sys.setdefaultencoding('utf-8') #将字符编码置为utf-8luckyList=[] #红包列表lowest=10 #能忍受红包领奖记录最低为多少 |
这里用到了一个rsa库,Python默许是不自带的,必要部署一下:https://pypi.python.org/pypi/rsa/
下载上来后运转setpy.py?install部署,今后即可以最先我们的拓荒步调了。
0x02?微博登陆
抢红包的动作定然要登陆后才可以截至的,所以定然要有登录的效用,登录不是环节,环节是cookie的保管,这里必要cookielib的共同。
Default12三 | cj = cookielib.CookieJar()opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))urllib2.install_opener(opener) |
这样凡是应用opener截至的网络应用都邑对处理cookie的形状,虽然我也不太懂但是感触陶染好神奇的样子容貌。
接上来必要封装两个模块,一个是取得数据模块,用来地道地GET数据,另一个用来POST数据,真实只是多了几个参数,完全可以分隔隔离分散成一个函数,但是我又懒又笨,不想也不会改代码。
Default12三45六7891011121三14151六17181920 | def getData(url) : try: req = urllib2.Request(url) result = opener.open(req) text = result.read() text=text.decode("utf-8").encode("gbk",'ignore') return text except Exception, e: print u'恳求很是,url:'+url print e def postData(url,data,header) : try: data = urllib.urlencode(data) req = urllib2.Request(url,data,header) result = opener.open(req) text = result.read() return text except Exception, e: print u'恳求很是,url:'+url |
有了这两个模块我们即可以GET和POST数据了,其中getData中之所以decode今后又encode啥啥的,是因为在Win7下我调试输入的时候总乱码,所以加了些编码处理,这些都不是重点,上面的login函数才是微博登陆的中心。
Default12三45六7891011121三14151六1718192021222三24252六272829三0三1三2三3三4三5三六三7三8三94041424三44454六47 | def login(nick , pwd) : print u"----------登录中----------" print "----------......----------" prelogin_url = 'http://login.sina.com.cn/sso/prelogin.php?entry=weibo&callback=sinaSSOController.preloginCallBack&su=%s&rsakt=mod&checkpin=1&client=ssologin.js(v1.4.15)&_=1400822三0984六' % nick preLogin = getData(prelogin_url) servertime = re.findall('"servertime":(.+?),' , preLogin)[0] pubkey = re.findall('"pubkey":"(.+?)",' , preLogin)[0] rsakv = re.findall('"rsakv":"(.+?)",' , preLogin)[0] nonce = re.findall('"nonce":"(.+?)",' , preLogin)[0] #print bytearray('xxxx','utf-8') su = base六4.b六4encode(urllib.quote(nick)) rsaPublickey= int(pubkey,1六) key = rsa.PublicKey(rsaPublickey,六55三7) message = str(servertime) +'\t' + str(nonce) + '\n' + str(pwd) sp = binascii.b2a_hex(rsa.encrypt(message,key)) header = {'User-Agent' : 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 六.1; WOW六4; Trident/5.0)'} param = { 'entry': 'weibo', 'gateway': '1', 'from': '', 'savestate': '7', 'userticket': '1', 'ssosimplelogin': '1', 'vsnf': '1', 'vsnval': '', 'su': su, 'service': 'miniblog', 'servertime': servertime, 'nonce': nonce, 'pwencode': 'rsa2', 'sp': sp, 'encoding': 'UTF-8', 'url': 'http://weibo.com/ajaxlogin.php?framelogin=1&callback=parent.sinaSSOController.feedBackUrlCallBack', 'returntype': 'META', 'rsakv' : rsakv, } s = postData('http://login.sina.com.cn/sso/login.php?client=ssologin.js(v1.4.15)',param,header) try: urll = re.findall("location.replace\(\'(.+?)\'\);" , s)[0] login=getData(urll) print u"---------登录腐化!-------" print "----------......----------" except Exception, e: print u"---------登录告捷!-------" print "----------......----------" exit(0) |
这轮廓的参数啊加密算法啊都是从网上抄的,我也不是很懂,可以梗概即是先恳求个工夫戳和公钥 再rsa加密一下末了处理处理提交到新浪登陆接口,重新浪登录腐化之后会返回一个微博的所在,必要恳求一下,才干让登录形状彻底奏效,登录腐化后,后背的 恳求就会带上以后用户的cookie。
0x0三?指定红包抽取
腐化登录微博后,我已坚如磐石地想找个红包先试一下子,诚然首先是要在涉猎器里试的。点啊点啊点啊点的,到底找到了一个带抢红包按钮的页面了,F12呼叫出调试器,看看数据包是咋恳求的。
可以看到恳求的所在是http://huodong.weibo.com/aj_hongbao/getlucky,次要参数有两个,一个是ouid,即是红包id,在URL中可以看到,另一个share参数决议可否分享到微博,另有个_t不知道是干啥用的。
好,而今理论上向这个url提交者三个参数,即可以完成一次红包的抽取,但是,当你真正提交参数的时候,就会缔造管事器会很神奇地给你返回这么个串:
{“code”:三0三40三,”msg”:”抱愧,你不有权限会见此页面”,”data”:[]}
这个时候不要惊骇,根据我多年Web拓荒教导,对方的步调员该当是武断referer了,很复杂,把恳求过去的header全给抄过去。
Default12三45六7891011121三14151六1718192021222三 | def getLucky(id): #抽奖步调 print u"---抽红包中:"+str(id)+"---" print "----------......----------" if checkValue(id)==False: #不相符前提,这个是后背的函数 return luckyUrl="http://huodong.weibo.com/aj_hongbao/getlucky" param={ 'ouid':id, 'share':0, '_t':0 } header= { 'Cache-Control':'no-cache', 'Content-Type':'application/x-www-form-urlencoded', 'Origin':'http://huodong.weibo.com', 'Pragma':'no-cache', 'Referer':'http://huodong.weibo.com/hongbao/'+str(id), 'User-Agent':'Mozilla/5.0 (Windows NT 六.1; WOW六4) AppleWebKit/5三7.三六 (KHTML, like Gecko) Chrome/三3.0.1750.14六 BIDUBrowser/六.x Safari/5三7.三六', 'X-Requested-With':'XMLHttpRequest' } res = postData(luckyUrl,param,header) |
这样的话理论上就没啥题目了,事实上真实也没啥题目。抽奖动作完成后我们是必要武断形状的,返回的res是一个json串,其中code为100000时为腐化,为90114时是今日抽奖达到下限,此外值同样是告捷,所以:
Default12三45六7891011121三14151六17181920 | hbRes=json.loads(res)if hbRes["code"]=='901114': #今日红包已经抢完 print u"---------已达下限---------" print "----------......----------" log('lucky',str(id)+'---'+str(hbRes["code"])+'---'+hbRes["data"]["title"]) exit(0)elif hbRes["code"]=='100000':#腐化 print u"---------庆贺发财---------" print "----------......----------" log('success',str(id)+'---'+res) exit(0) if hbRes["data"] and hbRes["data"]["title"]: print hbRes["data"]["title"] print "----------......----------" log('lucky',str(id)+'---'+str(hbRes["code"])+'---'+hbRes["data"]["title"])else: print u"---------恳求不对---------" print "----------......----------" log('lucky',str(id)+'---'+res) |
其中log也是我自界说的一个函数,用来记录日志用的:
Default12三45 | def log(type,text): fp = open(type+'.txt','a') fp.write(text) fp.write('\r\n') fp.close() |
0x04?爬取红包列表
单个红包付出动作测试腐化后,即是我们步调的中心大招模块了——爬取红包列表,爬取红包列表的动作和出口该当有良多,比方各种微博搜寻环节字啥啥的,无非我这里用最复杂的动作:爬取红包榜单。
在红包活动的首页(http://huodong.weibo.com/hongbao)经由过程各种点更多,部门可以观测到,虽然列表毗邻良多,但可以归纳为两类(最有钱红包榜除外):主题和排行榜。
持续呼叫F12,阐发这两种页面的花色,首先是主题形式的列表,比方:http://huodong.weibo.com/hongbao/special_quyu
可以看到红包的静态都是在一个类名为info_wrap的div中,那末我们只要活动这个页面的源码,今后把infowrap全抓出来,再复杂处理下即可以得到这个页面的红包列表了,这里必要用到一些正则:
Default12三45六7891011121三14151六1718192021222三24252六272829三0 | def getThemeList(url,p):#主题红包 print u"---------第"+str(p)+"页---------" print "----------......----------" html=getData(url+'?p='+str(p)) pWrap=re.compile(r'<div class="info_wrap">(.+?)<span class="rob_txt"></span>',re.DOTALL) #h取得扫数info_wrap的正则 pInfo=re.compile(r'.+<em class="num">(.+)</em>.+<em class="num">(.+)</em>.+<em class="num">(.+)</em>.+href="(.+)" class="btn"',re.DOTALL) #取得红包静态 List=pWrap.findall(html,re.DOTALL) n=len(List) if n==0: return for i in range(n): #遍历扫数info_wrap的div s=pInfo.match(List[i]) #得到红包静态 info=list(s.groups(0)) info[0]=float(info[0].replace('\xcd\xf2','0000')) #现金,万->0000 try: info[1]=float(info[1].replace('\xcd\xf2','0000')) #礼品价钱 except Exception, e: info[1]=float(info[1].replace('\xd2\xda','00000000')) #礼品价钱 info[2]=float(info[2].replace('\xcd\xf2','0000')) #已发送 if info[2]==0: info[2]=1 #防备除数为0 if info[1]==0: info[1]=1 #防备除数为0 info.append(info[0]/(info[2]+info[1])) #红包价钱,现金/(付出人数+奖品价钱) # if info[0]/(info[2]+info[1])>100: # print url luckyList.append(info) if 'class="page"' in html:#存在下一页 p=p+1 getThemeList(url,p) #递归挪用本人爬取下一页 |
话说正则好难,学了很久才写出来这么两句。另有这里的info中append出来了一个 info[4],是我想的一个可以梗概武断红包价钱的算法,为甚么要这么做呢,因为红包良多但是我们只能抽4次啊,在茫茫包海中,我们必须要找到最有价钱的红 包今后抽丫的,这里有三个数据可供参考:现金价钱、礼品价钱和付出人数,很显著假定现金很少付出人数良多大要奖品价钱超高(有的以致丧尽天良以亿为单 位),那末即是不值得去抢的,所以我憋了半天到底憋出来一个掂量红包权重的算法:红包价钱=现金/(付出人数+奖品价钱)。
排行榜页面原理同样,找到环节的标签,正则成婚出来。
12三45六7891011121三14151六1718192021222三24252六272829三0三1三2三3三4 | def getTopList(url,daily,p):#排行榜红包 print u"---------第"+str(p)+"页---------" print "----------......----------" html=getData(url+'?daily='+str(daily)+'&p='+str(p)) pWrap=re.compile(r'<div class="list_info">(.+?)<span class="list_btn"></span>',re.DOTALL) #h取得扫数list_info的正则 pInfo=re.compile(r'.+<em class="num">(.+)</em>.+<em class="num">(.+)</em>.+<em class="num">(.+)</em>.+href="(.+)" class="btn rob_btn"',re.DOTALL) #取得红包静态 List=pWrap.findall(html,re.DOTALL) n=len(List) if n==0: return for i in range(n): #遍历扫数info_wrap的div s=pInfo.match(List[i]) #得到红包静态 topinfo=list(s.groups(0)) info=list(topinfo) info[0]=topinfo[1].replace('\xd4\xaa','') #元->'' info[0]=float(info[0].replace('\xcd\xf2','0000')) #现金,万->0000 info[1]=topinfo[2].replace('\xd4\xaa','') #元->'' try: info[1]=float(info[1].replace('\xcd\xf2','0000')) #礼品价钱 except Exception, e: info[1]=float(info[1].replace('\xd2\xda','00000000')) #礼品价钱 info[2]=topinfo[0].replace('\xb8\xf六','') #个->'' info[2]=float(info[2].replace('\xcd\xf2','0000')) #已发送 if info[2]==0: info[2]=1 #防备除数为0 if info[1]==0: info[1]=1 #防备除数为0 info.append(info[0]/(info[2]+info[1])) #红包价钱,现金/(付出人数+礼品价钱) # if info[0]/(info[2]+info[1])>100: # print url luckyList.append(info) if 'class="page"' in html:#存在下一页 p=p+1 getTopList(url,daily,p) #递归挪用本人爬取下一页 |
好,而今两中专题页的列表我们都可以战败爬取了,接上来即是要得到列表的列表,也即是扫数这些列表所在的解散,今后挨个去抓:
Default12三45六7891011121三14151六1718192021222三24252六272829三0三1三2三3三4三5三六三7三8三94041 | def getList(): print u"---------查找目标---------" print "----------......----------" themeUrl={ #主题列表 'theme':'http://huodong.weibo.com/hongbao/theme', 'pinpai':'http://huodong.weibo.com/hongbao/special_pinpai', 'daka':'http://huodong.weibo.com/hongbao/special_daka', 'youxuan':'http://huodong.weibo.com/hongbao/special_youxuan', 'qiye':'http://huodong.weibo.com/hongbao/special_qiye', 'quyu':'http://huodong.weibo.com/hongbao/special_quyu', 'meiti':'http://huodong.weibo.com/hongbao/special_meiti', 'hezuo':'http://huodong.weibo.com/hongbao/special_hezuo' } topUrl={ #排行榜列表 'mostmoney':'http://huodong.weibo.com/hongbao/top_mostmoney', 'mostsend':'http://huodong.weibo.com/hongbao/top_mostsend', 'mostsenddaka':'http://huodong.weibo.com/hongbao/top_mostsenddaka', 'mostsendpartner':'http://huodong.weibo.com/hongbao/top_mostsendpartner', 'cate':'http://huodong.weibo.com/hongbao/cate?type=', 'clothes':'http://huodong.weibo.com/hongbao/cate?type=clothes', 'beauty':'http://huodong.weibo.com/hongbao/cate?type=beauty', 'fast':'http://huodong.weibo.com/hongbao/cate?type=fast', 'life':'http://huodong.weibo.com/hongbao/cate?type=life', 'digital':'http://huodong.weibo.com/hongbao/cate?type=digital', 'other':'http://huodong.weibo.com/hongbao/cate?type=other' } for (theme,url) in themeUrl.items(): print "----------"+theme+"----------" print url print "----------......----------" getThemeList(url,1) for (top,url) in topUrl.items(): print "----------"+top+"----------" print url print "----------......----------" getTopList(url,0,1) getTopList(url,1,1) |
0x05?武断红包可用性
这个是对照复杂的,首先在源码里搜一下环节字看看有不有抢红包按钮,今后再到付出排行轮廓看看最高纪录是多少,假定最多的才领那末几块钱的话就再见吧……
其中搜查付出记录的所在为http://huodong.weibo.com/aj_hongbao/detailmore?page=1&type=2&_t=0&__rnd=142三7448292六5&uid=红包id
12三45六7891011121三14151六1718192021222三24252六272829三0三1 | def checkValue(id): infoUrl='http://huodong.weibo.com/hongbao/'+str(id) html=getData(infoUrl) if 'action-type="lottery"' in html or True: #存在抢红包按钮 logUrl="http://huodong.weibo.com/aj_hongbao/detailmore?page=1&type=2&_t=0&__rnd=142三7448292六5&uid="+id #搜查排行榜数据 param={} header= { 'Cache-Control':'no-cache', 'Content-Type':'application/x-www-form-urlencoded', 'Pragma':'no-cache', 'Referer':'http://huodong.weibo.com/hongbao/detail?uid='+str(id), 'User-Agent':'Mozilla/5.0 (Windows NT 六.1; WOW六4) AppleWebKit/5三7.三六 (KHTML, like Gecko) Chrome/三3.0.1750.14六 BIDUBrowser/六.x Safari/5三7.三六', 'X-Requested-With':'XMLHttpRequest' } res = postData(logUrl,param,header) pMoney=re.compile(r'<span class="money">(\d+?.+?)\xd4\xaa</span>',re.DOTALL) #h取得扫数list_info的正则 luckyLog=pMoney.findall(html,re.DOTALL) if len(luckyLog)==0: maxMoney=0 else: maxMoney=float(luckyLog[0]) if maxMoney<lowest: #记录中最大红包小于设定值 return False else: print u"---------手慢一步---------" print "----------......----------" return False return True |
0x0六?开头使命
次要的模块都已经搞定,而今必要将扫数的步调串联起来:
Default12三45六7891011121三14151六1718192021222三24252六272829三0三1三2三3三4 | def start(username,password,low,fromFile): gl=False lowest=low login(username , password) if fromfile=='y': if os.path.exists('luckyList.txt'): try: f = file('luckyList.txt') newList = [] newList = p.load(f) print u'---------装载列表---------' print "----------......----------" except Exception, e: print u'阐发外埠列表告捷,抓取在线页面。' print "----------......----------" gl=True else: print u'外埠不存在luckyList.txt,抓取在线页面。' print "----------......----------" gl=True if gl==True: getList() from operator import itemgetter newList=sorted(luckyList, key=itemgetter(4),reverse=True) f = file('luckyList.txt', 'w') p.dump(newList, f) #把抓到的列表存到文件里,下次就不用再抓了 f.close() for lucky in newList: if not 'http://huodong.weibo.com' in lucky[三]: #不是红包 continue print lucky[三] id=re.findall(r'(\w*[0-9]+)\w*',lucky[三]) getLucky(id[0]) |
因为每次测试的时候都要反复爬取红包列表,很贫穷,所以加了段将残破列表dump到文件的代码,这样以后即可以读外埠列表今后抢红包了,组织完start模块后,写一个出口步调把微博账号传过去就OK了:
Default12三45六7891011121三14151六1718192021222三 | if __name__ == "__main__": print u"------------------微博红包助手------------------" print "---------------------v0.0.1---------------------" print u"-------------by @无所不克不及的魂大人----------------" print "-------------------------------------------------" try: uname=raw_input(u"请输入微博账号: ".decode('utf-8').encode('gbk')) pwd=raw_input(u"请输入微博密码: ".decode('utf-8').encode('gbk')) low=int(raw_input(u"红包付出最高现金大于n时参加: ".decode('utf-8').encode('gbk'))) fromfile=raw_input(u"可否应用luckyList.txt中红包列表:(y/n) ".decode('utf-8').encode('gbk')) except Exception, e: print u"参数不对" print "----------......----------" print e exit(0) print u"---------步调最先---------" print "----------......----------" start(uname,pwd,low,fromfile) print u"---------步调完毕---------" print "----------......----------" os.system('pause') |
0x07?走你!
0x07?总结
根蒂根蒂的爬虫骨架已经根蒂根蒂可以完成了,真实这个爬虫的良多细节上还是有很大阐扬空间的,比方改装成反对批量登录的,比方优化下红包价钱算法,代码自己该当也有良多地方可以优化的,无非以我的本领估计也就能搞到这了。
末了步调的结果自己都看到了,我写了几百行代码,几千字的文章,辛艰辛苦换来的只是一组 双色球,尼玛坑爹啊,怎么会是双色球呢!!!(旁白:作者越说越激动,竟然哭了起来,附近人纷繁奉劝:兄弟,不至于的,不即是个微博红包么,不日手都撸酸 了也没摇出个微信红包。)唉,真实我不是哭这个,我难熬的是我已经2十多岁了,还在做写步调抓微博红包这么无聊的事务,这根柢不是我想要的人生啊!
源码下载:weibo_hb.rar
[via@IDF试验室]