【学习笔记】----只做个人记录,以下内容中极验验证码部分实用性不大
- 利用OCR技术识别图形验证码
- 使用到tesserocr库,需要下载安装
- 获取验证码的方式这里采用本地保存的方式,比较简便
识别图形验证码
- 识别测试代码:
import tesseror
from PIL import Image
image = Image.open('code.jpg')
result = tesseror.image_to_text(image)
# 另一个方法: tesseror.file_t_text('code.jpg') 效果没那么好
print(result)
-
然而有时候识别并不是准确的,因此还需要将图片做额外的处理
- 例如:转灰度、二值化等操作。。。
- 二值化处理需要先将图片转灰度
- 例如:转灰度、二值化等操作。。。
-
代码
image = image.convert('L') # 传入参数L->转灰度
image = image.convert('1') # 传入参数1->二值化处理(默认阈值127)
还能确定阈值
# 转灰度
image = image.convert('L')
# 阈值设置
threhold = 150
# 还是需要根据图片来挑的可能,书中使用80,但缺失了一半内容
table = [0 if i < threhold else 1 for i in range(256) ]
image = image.point(table, '1')
image.show()
接下来只要使用一行代码就可以识别啦!
print(tesseror.image_to_text(image))
由于使用的python是3.8版本的,没有对应的包,便使用另外的tesseract代替了,搞了好久才搞明白。咳咳。以下是实验中的代码
from PIL import Image
import pytesseract
pytesseract.pytesseract.tesseract_cmd = r'C:\Program Files\Tesseract-OCR\tesseract.exe'
image = Image.open('picture/code.jpg')
# 另一个方法: tesseror.file_t_text('code.jpg') 效果没那么好
# 转灰度
image = image.convert('L')
# 阈值设置
threhold = 107 # 可变阈值
table = [0 if i < threhold else 1 for i in range(256) ] # 固定 256
image = image.point(table, '1')
image.show()
result = pytesseract.image_to_string(image)
print(result)
利用这个办法似乎还是无法很准确的将其识别出来。调了许多次参数,但没有正确识别bgll
极验验证(该部分代码只用来记录思路,代码不可用)
《Python3网络爬虫开发实战》中的一些内容似乎有些过时了,滑动验证码并没有提供缺口不存在的原图片。因此这个方法找不到对应的缺口位置。 这里也简单学习记录一下
==注:==现在的验证码图片都是由一小块一小块组成的,需要将所有图片的url收集下载并且重组才能得到最终的完整图片。而且这些url并没有在源代码中直接呈现出来T_T
-
验证方式:滑动图片,使得图像完成即成功,失败得重来。
-
特点:
- 多次验证:
- 首先点击智能验证
- 不通过就要进行滑动验证
- 最后形成三个加密参数形成表单提交后台进行验证
- 还增加了机器学习方法识别拖动轨迹
- 防模拟
- 防伪造
- 防暴力
- 安全性和易用性高
- 多次验证:
-
完成验证需要的3步
class CrackGeetest(object): def __init__(self): self.url = 'xxxx' self.browser = webdriver.Chrome() self.wait = WebDriverWait(self.browser, 20) self.phoneNumber = phoneNumber self.pwd = pwd
-
点击模拟验证按钮
- 这个使用selenium可以做到
button = self.wait.until(EC.element_to_be_clickable((By.XPATH, "//xxx"))) button.click()
-
识别模拟滑动缺口的位置
- 获取验证码图片位置 -> 截图1(原图) -> 点击验证呼出缺口 -> 截图2(带缺口)
""" 这个方式是获取位置的 """ def get_position(self): # 获取图片信息 img = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'geetest_canvas_img'))) time.sleep(2) location = img.location size = img.size top, bottom, left, right = location['y'], location['y'] + size['height'],location['x'],location['x'] + size['width'] return (top, bottom, left, right) # 返回坐标元组
""" 这个方式是截图的 """ def get_screenshot(self): screenshot = self.browser.get_screenshot_as_png() # 网页截图 screenshot = Image.open(BytesIO(screenshot)) return screenshot
""" 接着使用crop()进行裁剪,并且保存 """ capt_ = screenshot.crop((left, top, right, bottom)) capt_.save('path/name')
""" 呼出缺口 """ def get_slider(self): """ 获取滑块 :return: 滑块对象 """ slider = self.wait.until(EC.element_to_be_clickable((By.CLASS_NAME, 'geetest_slider_button'))) return slider # 加上一个方法包含 slider.click() 图片就是带有缺口的了,接下来进一步的截图,裁剪,保存
""" 比较图片中每一个像素点,看看同一位置的像素是否相同,需要判断RGB的绝对值是否都小于阈值。 不同就返回当前位置 """ def get_gap(self, image1, image2): left = 60 # 从60的位置也就是滑块右侧的位置 for i in range(left, image1.size[0]): # 行坐标 for j in range(image1.size[1]): # 列坐标 if not self.is_pixel_equal(image1, image2, i, j): left = i # 找到不同的点,直接返回该点, 该点表示缺口左边坐标 return left return left def is_pixel_equal(self, image1, image2, x, y): """ 判断两个像素是否相同 :param image1: 图片1 :param image2: 图片2 :param x: 位置x :param y: 位置y :return: 像素是否相同 """ # 取两个图片的像素点 pixel1 = image1.load()[x, y] # 该行列(该点)的RGB三通道数值 pixel2 = image2.load()[x, y] # 同上 threshold = 60 # 阈值 if abs(pixel1[0] - pixel2[0]) < threshold and abs(pixel1[1] - pixel2[1]) < threshold and abs( pixel1[2] - pixel2[2]) < threshold: # 看绝对值 return True # 在阈值之内表明该点相同,不是缺口 else: return False
-
滑动
- 在模拟拖动的之前,需要考虑的是:人无法做到绝对的均匀速度滑动,在验证中,极验验证码利用机器学习模型筛选此类操作形成的数据为机器操作。因此需要利用变速运动公式进行模拟,这里采用匀变速运动(前段加速,后段减速,比较符合人的操作过程)。
""" 根据偏移量获取移动轨迹 :param distance: 偏移量, 缺口位置 :return: 移动轨迹 """ def get_track(self, distance): # 移动轨迹 track = [] # 当前位移 current = 0 # 减速阈值,在最后的1/5的位置开始匀减速 mid = distance * 4 / 5 # 计算间隔 t = 0.2 # 初速度 v = 0 while current < distance: if current < mid: # 加速度为正2 a = 2 else: # 加速度为负3 a = -3 # 初速度v0 刚开始时0,后来的初速度是上0.2秒末尾的速度 v0 = v # 当前速度v = v0 + at 加速 v = v0 + a * t # 移动距离x = v0t + 1/2 * a * t^2 move = v0 * t + 1 / 2 * a * t * t # 则是计算每0.2秒内行走的距离,初速度每次都变 # 当前位移 current += move # 总位移是每次移动距离的叠加 # 加入轨迹 track.append(round(move)) #将每次移动距离计算为整数 return track
- 开始滑动
- 利用在前两篇学习到的 动作链 进行拖动
""" 拖动滑块到缺口处 :param slider: 滑块 :param track: 轨迹 :return: """ def move_to_gap(self, slider, track): ActionChains(self.browser).click_and_hold(slider).perform() # 在此网页 点击并按住 slider for x in track: # 遍历轨迹 ActionChains(self.browser).move_by_offset(xoffset=x, yoffset=0).perform() # 每次都滑动到相应位置 time.sleep(0.5) ActionChains(self.browser).release().perform() # 释放
-
# 这是crack 过程完整思路
def crack(self):
# 输入用户名密码
self.open()
# 点击验证按钮
button = self.get_geetest_button()
button.click()
# 获取验证码图片
image1 = self.get_geetest_image('captcha1.png')
# 点按呼出缺口
slider = self.get_slider()
slider.click()
# 获取带缺口的验证码图片
image2 = self.get_geetest_image('captcha2.png')
# 获取缺口位置
gap = self.get_gap(image1, image2)
print('缺口位置', gap)
# 减去缺口位移
gap -= BORDER
# 获取移动轨迹
track = self.get_track(gap)
print('滑动轨迹', track)
# 拖动滑块
self.move_to_gap(slider, track)
success = self.wait.until(
EC.text_to_be_present_in_element((By.CLASS_NAME, 'geetest_success_radar_tip_content'), '验证成功'))
print(success)
# 失败后重试
if not success:
self.crack()
else:
self.login()