使用selenium进行模拟登录豆瓣网,利用opencv模块获取滑块验证码的缺口,最终实现模拟登录
涉及
1. selenim启动360极速浏览器
2. selenium获取标签、输入等的基本操作
3. 获取登录相关标签时,遇iframe的解决办法
3. 获取滑块验证码图片,并下载到本地
4. 使用opencv模块中的"模板匹配"方法获取缺口的位置
5. 采用物理加速度位移相关公式按照先快后慢的人工滑动规律模拟滑块运动轨迹
6. selenium滑块操作
流程
1. 访问主页
2. 切换到密码登录页
3. 输入账号、密码,点击登录
4. 错误登录几次后会出现滑块验证码
5. 破解滑块验证码,使验证通过
详解
- selenim启动360极速浏览器
1. 下载360极速浏览器对应的Chromedriver,下载请点击,内核版本查询见下图
2. 将下载的Chromedriver.exe放到360chrome.exe所在目录下以及pythonw.exe所在目录
3. 添加系统环境变量path:F:\360Chrome\Chrome\Application\360chrome.exe
4. 初始化driver
browser_url = r'F:\360Chrome\Chrome\Application\360chrome.exe'
chrome_options = Options()
chrome_options.binary_location = browser_url
# 声明浏览器对象
driver = webdriver.Chrome(chrome_options=chrome_options)
# 登录豆瓣
# 访问页面
driver.get('https://www.douban.com')
- selenium常用操作
1. 详细操作参考:详细说明
2. 输入账号、密码
# 通过类名查找 若遇到复合类(即中间有空格)class='account-tab-account on',选取其中一个类即可,#若其再html中不唯一,可采用[index]定位
driver.find_element_by_class_name("account-tab-account").click()
driver.find_element_by_id('username').send_keys('12345678')
driver.find_element_by_id('password').send_keys('qwertyu')
time.sleep(2)
driver.find_element_by_class_name('btn-account').click()
- iframe获取
1. iframe,又叫浮动帧标记,是内嵌的网页元素,可以将一个html文件嵌入到另一个html文件中显示。
# switch_to.frame() 切换到iframe上
# switch_to.default_content() 切换回原主页面
# 定位到iframe #通过contains函数,提取匹配特定文本的元素
iframe=driver.find_element_by_xpath("//iframe[contains(@src,'//accounts.douban.com/passport/login_popup?login_source=anony')]")
# 切换到iframe
driver.switch_to.frame(iframe)
# 定位到滑块验证码的iframe
slider_iframe = driver.find_element_by_xpath('//div[@id="TCaptcha"]/iframe[1]')
# 切换到iframe
driver.switch_to.frame(slider_iframe)
- 获取滑块验证码图片,并下载到本地
1. 获取验证码图片
# 获取背景图和滑块图的url
background_image_url=driver.find_element_by_id('slideBkg').get_attribute('src')
slider_image_url=driver.find_element_by_id('slideBlock').get_attribute('src')
# 下载图片到本地
def get_image(img_url,imgname):
# 以流的形式下载文件
image=requests.get(img_url,stream=True)
# str.join()方法用于将序列中的元素以指定的字符(str)连接生成一个新的字符串
imgName = ''.join(["./", imgname])
with open(imgName, 'wb') as f:
for chunk in image.iter_content(chunk_size=1024): # 循环写入 chunk_size:每次下载的数据大小
if chunk:
f.write(chunk)
f.flush()
f.close()
- 使用opencv模块中的"模板匹配"方法获取缺口的位置
# 使用opencv模块 计算缺口的偏移值
def get_image_offset(background_image_url,slider_image_url):
back_image='back_image.png' # 背景图像命名
slider_image='slider_image.png' # 滑块图像命名
get_image(background_image_url,back_image)
get_image(slider_image_url,slider_image)
# 获取图片并灰度化
block=cv2.imread(slider_image,0)
template=cv2.imread(back_image,0)
w,h=block.shape[::-1]
print(w,h)
# 二值化后图片名称
block_name='block.jpg'
template_name='template.jpg'
# 保存二值化后的图片
cv2.imwrite(block_name,block)
cv2.imwrite(template_name,template)
block=cv2.imread(block_name)
block=cv2.cvtColor(block,cv2.COLOR_RGB2GRAY)
block=abs(255-block)
cv2.imwrite(block_name,block)
block=cv2.imread(block_name)
template=cv2.imread(template_name)
# 获取偏移量
# 模板匹配,查找block在template中的位置,返回result是一个矩阵,是每个点的匹配结果
result=cv2.matchTemplate(block,template,cv2.TM_CCOEFF_NORMED)
x,y=np.unravel_index(result.argmax(),result.shape)
print(x,y)
# 由于获取到的验证码图片像素与实际的像素有差(实际:280*158 原图:680*390),故对获取到的坐标进行处理
offset=y*(280/680)
# 画矩形圈出匹配的区域
# 参数解释:1.原图 2.矩阵的左上点坐标 3.矩阵的右下点坐标 4.画线对应的rgb颜色 5.线的宽度
cv2.rectangle(template, (y, x), (y + w, x + h), (7, 249, 151), 2)
show(template)
return offset
# 显示图片
def show(name):
cv2.imshow('Show', name)
cv2.waitKey(0)
cv2.destroyAllWindows()
- 模拟运动轨迹
# 采用物理加速度位移相关公式按照先快后慢的人工滑动规律进行轨迹计算,
# 同时还采用了模拟人滑动超过了缺口位置再滑回至缺口的情况以使轨迹更契合人工滑动轨迹
def get_track(distance):
track = []
current = 0
mid = distance * 3 / 4
t=random.randint(2,3)/10
v = 0
while current < distance:
if current < mid:
a = 2
else:
a = -3
v0 = v
v = v0 + a * t
move = v0 * t + 1 / 2 * a * t * t
current += move
track.append(round(move,2))
return track
- selenium滑块操作
# 定位到滑块按钮
button=driver.find_element_by_id('tcaptcha_drag_thumb')
# 拖动操作用到ActionChains类,实例化
action=ActionChains(driver)
# perform()用来执行ActionChains中存储的行为
action.click_and_hold(button).perform()
# 清除之前的action
action.reset_actions()
# 获取轨迹
track=get_track(distance+random.randint(3,5))
print(track)
sum=0
for i in track:
sum+=i
print(sum)
for i in track:
action.move_by_offset(xoffset=i,yoffset=0).perform()
action.reset_actions()
time.sleep(1)
action.release().perform()
time.sleep(3)
driver.quit()
运行结果:
识别率还ok,代码写的比较粗糙,后续处理别的网站再进行细节优化