新博客地址:http://gorthon.sinaapp.com/
识别笔记一:http://blog.csdn.net/bh20077/article/details/7041280
识别笔记二:http://blog.csdn.net/bh20077/article/details/7041400
这一篇其实与验证码识别没有关系。
但是是由验证码识别引起的一些问题。
这个网站是一个电子书籍分享下载网站,但是有下载限制(每天每个用户只能下载10本),试了一下注册新的账号又可以下载10本,看来是没有对ip进行处理。
我本意是要下载python,C++以及css,html方面的书籍的,但是每天10本的限制我得下载到什么时候……
所以准备如下:
1. 网站自动注册
以下是注册界面:
可以看到站长做了个创新,需要填写C语言运行结果才能注册,这抵挡了相当一大部分普通用户注册,这是为何?试验结果表明这里刷新页面后可能出现C(居多)、Python(较多)以及JavaScript(很少)代码。而且几乎只用刷新5次你就能得到相同的代码…………
解决方法:
先准备一个用于进行get以及post请求的类:
class Net(object):
def __init__(self):
self.cookie = cookielib.LWPCookieJar()
self.opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(self.cookie))
self.opener.addheaders = [('User-agent', 'Mozilla/5.0 (Windows; U; Windows NT 5.1;\
zh-CN; rv:1.9.2.3) #Gecko/20100401 Firefox/3.6.3')]
urllib2.install_opener(self.opener)
def post(self, url, params):
return self.opener.open(urllib2.Request(url, urllib.urlencode(params))).read().decode('u8')
def get(self, url):
try:
return self.opener.open(urllib2.Request(url)).read().decode('u8')
except:
return self.opener.open(urllib2.Request(url)).read()
然后自动注册:
随机生成用户名:
def generateUserName():
table = string.letters + string.digits
return ''.join(random.sample(table, random.randint(10, 15)))
注册:
def register():
n = Net()
name = generateUserName()
pwd = name
url = 'http://www.和谐.com/register'
html = n.get(url)
cpp_code = re.findall('<pre class="brush:(\w+);.*?>(.*?)</pre>', html, re.S)[0]
language = cpp_code[0]
code = cpp_code[1]
code = HTMLParser().unescape(code)
if language == u'python':
answer = pythonAnswer(code)
elif language == u'c':
answer = cppAnswer(code)
else: # javascript
answer = 'true'
if answer == 'error':
print 'javascript'
register()
p = {
'loginname' : name,
'email' : '%s@df.com' % name,
'loginpass' : pwd,
'verifypass' : pwd,
'answer' : answer,
'submit' : '注册'
}
js = eval('u"'+eval(n.post(url, p).replace('true', 'True').replace('false', 'False'))['msg']+'"')
print js
if u'注册成功' in js:
#return name
file('./user-pwd.txt', 'a').write(name + '\n')
下面是得到python以及c代码的运行结果:(Linux下可以使用gcc代码cl)
def pythonAnswer(code):
try:
buf = StringIO()
tmp = sys.stdout
sys.stdout = buf
exec(code)
answer = buf.getvalue().strip()
except:
answer = 'error'
finally:
sys.stdout = tmp
return answer
def cppAnswer(code):
file('./tmp.cpp', 'w').write(code)
os.system('cl.exe tmp.cpp')
return Popen(['tmp.exe'], stdout=PIPE).communicate()[0].strip()
可以事先注册好多个用户,也可以下载过了10本的时候再注册:
for i in range(20): # 注册20个用户
register()
user-pwd.txt里面将会保存这20个注册成功的用户名
3. 抓包分析
用户登录:
def login():
url = 'http://www.和谐.com/login/'
p = {
'loginname' : name,
'loginpass' : pwd,
'remember' : 'on'
}
if u'退出' in n.post(url, p):
return True
分析要下载的资源的url以及get/post流程:
看到一个下载页面之后,中间有下载地址:
“点击获取下载地址”会提示需要输入验证码:
输入后如下:
至此得到下载地址,具体验证码获取地址可抓包得到。
4. 验证码识别
这个部分已经做好了,可以参见上面笔记一与笔记二的链接。
因为此网站验证码数量很少,而且大多是我们非常熟悉的单词,下面这章图列出了部分由笔记二识别的结果。可以看出仅仅一两百张图片就有好多重复,这个加个单词检查提高识别率,不过在我的试验过程中,效果已经不错,问题就交给电脑吧。
5. 下载:def download(book):
html = n.get(book)
ext = re.findall(u'文件格式:</span>(\w+?)</p>', html)[0]
size = re.findall(u'文件大小:</span>(.*?)</p>', html, re.S)[0]
print size
## if raw_input(u'继续吗:y/n') == 'n':
## return
filename = book.split('/')[-1][:-5] + '.' + ext
filename = urllib.unquote(str(filename))
try:
skey = re.findall('surl="/captcha\?skey=(.*?)"', html)[0]
except:
skey = 'dda032aaedd063e3360b159aef8d54ba'
url = 'http://www.和谐.com/captcha'
try:
png = n.get('http://www.和谐.com/captcha/?skey=%s' % skey)
except:
print u'验证码解析失败\n尝试重新获取中...'
download(book)
return
try:
post_skey = re.findall('name="skey" value="(.*?)"', html)[0]
except:
post_skey = 'UlcENQokUidVMwZtUw9UOwIgBmIOY1M3VjFRbFdvUDE='
file('./captcha.png', 'wb').write(png)
captcha = getCode('captcha.png')
print u'识别出的验证码为: ' + captcha
p = {'captcha' : captcha,
'skey' : post_skey
}
html = n.post(url, p)
msg = eval('u"'+eval(html)['msg']+'"')
if u'为节省服务器资源' in msg:
print msg
return msg
if not 'data' in html:
print u'验证码识别错误\n正在重新获取验证码...\n'
download(book)
return
ftp_url = eval(html)['data'].replace('\/', '/')
print u'验证码正确'
print u'下载地址为:' + ftp_url
print filename
try:
file(filename.decode('u8').encode('gbk'), 'wb').write(n.get(ftp_url))
except:
file("unknow" + '.%s' % ext, 'wb').write(n.get(ftp_url))
print u'下载成功'
time.sleep(999)
return ftp_url
def getNextUser():
for line in file('user-pwd.txt'):
name = line.strip()
print name
yield name
运行:
def main():
for line in file('url.txt'):
if not line.startswith('http://'):
continue
name = getNextUser().next()
print name
pwd = name
global n
n = Net(name, pwd)
if not login():
print u'登录失败'
continue
print u'登录成功'
url = line.strip()
thread.start_new_thread(download, (url, ))
time.sleep(2)
if __name__ == '__main__':
main()
while True:
time.sleep(.33)
超过10本时:
换用户名之后正确下载(可以加上自动换用户名,懒得弄了……):