常见的几种验证码
图形验证码
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))
灰度图片
二值图片
设置阈值的目的就是,灰度大于阈值的像素点,其灰度直接设为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)
点触验证码
图中有各种字样,或者跟据文字点击对应图片。
可用付费识别服务。
四宫格验证码
保存四宫格的所有形状,匹配相应的拖拽方法。