新浪微博验证码的破解方法【代码】


有次帮某人发spam,找人来填调查。写了个自动评论的脚本,但是发出一定数量的评论之后就会遭遇验证码,于是决定破解之。
思路也是一般的转化切割比对,成功率不是很高,不过重试几次也是可以用的。
虽然已经控制好了频率,不过最后还是被管理员发现了,直接封了帐号(好在是临时注册的),再注册再封,后来干脆封IP,

于是不得不给我的vps换了个ip(也好在是免费的),杯具。
闲话休说,言归正题。

首先是需要取得验证码的样本,以作训练特征之用。而要取得验证码,首先要模拟登录的请求:

usr = 'xx' psw = 'oo' 
resp = urllib2.urlopen('https://login.sina.com.cn/sso/login.php?username=%s&password=%s&returntype=TEXT' %
 ( usr, psw)) cookie = Cookie.SimpleCookie(resp.headers['set-cookie']) headers = { 'Referer': 'http://t.sina.com.cn', 'Cookie': cookie_header(cookie),
'User-Agent': 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.3pre) Gecko/20100405 Firefox/3.6.3plugin1', } def cookie_header(cookie):
ret = '' for v in cookie.values(): ret += "%s=%s; " % (v.key, v.value) return ret

headers就是后续的请求中,需要传递的参数了。
取回一些验证码样本:

for i in xrange(100):  
req_img = urllib2.Request('http://t.sina.com.cn/pincode/pin.php?lang=zh&r=%d&rule' % int(time() * 1000),
headers = headers) res_img = urllib2.urlopen(req_img) f = open('xinlang_pincode/%d.png' % i, 'wb') f.write(res_img.read()) f.close()

有一些验证码的回答是中文,中国首都什么的,这些不处理,直接返回失败。因为可以重复获取重新识别,不成问题的。下面是处理算术问题验证码的方法:
先进行变换处理:

from PIL import Image, ImageFilter, ImageEnhance file = 'xinlang_pincode/0.png'   im = Image.open(file) im = im.convert()   
enhancer = ImageEnhance.Brightness(im) im = enhancer.enhance(2.0) #加亮,效果见图1 enhancer = ImageEnhance.Contrast(im) im = enhancer.enhance(4)
#提高对比度,效果见图2 im = im.convert('1') #二值化,效果见图3 im = im.filter(ImageFilter.MedianFilter) #中值去噪,效果见图4 im.show()
#调用xv命令来显示图片,方便debug

图1:

图2:

图3:

图4:


这样处理过之后,图片背景中的色块被过滤掉,杂点也被过滤掉,而数字的形状也没有太大的损失。

下面是分解字符,也就是将每一个数字或者+-*等符号分解出来:

imim = im.load() WIDTH = 250 HEIGHT = 50   i = 0 has_start = False chars = [] while i < WIDTH:   all_none = True   for j in xrange(HEIGHT):     if imim[i, j] != 255:  
all_none = False if all_none: if has_start: end_x = i has_start = False char = im.crop((start_x, 0, end_x, HEIGHT))
char.show() #到这一步的效果见图5   charchar = char.load() width = end_x - start_x y1 = 0 y2 = HEIGHT - 1 all_none = True
while all_none: for ii in xrange(width): if charchar[ii, y1] != 255: all_none = False y1 += 1 all_none = True while all_none:
for ii in xrange(width): if charchar[ii, y2] != 255: all_none = False y2 -= 1 char = char.crop((0, y1 - 1, width, y2 + 2))
char = char.resize((20, 20)) #将图片缩放到统一的大小 char.show() #到这一步的效果见图6   chars.append(char) else: if not has_start:
start_x = i has_start = True i += 1

图5:字符被独立分割开

图6:字符上下两边的空白被去掉,且缩放到同一大小


这一步得到的chars是下面要用到的。

然后是训练,也就是形成特征库。特征库规模越大,识别率也越高。不过训练起来也挺累的,有几十上百条也就好了。至少0到9和+-*=等几个字符的特征都要有:
file = open('xinlang.img', 'a') for c in chars:   nstr = ''   im_loaded = c.load()   for x in range(20):     for y in range(20):       if im_loaded[x, y] == 255:      
nstr += '0' else: nstr += '1' c.show() n = raw_input('? ') file.write(nstr+':'+n+'\n') file.close()

这里的特征,就是直接把每一个像素的信息,用0和1组成的字符串表示。
训练的结果是一个文本文件,记录了对应的特征和字符,用于下面的比对。

比对函数:

pattern = [] for l in open('xinlang.img', 'r').read().split('\n'):   pattern.append(l.split(':')) del pattern[-1]   def what(img):   im = img.load()   nstr = ''  
for x in xrange(20): #生成目标图像的特征字符串 for y in xrange(20): if im[x, y] == 255: nstr += '0' else: nstr += '1' minmin = 400
res = None for p in pattern: cur = 0 for i in xrange(400):
if nstr[i] != p[0][i]: #比对每一个像素,如果不相同,则增加差异值 cur += 1 if cur < = minmin: #记录下差异值最小时所对应的字符
minmin = cur res = p[1] return res

最后测试一下:
for c in chars:   print what(c),

结果:

可以看到18+18这些字符可以成功识别。那为什么=和?识别不了呢?因为我没有训练这两个字符,而=和?都和数字2的特征最接近 -_,-|

这个验证码还是挺好破解的,因为字符之间间距很大,而且没有旋转,没有扭曲,不需要多少变换就能得到可用的结果。像google的那种,就完全没法可想了。

原文地址:https://www.cnblogs.com/goody9807/p/2111636.html