我要爬爬虫(12)-识别验证码

常见的几种验证码

图形验证码

安装tesserocr之后
验证码

import tesserocr
from PIL import Image
image = Image.open('origin.png') #打开一个图片对象
text = tesserocr.image_to_text(image) #从图片中识别文字

结果:6mg
因为图中的线条会干扰识别结果,所以需要对图片进行二值化处理。

image = image.convert('L') #首先转成灰度图片
threshold=152
table=[]
for i in range(256):
    if i>threshold:
        table.append(1)
    else:
        table.append(0)
image = image.point(table, '1')#根据生成的table把图片二值化
print(tesserocr.image_to_text(image))

灰度图片
灰度
二值图片
binary
设置阈值的目的就是,灰度大于阈值的像素点,其灰度直接设为255(白),反之为0(黑)。
这里的阈值152是手动一个一个试出来的,终于使识别结果和人眼识别一致。
结果:6M2Q

极验验证码

就是常见的滑块拼图,思路就是取得拼图完成画和拼图未完成画两张图片,然后比较两张图片不同的像素点,进而确定拼图的起始位置和结束位置,最后实现拖拽。
先初始化一个WebDriverWait对象,用来定位元素。

from selenium.webdriver.support.wait import WebDriverWait
wait = WebDriverWait(browser,2) #等待时间为2s

这里根据网站的设置,只有输错邮箱,才会出现滑块验证码。所以定位输入栏,输入,定位登录按钮,点击按钮。
expected_conditions等待条件
presence_of_element_located元素是否存在
element_to_be_clickable元素是否可点击
By查找元素的方法
By.CSS_SELECTOR 按css来定位

from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
mail = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '.input-box #email')))
mail.send_keys('a')
button = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, '.geetest_btn')))
button.click()

接下来是获得两张验证图片。
首先是缺省的拼图,即需要操作的拼图

image = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '.geetest_window'))) #定位图片元素
location = image.location #获得图片位置,即左上角的坐标
x = location['x']
y = location['y']
print(x,y)
size = image.size #获得图片大小
width,length = size['width'],size['height']
print(size)
left,right,top,bottom = x,x+width,y,y+length #算出图片四角坐标
print(left,right,top,bottom)
screenshot = browser.get_screenshot_as_png()# 整个浏览器截图
screenshot = Image.open(BytesIO(screenshot)) #以二进制数据的格式写成图片
captcha = screenshot.crop((left,top,right,bottom))#按四角坐标截图,注意是双括号
captcha.save('captcha.png') #保存图片

这样就得到了缺省拼图
完整
为了获得完整的拼图,需要把验证图片的display属性从none改为block
可以直接通过浏览器对象执行js语句。

browser.execute_script('document.getElementsByClassName("geetest_canvas_fullbg")[0].style.display = "block"')

完整拼图会显示出来,再截一次即可。
完整拼图

完整
如果获得的图片大小和看起来有出入,可把browser调为headless模式。
接下来比较两张图片的每一个像素点,找到起始位置。

    for i in range(width):
        bb=True
        #60为设定的一个阈值,cmyk四个值的差距均在60以内,则判断像素点相同。
        for j in range(length):
            if not (abs(origin.load()[i,j][0]-captcha.load()[i,j][0])<60 and
                    abs(origin.load()[i,j][1]-captcha.load()[i,j][1])<60 and
                    abs(origin.load()[i,j][2]-captcha.load()[i,j][2])<60 and
                    abs(origin.load()[i,j][3]-captcha.load()[i,j][3])<60):
                start_x,start_y = i,j
                bb=False
                break
        if bb == False:
            break

返回拼图块的起始位置的坐标

而在起始位置之后,即为缺省块,所以有一连串的像素点是不同的,需要跳过这一串不相同的点,跳过中间的相同点,才得到结束位置。

diff=[]
    for i in range(width):
        bb=True
        for j in range(length):
            if not (abs(origin.load()[i,j][0]-captcha.load()[i,j][0])<60 and
                    abs(origin.load()[i,j][1]-captcha.load()[i,j][1])<60 and
                    abs(origin.load()[i,j][2]-captcha.load()[i,j][2])<60 and
                    abs(origin.load()[i,j][3]-captcha.load()[i,j][3])<60):
                diff.append(i)
    print(set(diff))
{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, 47, 48, 49, 50, 51, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142}

这里输出所有比较不同的像素点的横坐标,可以看看到从51到98有一段较大的空隙,可以判断51即为缺省块的右边缘横坐标。通过几次观察,缺省块的右边缘没有大于60的,所以从60开始寻找结束位置比较合理。

end_x=0
    for i in range(60,width):
        if not (abs(origin.load()[i, start_y][0] - captcha.load()[i, start_y][0]) < 60 and \
                abs(origin.load()[i, start_y][1] - captcha.load()[i, start_y][1]) < 60 and \
                abs(origin.load()[i, start_y][2] - captcha.load()[i, start_y][2]) < 60 and \
                abs(origin.load()[i, start_y][3] - captcha.load()[i, start_y][3]) < 60):
            end_x = i
            break

确定了起始位置,结束位置,就可以滑动滑块了,webdriver可以实现按住按钮并拖动。
由于极验验证有识别机器操作的功能,如果直接匀速拖动的话,它会识别出并非人工操作。所以需要刻意的变速拖动。极验的最小识别匀速单位是0.2s,所以0.2s以内匀速是允许的,即每0.2s变速一次。时间差也不能设的太小,那样会导致步数超大,存储困难。
这里设计前半段以4pixel/s的加速度拖动,后半段以-1pixel/s的加速度拖动。
别忘了加速度运动公式 L=v0*t+a*t*t/2

 distance=end_x-start_x
 track=[]
 current=0
 mid=distance/2
 t=0
 v=0
 t = 0.2
 a1=4
 a2=1
 #move为每一步即每0.2s的拖动距离
 while current<distance:
     if current<mid:
         v+=a1*t
         move = v * t + t * t * a1/2
     else:
         v-=a2*t
         move=v*t-t*t*a2/2
     #最后一步判断一下别走过了
     move= (distance-current) if move >(distance-current) else move
     move=round(move) #浮点数四舍五入
     current+=move
     track.append(move) #每一步的距离加入列表中

然后定位滑块元素,点击并拖动。

button = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR,'.geetest_slider_button'))) 
ActionChains(browser).click_and_hold(button).perform() #点击并按住
num=0
for x in track:
    ActionChains(browser).move_by_offset(xoffset=x,yoffset=0).perform()
time.sleep(0.5) #拖完了停0.5秒
ActionChains(browser).release().perform()#松掉点击

然而实际上,还是会偶尔出现失败,例如拖得不准或者被识别出是程序。
所以需要多试几次,直到提示成功的元素出现才停止。

crawl()
    while True:
        try:
            success = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,'.geetest_success')))
            break
        except TimeoutException:
            print('失败')
            crawl()
            time.sleep(2)

点触验证码

图中有各种字样,或者跟据文字点击对应图片。
可用付费识别服务。

四宫格验证码

保存四宫格的所有形状,匹配相应的拖拽方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值