zhendeaini123 发表于 2015-4-23 10:06:42

Python刷票小脚本——网络人气奖?不好意思,我要了

零、前言
  最近参加微软的kinect大赛,报名之后发现有一个网络投票,票数最多的项目可以得到网络人气奖。
  这种事,必然是要搞一搞!
  说干就干。
  
  
  
  说明:由于本人过于懒惰,所以就不截图了,让大家失望了!
  重点看一下思想就可以了。
  
  

一、探查敌情
  第一步先搞清楚投票的具体流程以及可能的限制条件。
  经过研究之后,总结如下:


[*]投票需要登录
[*]注册帐号需要验证邮箱
[*]登录不需要验证码
[*]每个帐号一天可以投一次票,票数可以选择,从0~10
[*]投票不需要验证码
  
  注意到红字了是吧,这就是最关键的地方了。
  
  好了,我们的初步思路就出来了:
  手动注册好帐号——代码模拟用户登录——登录之后进行投票
  
  思路出来了,下面就是工具的选择。
  

二、工欲利其事。。。
  语言嘛必然是我爱的Python。
  工具的话,之前其实做过模拟登录,简单来说就是用一个模拟浏览器的Python插件,然后进行各种模拟用户操作,比如点击按钮啊输入信息啊之类的。
  但是这类插件主要有两大问题:


[*]headless的很少
[*]基本上对于HTML页面的操作只能应用于form
  
  headless是什么呢,可以简单理解为后台操作。如果做不到headless,那么你运行的时候其实还是需要打开一个浏览器,只不过脚本会操作浏览器。
  所以我们可以看到,做不到headless的话,不仅看起来非常低端(想象一下电脑屏幕上开着一个浏览器,然后自动输入东西,而你之只能傻坐着什么都不能做),并且使用起来很不方便,比如在没有图形界面的系统就无法使用了。
  
  只能应用于form是什么意思呢,我们拿jQuery来对比吧。jQuery可以选择所有HTML里出现的东西,但是只能应用于form就意味着只能操作表单,对于其他元素就无能为力了。我不知道为什么会出现这种情况,可能是插件底层有一些限制吧,反正大部分插件都只能操作form。
  
  给大家提供三个方案以供参考吧:
  


[*]Ghost.py 支持headless并且可以操作所有元素,甚至可以运行js,你就知道这个有多强大了。唯一缺点——依赖PyQt或者PySide,你装一下这俩货就知道了,简直折磨死人。所以如果你不想折腾的话,还是不要用这个了。
[*]splinter半支持headless——splinter默认是不支持headless,但是可以在使用zope的一个插件的前提下headless,因为我用的是默认的,所以具体怎么做到headless没有研究,感兴趣的自己搞搞吧。不过估计八成不给力。。。splinter的功能同样很强大,可以操作所有元素。
[*]mechanize支持headless但是只能操作form
  
  大概就是这样。
  

三、Just Do It
  重复一下我们的思路:
  登录——投票
  因为我决定采用headless的方案,所以就使用mechanize。
  登录的话没问题,登录框本来就是form。投票嘛。。。先放着!我们先把登录搞定。
  
  直接上代码:



# coding:utf-8
import cookielib
import mechanize
import urllib
br = mechanize.Browser()
cj = cookielib.LWPCookieJar()
br.set_cookiejar(cj)
br.open('http://k4w.cn/user/index.html')
br.select_form(nr=0)
br.form['mail'] = 'xxxx@xxx.com'
br.form['password'] = 'xxxxxxx'
br.submit()
  cookielib是用来操作cookie的。因为我们登录之后需要跳转到投票页面,如果我们不保留cookie,那么网站就会把我们当做未登录——别忘了,你现在是用代码在模拟登录,所以不要以为他会自动给你保存cookie。
  
  代码很简单吧,我就不解释了,总之,打开页面——输入用户名密码——提交
  
  可以输出一下结果看看:



.....同上.....
response = br.submit()
print response.read()
  我们可以看到输出的HTML中有“xxx,欢迎你”这样的字样,说明已经成功登录了
  
  下面就是重头戏了——如何投票。
  
  先具体化投票的操作:
  从下拉列表里选择“10”,然后点击确定。
  我们已经知道,mechanize只能操作form,对于其他元素是无能为力的,所以我们不能直接来模拟人的操作。
  那么该怎么办呢?
  
  大家先思考5秒钟
  
  
  
  。。。。。。。。
  
  
  。。。。。
  
  
  。。。
  
  
  。
  
  
  好吧我知道你直接翻下来了。
  我当时可是思考了半天才想出来的啊!!!
  
  我们可以换个思路,投票,表面上是人的操作,但是最终发送给服务器的其实是一个POST请求!所以,我们可以跳过模拟操作,直接发送请求!
  
  好的,这下思路清晰了。我们先——等等,我们POST什么东西?
  投票啊,告诉服务器我们投票了。
  但是代码是一个很严谨的东西,格式不对的话服务器是不认的!
  好吧,这次不思考了,直接告诉你答案吧。
  
  我们先手动投一次票,然后查看POST请求中的数据格式。
  
  我用的是firebug,打开firebug,然后选择票数,按确定按钮,可以看到firebug中出来了这次POST请求的具体信息。
  我们点开信息,可以看到数据的格式:



