新博客地址: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本时:
换用户名之后正确下载(可以加上自动换用户名,懒得弄了……):