前段时间用urllib, urllib2, cookielib实现了自动登录校园网的脚本,类似发送请求的原理同样可以用在小木虫等类似的这种用PHP+MySQL实现的discuz论坛上面。于是就动手开始写了。
写这个脚本的最初目的是懒得每天手动到小木虫网站上点击领取金币了,而且我这种经常忘记这种事情,等要用到金币的时候就后悔莫及了,那干脆用python写一个开机自动领取金币的脚本更省事。
对了这个脚本实际上是上个登录校园网的一个分支,也就是LoggerBase的一个子类,一开始采用OO的方式果然是对的。。。
看了下小木虫的领取金币的页面的代码,获取小木虫金币的方式是通过POST方式向http://emuch.net/bbs/memcp.php?action=getcredit传表单信息。于是原理就可以是通过python向这个页面发送表单信息达到自动领取金币的效果。

表单信息:

1
2
getmode = 1
creditsubmit = 领取红包

要获取金币之前必须要做的就是登录小木虫论坛了,原理也是向表单处理页面(http://emuch.net/bbs/logging.php?action=login)发送请求.表单信息可以是:

1
2
3
4
username = vanillasmile
password = ******
cookietime = 31536000
loginsubmit = 会员登录

在这里和登录校园网不同的是,登录小木虫有两个更多的信息要处理,一个是页面的formhash,这个应该是个改变的POST变量,虽然我在写这个脚本的时候这个变量一直没有变,但是后面写成之后使用脚本登录的时候我每次会记录formhash的值,发现这个是还是会变的,但是不知道是间隔多长时间变一次,于是就通过python抽取每次页面的formhash的值,然后再传递。获取HTML标签内容一开始我使用的是BeautifulSoup模块:

1
2
3
4
5
6
7
8
def get_hash_code_BSoup(hash_name, url):
login_page = urllib.urlopen(url).read()
login_soup = BeautifulSoup(login_page)
formhash_tag = login_soup.find('input',attrs = {'name':hash_name})
if formhash_tag:
return formhash_tag['value']
else:
return

但后来吧代码发给别人用之后才发现BeatifulSoup不是python的标准库,在让其他人安装的话会很麻烦,于是干脆自己写了个获取formhash的方法:

1
2
3
4
5
6
7
8
9
@staticmethod
def get_hash_code(tag_name, response):
hash_regex = r'(<input.+name=")(' + tag_name + r')(" value=")([\d\w]+)(">)'
m = re.search(hash_regex, response)
if m:
#retrun tag_name, hash_code
return m.group(2), m.group(4)
else:
return

获取了这个信息就可以完成登录的第一步了,登录方法和之前登录校园网的方法相同, 设置cookie,发送请求,获取请求返回内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def post_with_cookie(self):
"post data with cookie setting, return page string and cookie tuple"
#get formhash
url_login = self.url_login
response = urllib2.urlopen(url_login).read()
formhash = self.get_hash_code('formhash', response)
#update form_data_dict
self.add_form_data({'formhash':formhash})
#set cookie
cj = cookielib.CookieJar()
form_data = self.form_data_dict
try:
opener=urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))
urllib2.install_opener(opener)
req=urllib2.Request(url_login,urllib.urlencode(form_data))
u=urllib2.urlopen(req)
cookie_list = []
for index, cookie in enumerate(cj):
cookie_list.append(cookie)
return u.read(), tuple(cookie_list)
except:
print "Ooops! Failed to log in !>_< there may be a problem."
return

第二个不同就是完成上面登录过程后会有小木虫的验证问题,感到幸运的是,小木虫不是用随即文字或者数字图片的验证码方式,而是通过计算并回答数学问题获取验证信息的,这个就太好办了,那就先从返回的页面中抽取问题,然后交给python计算一下,再自动将验证信息添加到form_data中最后一起发送实现登录。
写代码的过程中发现,其实小木虫只会计算乘法和除法,而且问题都是3、4个问题来回重复。。。
贴上登录小木虫的方法代码:

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
def log_in(self):
"""
Method to pass values by POST 2 times to log in emuch.net,
return cookie tuple.
"""
num1, num2, operation = 0, 0, ''
qustion_regex = r'(\xce\xca\xcc\xe2\xa3\xba)(\d+)(.+)(\d+)'+\
r'(\xb5\xc8\xd3\xda\xb6\xe0\xc9\xd9?)'
while not (num1 and num2 and operation):
response = self.post_with_cookie()[0]
match_obj = re.search(qustion_regex, response)
#get question parts
try:
num1, num2, operation = match_obj.group(2), match_obj.group(4),\
match_obj.group(3)
#return num1, num2, operation
except:
#print "failed to get question"
#time.sleep(6)
pass
#further log in
#calculate verify question
#division
if operation == '\xb3\xfd\xd2\xd4':
answer = str(int(num1) / int(num2))
#multiplication
if operation == '\xb3\xcb\xd2\xd4':
answer = str(int(num1) * int(num2))
#get formhash value
formhash = self.get_hash_code('formhash',response)[1]
#get post_sec_hash value
post_sec_hash = self.get_hash_code('post_sec_hash',response)[1]
#update form_data_dict
self.add_form_data({'formhash':formhash,
'post_sec_code':answer,
'post_sec_hash':post_sec_hash
})
#login_response = self.post_with_cookie()
cookies_tup = self.post_with_cookie()[1]
return cookies_tup

