拖动式验证码
问题点:
1、无法直接通过发送url请求来实现鼠标拖动的动作;
2、实际的背景图片是乱的,并不是我们实际肉眼看到的图像!
3、“开创行为判别算法,利用数据挖掘和机器学习,提取超过200多个行为判别特征,建立坚若磐石的多维验证防御体系。”这是官网的描述,听上去就已经很高大上,查了些资料也都说拖动轨迹的识别是geetest的核心内容而无过多的表述,那么这也应该是主要的难点了
提供的是一种思路:
1、获取图片,调整拼接
2、计算图片缺口(这个实例的计算不太理想)
3、生成移动轨迹(模拟)
4、滑动
安装geetest实例
首先自己安装配置一份geetest的样例。虽然geetest官网上有样例,但有时候反应比较慢,而且后面研究拖动轨迹的时候还需要对样例做一定的改动。编程语言我使用的是python2.7,所以这里选择的也是python版本的。
安装git:
[root@mysql-test1 ~]# yum install git
在github中clone出最新Demo项目:
[root@mysql-test1 ~]# git clone https://github.com/GeeTeam/gt-python-sdk.git
安装GeetestSDK:
[root@mysql-test1 ~]# cd gt-python-sdk/
[root@mysql-test1 gt-python-sdk]# python setup.py install
安装Django,要注意的是最新的Django-1.10.1和当前的GeetestSDK是有兼容性问题的,要用Django-1.8.14:
[root@mysql-test1 ~]# wget --no-check-certificate https://www.djangoproject.com/download/1.8.14/tarball/
[root@mysql-test1 ~]# tar zxvf Django-1.8.14.tar.gz
[root@mysql-test1 ~]# cd Django-1.8.14
[root@mysql-test1 Django-1.8.14]# python setup.py install
后面就可以直接运行了:
[root@mysql-test1 ~]# cd gt-python-sdk/demo/django_demo/
[root@mysql-test1 django_demo]# python manage.py runserver 0.0.0.0:8000
解析过程:
#!/usr/local/bin/python
# -*- coding: utf8 -*-
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.action_chains import ActionChains
import PIL.Image as image
# from PIL import Image
import time, re, cStringIO, urllib2, random
def get_merge_image(filename,location_list):
'''
根据位置对图片进行合并还原
:filename:图片
:location_list:图片位置
'''
pass
im = image.open(filename)
new_im = image.new('RGB', (260, 116))
im_list_upper = []
im_list_down = []
for location in location_list:
if location['y'] == -58:
# 选择区域对象,给定四点坐标,确定图像
im_list_upper.append(im.crop((abs(location['x']), 58, abs(location['x'])+10, 166)))
if location['y'] == 0:
im_list_down.append(im.crop((abs(location['x']), 0, abs(location['x'])+10,58)))
new_im = image.new('RGB', (260,116))
x_offset = 0
for im in im_list_upper:
# 粘贴到指定坐标
new_im.paste(im, (x_offset,0))
x_offset += im.size[0]
x_offset = 0
for im in im_list_down:
new_im.paste(im, (x_offset,58))
x_offset += im.size[0]
return new_im
def get_image(driver,div):
'''
下载并还原图片
:driver:webdriver
:div:图片的div
'''
pass
# 找到图片所在的div
background_images = driver.find_elements_by_xpath(div)
location_list = []
imageurl = ''
for background_image in background_images:
location={}
# 在html里面解析出小图片的url地址,还有长高的数值
location['x']=int(re.findall("background-image: url\(\"(.*)\"\); background-position: (.*)px (.*)px;",background_image.get_attribute('style'))[0][1])
location['y']=int(re.findall("background-image: url\(\"(.*)\"\); background-position: (.*)px (.*)px;",background_image.get_attribute('style'))[0][2])
imageurl = re.findall("background-image: url\(\"(.*)\"\); background-position: (.*)px (.*)px;",background_image.get_attribute('style'))[0][0]
location_list.append(location)
imageurl = imageurl.replace("webp", "jpg")
proxy_support = urllib2.ProxyHandler({"http": "172.17.18.80:8080"})
opener = urllib2.build_opener(proxy_support)
urllib2.install_opener(opener)
jpgfile = cStringIO.StringIO(urllib2.urlopen(imageurl).read())
# 重新合并图片
image = get_merge_image(jpgfile, location_list)
return image
def is_similar(image1, image2, x, y):
'''
对比RGB值
'''
pass
pixel1=image1.getpixel((x, y))
pixel2=image2.getpixel((x, y))
for i in range(0,3):
if abs(pixel1[i]-pixel2[i]) >= 50:
return False
return True
def get_diff_location(image1, image2):
'''
计算缺口的位置
'''
i = 0
for i in range(0, 260):
for j in range(0, 116):
if is_similar(image1, image2, i, j) == False:
return i
def get_track(length):
'''
根据缺口的位置模拟x轴移动的轨迹
'''
pass
list=[]
# 间隔通过随机范围函数来获得
x = random.randint(1, 3)
while length-x >= 5:
list.append(x)
length = length-x
x = random.randint(1, 3)
for i in xrange(length):
list.append(1)
return list
def main():
# 这里的文件路径是webdriver的文件路径
# driver = webdriver.Chrome(executable_path=r"C:\Program Files (x86)\Google\Chrome\Application\chromedriver.exe")
driver = webdriver.Firefox()
# 打开网页
driver.get("http://127.0.0.1:5000/")
# 等待页面的上元素刷新出来
WebDriverWait(driver, 30).until(lambda the_driver: the_driver.find_element_by_xpath("//div[@class='gt_slider_knob gt_show']").is_displayed())
# WebDriverWait(driver, 30).until(lambda the_driver: the_driver.find_element_by_xpath("//div[@class='gt_cut_bg gt_show']").is_displayed())
# WebDriverWait(driver, 30).until(lambda the_driver: the_driver.find_element_by_xpath("//div[@class='gt_cut_fullbg gt_show']").is_displayed())
# 下载图片
image1=get_image(driver, "//div[@class='gt_cut_bg gt_show']/div")
image2=get_image(driver, "//div[@class='gt_cut_fullbg gt_show']/div")
image1.save("image1.jpg", "JPEG")
image2.save("image2.jpg", "JPEG")
# 计算缺口位置
loc = get_diff_location(image1, image2)
# 生成x的移动轨迹点
track_list = get_track(loc)
# 找到滑动的圆球
element = driver.find_element_by_xpath("//div[@class='gt_slider_knob gt_show']")
location = element.location
# 获得滑动圆球的高度
y = location['y']
# 鼠标点击元素并按住不放
print "第一步,点击元素"
ActionChains(driver).click_and_hold(on_element=element).perform()
time.sleep(0.15)
print "第二步,拖动元素"
print track_list
track_string = ""
for track in track_list:
track_string = track_string + "{%d,%d}," % (track, y - 521)
# xoffset=track+22:这里的移动位置的值是相对于滑动圆球左上角的相对值,而轨迹变量里的是圆球的中心点,所以要加上圆球长度的一半。
# yoffset=y-521:这里也是一样的。不过要注意的是不同的浏览器渲染出来的结果是不一样的,要保证最终的计算后的值是22,也就是圆球高度的一半
ActionChains(driver).move_to_element_with_offset(to_element=element, xoffset=track+22, yoffset=y-521).perform()
# 间隔时间也通过随机函数来获得
time.sleep(random.randint(10, 50)/100)
print track_string
# xoffset=21,本质就是向后退一格。这里退了5格是因为圆球的位置和滑动条的左边缘有5格的距离
ActionChains(driver).move_to_element_with_offset(to_element=element, xoffset=21, yoffset=y-521).perform()
time.sleep(0.1)
ActionChains(driver).move_to_element_with_offset(to_element=element, xoffset=21, yoffset=y-521).perform()
time.sleep(0.1)
ActionChains(driver).move_to_element_with_offset(to_element=element, xoffset=21, yoffset=y-521).perform()
time.sleep(0.1)
ActionChains(driver).move_to_element_with_offset(to_element=element, xoffset=21, yoffset=y-521).perform()
time.sleep(0.1)
ActionChains(driver).move_to_element_with_offset(to_element=element, xoffset=21, yoffset=y-521).perform()
time.sleep(2)
print "第三步,释放鼠标"
# 释放鼠标
ActionChains(driver).release(on_element=element).perform()
time.sleep(5)
# 点击验证
driver.find_element_by_xpath("//input[@id='embed-submit']").click()
# ActionChains(driver).click(on_element=submit).perform()
time.sleep(10)
driver.quit()
if __name__ == '__main__':
pass
main()
参考:http://blog.youkuaiyun.com/paololiu/article/details/52514504