您的位置:首页 > 编程语言 > Python开发

python爬虫学习:验证码之滑动验证码

2019-05-30 15:29 555 查看

前面两个文章提到了普通图片的验证码识别,且尤其对于机器学习的识别方式精度相对会比较高。但是,现在开始流行滑动验证码,所以这里作者提及一点简单的滑动验证码识别技巧。

打开火狐浏览器,按下 

F12
 ,输入 
url
 为 http://www.gsxt.gov.cn/index.html ,可以打开 国家企业信用信息公示系统 ,输入关键词 中国联通 ,点击搜索会弹出一个滑动验证码出来,本文就是主要识别这个网址的滑动验证码。

识别这样的滑动验证码主要运用 

selenium
 库,判断图片中需要将按钮滑动到正确位置的距离。首先需要打开浏览器,设置最大的加载时间为 
90
 秒,如果超过 
90
 秒那么久直接调用 
js
 去停止加载,最后判断网页是否加载完毕,如果没有,则重新加载:

[code]from selenium import webdriver
# 打开浏览器
'''
遇到python不懂的问题,可以加Python学习交流群:1004391443一起学习交流,群文件还有零基础入门的学习资料
'''
def openbrowser(url):
global browser

# 声明谷歌浏览器
browser = webdriver.Chrome()
# 限制加载时间不能超过90秒
browser.set_page_load_timeout(90)

try:
# 输入网址
browser.get(url)
if "400 Bad request" in browser.page_source:
browser.get(url)
except:
# 超过90秒强制停止加载
browser.execute_script('window.stop()')
if "400 Bad request" in browser.page_source:
browser.get(url)
else:
pass
time.sleep(5)

利用 

F12
 可以知道输入框的 
id=keyword
 ,查询框的 
id=btn_query
 :

[code]# 输入关键字并查询
def inputKeyword(keyword):
# 清空输入框
browser.find_element_by_id("keyword").clear()
# 输入查找的关键字
browser.find_element_by_id("keyword").send_keys(keyword)
time.sleep(5)
# 点击查询按钮
browser.find_element_by_id("btn_query").click()
time.sleep(5)

而 

selenium
 可以控制鼠标操作,调用库即可。输入中国联通进行查询,出现滑动验证码识别,由此可以知道横拉滑块的 
classname=gt_slider_knob
 ,设置一个函数,输入拖动的横距离和纵距离:

[code]from selenium.webdriver.common.action_chains import ActionChains
# 拖放滑块
def drag_and_drop(x_offset, y_offset):
# 找到滑块并滑动
source = browser.find_element_by_class_name("gt_slider_knob")
# 调用鼠标操作并且拖动
ActionChains(browser).drag_and_drop_by_offset(source, x_offset, y_offset).perform()
time.sleep(8)

下面这里是重点!识别滑动验证码的核心是,判断原图和验证码图片 

RGB
 值的变化,假设原来验证码的图片是:

然后用鼠标轻轻拉动下方的滑块,就会出现滑动验证码的图片:

一般我们都知道每张图片都是用很多像素点构成的:

每一个像素点都会有一个 

RGB
 值,这个值代表的是改像素点图片的颜色,而这里生成的验证码的图片很明显是有一个凹下去的区域,这个区域的颜色和原图不一样。所以我们主要是判断:

  1. 在什么地方原图和验证码图片的 
    RGB
     值相差很大
  2. 相差很大的这个点的横纵坐标是多少,横坐标即为需要进行滑动的距离

在浏览器中找到验证码图片的位置,即为 

classname=gt_box

获取这个图片的 

left
 、 
top
 、 
right
 、 
bottom
 ,这里需要解释一下,
top
 和 
right
 相当于一张图片左上角的坐标,
right
 是距离左上角这个点向右的宽度,
bottom
 是距离左上角这个点向下的宽度:

定位到这张图片的位置,截取图片保存到内存中:

[code]import io
'''
遇到python不懂的问题,可以加Python学习交流群:1004391443一起学习交流,群文件还有零基础入门的学习资料
'''
# 截取验证码图片
def screenShotImage():
try:
# 定位到验证码的框
captchaElement = browser.find_element_by_class_name("gt_box")
except:
inputKeyword(keyword)
# 定位到验证码的框
captchaElement = browser.find_element_by_class_name("gt_box")
locations = captchaElement.location
sizes = captchaElement.size