登录成功以后终于可以去实现最初目的了,获取金币(实现自动登录真的是没什么用处啊喂)。
获取金币也需要获取formhash的值,那就重复用get_hash_code()就好了,贴上代码:

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
def get_credit(self):
"""get today's credit,
if get, return page content, else return 'have_got' and credit_num
"""
#get formhash value
req_1 = urllib2.Request(self.credit_url,
urllib.urlencode({'getmode':'1'}))
response_1 = urllib2.urlopen(req_1).read()
if self.get_hash_code('formhash', response_1):
formhash = self.get_hash_code('formhash', response_1)[1]
#formhash = self.get_hash_code_BSoup('formhash', credit_url)
credit_form_data = {'getmode':'1', 'creditsubmit':'领取红包'}
credit_form_data['formhash'] = formhash
setattr(self, 'credit_form_data', credit_form_data)
#post values to get credit
data = urllib.urlencode(credit_form_data)
req_2 = urllib2.Request(self.credit_url, data)
response_2 = urllib2.urlopen(req_2).read()
if response_2:
credit_num = self.get_credit_number(response_2)
self.log(event='get_credit_succeed', credit_num=credit_num)
return response_2
else:
#print 'got!'
credit_num = self.get_credit_number(
self.send_post(self.credit_url,self.form_data_dict))
self.log(event='get_credit_fail', credit_num=credit_num)
return 'have_got', credit_num

其中如果用户已经领取了今天的金币,那么便不会再返回获取金币的部分,也就是没有form部分了,我是通过判断此页面是否还会有formhash的值来判断用户是否已经领取金币。为了方便后面单独的执行脚本输出相应的信息,这个直接返回的特定字符串提供给后面的代码进行判断。上面这段代码中有一个get_credit_number()方法,没错我多写了个方法获取用户当前金币数,原理还是抓取页面,正则表达式匹配:

1
2
3
4
5
6
7
8
9
10
11
12
@staticmethod
def get_credit_number(response):
#regex = r'(<u>\xbd\xf0\xb1\xd2: )(\d\d\.\d)(</u>)'
regex_float = r'(<u>\xbd\xf0\xb1\xd2: )(\d*\.\d+)(</u>)'
regex_int = r'(<u>\xbd\xf0\xb1\xd2: )(\d*)(</u>)'
m_float = re.search(regex_float, response)
m_int = re.search(regex_int, response)
if m_float:
credit_num_str = m_float.group(2)
if m_int:
credit_num_str = m_int.group(2)
return credit_num_str

最后我还是单独写了个”run_credit.py”脚本给别人直接执行使用,自动登录小木虫并获取金币。其中在后面同学用这个脚本的时候向我反映开机如果没联网会不再自动登录小木虫,于是我又在run_credit.py里面添加了检测网络连接的部分,其中包括检测是否连上无线网但是没登录以及是否联网。若连接网络失败,窗口不会消失,会不断提示用户联网,等待联网成功后自动登录。
emuch()函数会返回字符串信号用来判断。

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
def emuch(emuch_logger):
url = emuch_logger.url_login
#chk internet connection
try:
resp = urllib2.urlopen(url)
except:
return 'no_internet'
title_1 = emuch_logger.get_page_title(url)
try:
title_2 = emuch_logger.get_page_title('http://www.weibo.com/')
except:
title_2 = None
try:
title_3 = emuch_logger.get_page_title('http://www.baidu.com/')
except:
title_3 = None
if title_1 == title_2 or title_1 == title_3:
return 'need_login'
else:
cookies = emuch_logger.log_in()
#get credit
response = emuch_logger.get_credit()
#check if there is a formhash tag
if 'have_got' in response:
print 'You\'ve got today\'s coin~'
print 'Current credit number : %s' % str(response[1])
return
else:
credit_num = emuch_logger.get_credit_number(response)
print "Today's credit -> get!"
print 'Current credit number : %s' % str(credit_num)
return

后面我还添加的半夜自动下载小木虫付费资源的部分,在后面的文章中进行简单介绍。

Comments