先看题目和爬取的网站
这里加了验证码,每次请求页面之前通过验证码验证就可以获得数字了,数字就在源码中,所以关键的地方是解决验证码。这里想到的是两个方法,第一个是用开发者模式看一下验证码验证成功之后发送的请求是什么,如果可以模拟的话,就直接模拟这个请求;另一个方法是使用selenium,模拟滑动验证码。网站使用的是腾讯滑块验证码,请求的接口比较复杂。
所以不考虑第一种方法,直接使用第二种,首先就是要识别出缺口的位置,腾讯的滑块验证码比较简单,网上也有相应的代码,就直接拿来用了,参考地址https://blog.csdn.net/longjuanfengzc/article/details/109097959。
import cv2 import numpy as np img = cv2.imread(r'D:pycharm_files estpracticecaptchasrc.jpg') block = cv2.imread(r'D:pycharm_files estpracticecaptchasrc_block.jpg') img = cv2.resize(img, (340, 195)) block = cv2.resize(block, (68, 68)) img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) block = cv2.cvtColor(block, cv2.COLOR_BGR2GRAY) w, h = block.shape[:2] block = abs(255 - block) result = cv2.matchTemplate(block, img, cv2.TM_CCOEFF_NORMED) x, y = np.unravel_index(result.argmax(), result.shape) # 展示圈出来的区域 cv2.rectangle(img, (y, x), (y + w, x + h), (7, 249, 151), 2) cv2.imshow('img', img) cv2.waitKey() cv2.destroyAllWindows()
这里将缺口图片和滑块都resize成网站上显示的大小,这样子匹配出来的坐标就不用再转换了,里面得到的x,y就是画框的左上角坐标,因为滑块和缺口是平行的,所以这里只需要y值就可以了,这里要注意的是滑块是在图片上面的,所以计算滑块和缺口的距离时,还要减掉滑块初始位置的y值,看多几张验证码图片发现是一个定值,大概是25,所以计算出来的滑块和缺口的距离是y-25。
有了距离,那么就是需要模拟滑动这个动作了,上面的参考链接也提供了模拟滑动的方法,这里要注意,因为验证码是在一个iframe里面的,使用selenium的时候要从主窗口切换到验证码的iframe里面去。
action = ActionChains(browser).click_and_hold(on_element=src_block).perform() # get_distance()是获取距离的函数,返回滑块和缺口的距离 distance = get_distance() # 初速度 v = 0 # 单位时间为0.2s来统计轨迹,轨迹即0.2内的位移,越低看起来越丝滑!! t = 0.08 # 位移/轨迹列表,列表内的一个元素代表0.2s的位移 tracks = [] # 当前的位移 current = 0 # 到达mid值开始减速 mid = distance * 5 / 8 # distance += 10 # 先滑过一点,最后再反着滑动回来 while current < distance: if current < mid: # 加速度越小,单位时间的位移越小,模拟的轨迹就越多越详细 a = random.randint(100, 200) # 加速运动 else: a = -random.randint(2, 10) # 减速运动 # 初速度 v0 = v # 0.2秒时间内的位移 s = v0 * t + 0.5 * a * (t ** 2) # 当前的位置 current += s # 添加到轨迹列表 tracks.append(round(s)) # 速度已经达到v,该速度作为下次的初速度 v = v0 + a * t if current > distance: break # 反着滑动到大概准确位置 random.shuffle(tracks) count = 0 for item in tracks: count += item ActionChains(browser).move_by_offset(xoffset=item, yoffset=0).perform() # # 释放鼠标 ActionChains(browser).release(on_element=src_block).perform()
初次尝试,效果还可以,但是多试几次有时候滑动位置会不准确,这里我使用的方法是获取显示数据的div如果不存在的画,那就点击一次重新加载验证码的按钮,再进行一次模拟滑动验证码,直到成功为止,这里也可以重新加载一次页面,但是会比较慢。
最后再总结一下使用selenium完成这个题目的流程:
1、因为要登录才能操作,所以开始先访问登录页面登录,再跳转到这个题目的爬取网站。
2、保存缺口图片和滑块图片,计算滑动距离。
3、模拟滑动滑块,如果失败了,重新加载验证码或者重新加载页面,从步骤二开始重来,直到成功,获取数字,再进行下一页的数字提取。
流程比较简单,注意要加载的页面要等待元素出现再进行下一步操作,不然容易出错;验证码识别失败的时候,如果重新加载验证码,一直不成功的话,时间久了会出现元素过时,无法获取到元素的错误,出现这个异常的时候,就刷新一下页面,再进行下一步。