爬虫访问一些网站遇到滑动验证码解决方案
这里是用selenium做模拟,如果是requests可以封装这个登录方法来获取登录后的cookies也是可以用的。
1
思路
先讲思路,分析流程
我们输入账号密码后点击登录 ,出现的是第一张图的状态。我们要做的是模拟滑动到缺口处。
首先我们要拿到第二张(有缺口) 的图片再拿到第三张(无缺口)的图片然后我们通过对比两张图片的像素,找到不一样的那个像素的x轴的位置最后调用selenium模拟滑动到缺口处,实现登录
获取图片
分析网站
解决第一个问题:获取图片,chrome浏览器F12定位到图片,可以看到是canvas画板
一共有三个canvas,classname分别是geetest_canvas_bg、geetest_canvas_slice、geetest_canvas_fullbg。看名字也大概能知道分别是什么。我们尝试将geetest_canvas_slice的css属性透明的设置为0看看。可以再旁边直接写css也可以console里面写js语句,因为到时候用selenium需要执行js语句来实现,所以我们运行一下js语句
ok,可以获得有缺口的图片,在吧下面的一个canvas设置一下display:block,获得背景图
代码
全程代码都默认你们都会selenium打开网站等操作和python编程基础,然后这里执行几个js分别截图获取滑动图片和背景图片
b.get(url)# b.refresh()# b.implicitly_wait(25)# b.find_element_by_xpath('//*[@id="login"]/div/div/ul/li[2]/input').click()img1 = '1.jpeg'img2 = '2.png'b.find_element_by_xpath('//*[@id="login"]/div/div/ul/li[1]/input').send_keys('你的账号')b.find_element_by_xpath('//*[@id="login"]/div/div/ul/li[2]/input').send_keys('你的密码')time.sleep(30)b.find_element_by_xpath('//*[@id="login"]/div/div/ul/div/li/input[1]').click()print("登录中...")b.implicitly_wait(15)time.sleep(5)# img1 = Image.open('1.jpeg')# img2 = Image.open('2.png')js = 'document.getElementsByClassName("geetest_canvas_slice")[0].style.opacity=0'b.execute_script(js)hao = b.find_element_by_xpath('/html/body/div/div[2]/div[6]/div/div[1]/div[1]/div/a/div[1]/div/canvas[2]')hao.screenshot('1.jpeg')time.sleep(2)js = 'document.getElementsByClassName("geetest_canvas_fullbg")[0].style.display="block"'b.execute_script(js)hao2 = b.find_element_by_xpath('/html/body/div/div[2]/div[6]/div/div[1]/div[1]/div/a/div[1]/canvas')hao2.screenshot('2.png')time.sleep(2)js = 'document.getElementsByClassName("geetest_canvas_slice")[0].style.opacity=1'b.execute_script(js)flag=checkCode(b, img1, img2) # 过验证码
分析图片获得距离
这里其实可以调用超级鹰等三方平台帮你算,但是还是建议自己实现一次,了解一下原理。
其实思路也很简单,调用opencv的函数先把图片处理成灰度图,然后遍历对比两张图片中像素点不一样(两个图的rgb差值>某个值)的那个像素,保存其x轴位置,我这里最后实现的时候直接比较的没处理成灰度图,准确度似乎影响不大。
代码
def is_similar(image1,image2,x,y):pixel1 = image1.getpixel((x,y))pixel2 = image2.getpixel((x,y))for i in range(0,3):if abs(pixel1[i] - pixel2[i])>=50:return Falsereturn Truedef get_gap(f1,f2):"""获取缺口偏移量 f1,f2为两个图片地址:param final_imagebg: 带缺口图片:param cq: 不带缺口图片:return:"""bg =Image.open(f1)cq =Image.open(f2)for x in range(1,259):for y in range(1, 115):if is_similar(bg,cq,x,y) == False:# 判断成立 表示xy这个点 两张图不一样print(x-4)# 因为本身滑块有点距离根据不同屏幕分辨率大小可能有一定的误差值return x-4
模拟滑动行为
首先selenium库模拟鼠标滑动大概是这样的
#先xpath获取个元素
button = b.find_element_by_xpath('/html/body/div/div[2]/div[6]/div/div[1]/div[2]/div[2]')
#点击并按住元素
ActionChains(b).click_and_hold(button).perform()
# 拖动到x的距离
ActionChains(b).move_by_offset(xoffset=x, yoffset=0).perform()
# 松开鼠标
ActionChains(b).release(button).perform()
但是呢我们直接只有滑动x的距离会被失败出机器,会被提示怪兽吃掉了拼图,因为太快了,所以我们要做一个函数来模拟人的滑动行为,把x拆成n个逐渐增加在减小的数,然后遍历这个列表完成滑动动作
def get_track(distance):'''滑块移动轨迹初速度 v =0单位时间 t = 0.2位移轨迹 tracks = []当前位移 ccurrent = 0:param x::return:'''v = 5t = 0.3tracks = []current = 0mid = distance * 9 / 10distance += 5while current < distance:if current < mid:a = random.randint(2, 4) # 加速运动else:a = -random.randint(1, 3) # 减速运动v0 = vs = v0 * t + 0.5 * a * (t ** 2)current += stracks.append(round(s))v = v0 + a * t# if current > distance:#tracks.append(int(-current-distance))random.shuffle(tracks)return tracks
然后再做一个循环 先调用获取距离的函数 再做把距离变成一个列表的函数
遍历这个列表滑动对应距离,完成验证
def checkCode(b, img_file1, img_file2): scale = 1.1 button = b.find_element_by_xpath('/html/body/div/div[2]/div[6]/div/div[1]/div[2]/div[2]')try:c=0while c<=4:if c<3:x = int(get_gap(img_file1, img_file2)/scale)tracks = get_track(x)ActionChains(b).click_and_hold(button).perform()for x in tracks:ActionChains(b).move_by_offset(xoffset=x, yoffset=0).perform()# time.sleep(float(abs(x+1)/10))# print(x)time.sleep(float(abs(x + 1) / 10))ActionChains(b).release(button).perform()time.sleep(5)c += 1return Falseexcept:print("ok")return True
上面那个获取图片也可以把canvas里内容转base64然后保存成图片
def down_image(canvas_class, name):mypath = os.path.dirname((os.path.abspath(__file__))) js = 'return document.getElementsByClassName("{}")[0].toDataURL("image/png");'.format(canvas_class)image_data = browser.execute_script(js)# 执行js代码得到图片数据image_base64 = image_data.split(",")[1]# 获得base64编码的图片信息image_bytes = base64.b64decode(image_base64) # 将base64转为bytes类型image_png="{}\\{}.png".format(mypath, name)with open(image_png, "wb") as f:f.write(image_bytes)print("图片保存到",image_png)
仅作学习交流,欢迎加微信讨论mastercy1