z_data : 10
id : 99
sid : 78
  一下就看出来了嘛!
  z_data是票数,id是项目编号,sid。。。好吧我不知道这是什么。总之就写78好了。
  
  数据格式获取到了,下面我们回到代码中,模拟一个POST请求:



parameters = {'z_data' : '10',
'id' : '99',
'sid' : '78'
}            #POST data
data = urllib.urlencode(parameters)
response = br.open('http://k4w.cn/zone/z_num.html', data)
  很简单吧?
  别忘了import urllib!
  
  好了,我们和前面的代码组合起来实验一下,看看效果。
  发现票数确实增加了,我们的方法是可行的。
  
  然后呢,我们改一下,加个for循环,这样就可以按照我们设定好的用户名密码,自动登录所有用户并投票。
  
  基本功能就是这样了,但是使用了几天之后发现了一个不爽的地方:投票完要想查看票数还得手动打开网页。如果能直接显示出来当前票数就好了!
  
  于是我们继续踏上征程。
  

四、迭代好吃吗
  首先还是思路:
  打开项目页面——获取票数——显示
  
  打开页面我们已经会了,br.open()就行。显示也很简单,print。那么怎么获取票数呢?
  给大家介绍一个新工具——BeautifulSoup,靓汤!
  我承认名字有点。。。。
  不管了,继续我们编程之路。
  
  靓汤是一个解析HTML的插件,介绍完毕。
  
  我们可以把获取到的HTML用靓汤解析一下,然后找到我们需要的票数对应的那个元素,就可以获取票数了。
  很简单是吧!我们把HTML传入靓汤。。。。
  我靠怎么出错了!
  Google了半天,原来是HTML中有不规范的标签,就解析失败了。
  微软的页面原来也不符合标准。。。
  
  好吧,解析不了,怎么办呢?
  
  有人给出了解决办法:用lxml。
  
  lxml又是个什么东西?lxml是解析xml的一个插件,但是可以解析HTML并且,注意啊,并且可以忽略不规范的标签。
  正好是我们需要的!
  OK,照着官方文档使用一下~
  亮代码:



br = mechanize.Browser()
response = br.open('http://k4w.cn/level_search/1/78/0/0/0.html')
page = etree.HTML(response.read().lower().decode('utf-8'))
hrefs = page.xpath(u"//span[@class='number n_99']")
print "当前票数:" + hrefs.text
  依然是很简单不解释,看看就明白了。
  
  好的,这样我们整个刷票脚本就完工了~~
  
  所有代码来个合影



# coding:utf-8
import cookielib
import mechanize
import urllib
from lxml import etree
all_data = [['username1', 'password1'], ['username2', 'password2']]
for i in all_data:
br = mechanize.Browser()
cj = cookielib.LWPCookieJar()
br.set_cookiejar(cj)
br.open('http://k4w.cn/user/index.html')
br.select_form(nr=0)
br.form['mail'] = i
br.form['password'] = i
br.submit()
response = br.open('http://k4w.cn/level_search/1/78/0/0/0.html')
parameters = {'z_data' : '10',
'id' : '99',
'sid' : '78'
}            #POST data
data = urllib.urlencode(parameters)
response = br.open('http://k4w.cn/zone/z_num.html', data)
print "%s 投票成功!" % i
br = mechanize.Browser()
response = br.open('http://k4w.cn/level_search/1/78/0/0/0.html')
page = etree.HTML(response.read().lower().decode('utf-8'))
hrefs = page.xpath(u"//span[@class='number n_99']")
print "当前票数:" + hrefs.text
  搞定。
  

五、总结
  我们使用了:


[*]mechanize模拟用户操作模拟POST请求
[*]firebug获取POST数据格式
[*]lxml解析HTML内容
  
  我们实现了:


[*]自动登录
[*]自动投票
[*]输出票数
  
  好了,下面只要把这个脚本放到服务器上,并且设置一下每天运行就可以了。
页: [1]
查看完整版本: Python刷票小脚本——网络人气奖?不好意思,我要了