left = int(locations["x"])
top = int(locations["y"])
right = left + int(sizes["width"])
bottom = top + int(sizes['height'])

# 截图
screenshot = browser.get_screenshot_as_png()
screenshot = Image.open(io.BytesIO(screenshot))
png = screenshot.crop((left, top, right, bottom))
png.save(str(int(time.time())) + ".png")
time.sleep(1)
return png

在对原图和验证码图片进行对比的时候,要排除掉拼图的干扰,拼图也就是这张图片,它和原图也是不一样的:

如何排除这张图片的干扰呢?本文首先找到这张图片的宽度:

[code]# 获取滑块的图片,是为了得到滑块的宽度
def getWidth():
# 找到滑块并滑动
slider = browser.find_element_by_class_name("gt_slice")
sizes = slider.size
width = sizes["width"]
return width

由于该滑块总是出现在左边,所以在对比 

RBG
 值的时候跳过这个宽度。

将原图的每个像素点和验证码的每个像素点做差,如果差值都小于60则认为这个像素点出现 明显差异 ,即认定为验证码凹陷位置的起点。由于观察验证码图片的时候发现,滑块左边其实是有5个位移是空白的,所以最后的位移值要减掉5。而如果没有找到正确的偏移值,则返回一个假的偏移值55,防止程序中断:

[code]# 获取滑动偏移量
def getOffset():
img1 = screenShotImage()
drag_and_drop(x_offset=5, y_offset=0)
img2 = screenShotImage()
w1, h1 = img1.size
w2, h2 = img2.size
print("wh")
print(w1, h1, w2, h2)

if w1 != w2 or h1 != h2:
return False

slider = getWidth()
print("开始循环", w3, h3)
for x in range(slider, w3):
print(x)
for y in range(0, h3):
pix1 = img1.getpixel((x, y))
pix2 = img2.getpixel((x, y))
# 如果相差超过70则就认为找到了缺口的位置
if abs(pix1[0]-pix2[0]) < 60 and abs(pix1[1]-pix2[1]) < 60 and abs(pix1[2]-pix2[2]) < 60:
# 滑块左边其实是有5个位移是空白的,所以要减掉
return x-5
# 如果没有找到正确的偏移值,则返回一个假的偏移值,防止程序中断
return 55

但是不管临界值选取60还是其他值效果都非常差,所以可以利用图片库比较两张图片有什么不同:

[code]# 判断两张图片不同的地方
img3 = ImageChops.difference(img1, img2)
img3.save(str(int(time.time())) + ".png")

可以判断这张图片如果底色不是黑色,即 

RBG(0,0,0)
 ,且像素颜色鲜艳,颜色鲜艳即 
RBG
 的某个值大于70,那么就认为找到了凹陷地方的边界,更改代码增加挑选条件:

[code]# 获取滑动偏移量
def getOffset():
img1 = screenShotImage()
drag_and_drop(x_offset=5, y_offset=0)
img2 = screenShotImage()
# 判断两张图片不同的地方
img3 = ImageChops.difference(img1, img2)
img3.save(str(int(time.time())) + ".png")
w1, h1 = img1.size
w2, h2 = img2.size
w3, h3 = img3.size
print("wh")
print(w1, h1, w2, h2)

if w1 != w2 or h1 != h2:
return False

slider = getWidth()
print("开始循环", w3, h3)
for x in range(slider, w3):
print(x)
for y in range(0, h3):
pix1 = img1.getpixel((x, y))
pix2 = img2.getpixel((x, y))
pix3 = img3.getpixel((x, y))
print(pix3[0],pix3[1],pix3[2])
# 如果相差超过70则就认为找到了缺口的位置
if pix3[0] > 70 or pix3[1] > 70 or pix3[2] > 70 and abs(pix1[0]-pix2[0]) < 60 and abs(pix1[1]-pix2[1]) < 60 and abs(pix1[2]-pix2[2]) < 60:
# 滑块左边其实是有5个位移是空白的,所以要减掉
return x-5
# 如果没有找到正确的偏移值,则返回一个假的偏移值,防止程序中断
return 55

最后代入运行能准确的找到滑块验证码的位置:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: