python网站模拟登陆和滑动验证码识别

python 模拟网站登录——滑块验证码的识别

原文:https://www.jb51.net/article/207816.htm

普通滑动验证

http://admin.emaotai.cn/login.aspx为例这类验证码只需要我们将滑块拖动指定位置,处理起来比较简单。拖动之前需要先将滚动条滚动到指定元素位置。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

import time

from selenium import webdriver

from selenium.webdriver import ActionChains

# 新建selenium浏览器对象,后面是geckodriver.exe下载后本地路径

browser = webdriver.Firefox()

# 网站登陆页面

url = 'http://admin.emaotai.cn/login.aspx'

# 浏览器访问登录页面

browser.get(url)

browser.maximize_window()

browser.implicitly_wait(5)

draggable = browser.find_element_by_id('nc_1_n1z')

# 滚动指定元素位置

browser.execute_script("arguments[0].scrollIntoView();", draggable)

time.sleep(2)

ActionChains(browser).click_and_hold(draggable).perform()

# 拖动

ActionChains(browser).move_by_offset(xoffset=247, yoffset=0).perform()

ActionChains(browser).release().perform()

拼图滑动验证

我们以欧模网很多网站使用的都是类似的方式。因为验证码及拼图都有明显明亮的边界,图片辨识度比较高。所以我们尝试先用cv2的边缘检测识别出边界,然后进行模糊匹配,匹配出拼图在验证码图片的位置。

边缘检测

cv2模块提供了多种边缘检测算子,包括Sobel、Scharr、Laplacian、prewitt、Canny或Marr—Hildreth等,每种算子得出的结果不同。这里我们用Canny算子,测试了很多算子,这种效果最好。

我们通过一个程序调整一下canny算子的阈值,使得输出图片只包含拼图轮廓。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

import cv2

lowThreshold = 0

maxThreshold = 100

# 最小阈值范围 0 ~ 500

# 最大阈值范围 100 ~ 1000

def canny_low_threshold(intial):

  blur = cv2.GaussianBlur(img, (3, 3), 0)

  canny = cv2.Canny(blur, intial, maxThreshold)

  cv2.imshow('canny', canny)

def canny_max_threshold(intial):

  blur = cv2.GaussianBlur(img, (3, 3), 0)

  canny = cv2.Canny(blur, lowThreshold, intial)

  cv2.imshow('canny', canny)

# 参数0以灰度方式读取

img = cv2.imread('vcode.png', 0)

cv2.namedWindow('canny', cv2.WINDOW_NORMAL | cv2.WINDOW_KEEPRATIO)

cv2.createTrackbar('Min threshold', 'canny', lowThreshold, max_lowThreshold, canny_low_threshold)

cv2.createTrackbar('Max threshold', 'canny', maxThreshold, max_maxThreshold, canny_max_threshold)

canny_low_threshold(0)

# esc键退出

if cv2.waitKey(0) == 27:

  cv2.destroyAllWindows()

测试了若干个图片发现最小阈值100、最大阈值500输出结果比较理想。

拼图匹配

我们用cv2的matchTemplate方法进行模糊匹配,匹配方法用CV_TM_CCOEFF_NORMED归一化相关系数匹配。

几种方法算法详见。

【1】 平方差匹配 method=CV_TM_SQDIFF square dirrerence(error)
这类方法利用平方差来进行匹配,最好匹配为0.匹配越差,匹配值越大.
【2】标准平方差匹配 method=CV_TM_SQDIFF_NORMED standard square dirrerence(error)
【3】 相关匹配 method=CV_TM_CCORR
这类方法采用模板和图像间的乘法操作,所以较大的数表示匹配程度较高,0标识最坏的匹配效果.
【4】 标准相关匹配 method=CV_TM_CCORR_NORMED
【5】 相关匹配 method=CV_TM_CCOEFF
这类方法将模版对其均值的相对值与图像对其均值的相关值进行匹配,1表示完美匹配,
-1表示糟糕的匹配,0表示没有任何相关性(随机序列).
【6】标准相关匹配 method=CV_TM_CCOEFF_NORMED

canndy_test.py:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

import cv2

import numpy as np

def matchImg(imgPath1,imgPath2):

  imgs = []

  # 原始图像,用于展示

  sou_img1 = cv2.imread(imgPath1)

  sou_img2 = cv2.imread(imgPath2)

  # 原始图像,灰度

  # 最小阈值100,最大阈值500

  img1 = cv2.imread(imgPath1, 0)

  blur1 = cv2.GaussianBlur(img1, (3, 3), 0)

  canny1 = cv2.Canny(blur1, 100, 500)

  cv2.imwrite('temp1.png', canny1)

  img2 = cv2.imread(imgPath2, 0)

  blur2 = cv2.GaussianBlur(img2, (3, 3), 0)

  canny2 = cv2.Canny(blur2, 100, 500)

  cv2.imwrite('temp2.png', canny2)

  target = cv2.imread('temp1.png')

  template = cv2.imread('temp2.png')

  # 调整显示大小

  target_temp = cv2.resize(sou_img1, (350, 200))

  target_temp = cv2.copyMakeBorder(target_temp, 5, 5, 5, 5, cv2.BORDER_CONSTANT, value=[255, 255, 255])

  template_temp = cv2.resize(sou_img2, (200, 200))

  template_temp = cv2.copyMakeBorder(template_temp, 5, 5, 5, 5, cv2.BORDER_CONSTANT, value=[255, 255, 255])

  imgs.append(target_temp)

  imgs.append(template_temp)

  theight, twidth = template.shape[:2]

  # 匹配拼图

  result = cv2.matchTemplate(target, template, cv2.TM_CCOEFF_NORMED)

  # 归一化

  cv2.normalize( result, result, 0, 1, cv2.NORM_MINMAX, -1 )

  min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)

  # 匹配后结果画圈

  cv2.rectangle(target,max_loc,(max_loc[0]+twidth,max_loc[1]+theight),(0,0,255),2)

  target_temp_n = cv2.resize(target, (350, 200))

  target_temp_n = cv2.copyMakeBorder(target_temp_n, 5, 5, 5, 5, cv2.BORDER_CONSTANT, value=[255, 255, 255])

  imgs.append(target_temp_n)

  imstack = np.hstack(imgs)

  cv2.imshow('stack'+str(max_loc), imstack)

  cv2.waitKey(0)

  cv2.destroyAllWindows()

matchImg('vcode_data/out_'+str(1)+'.png','vcode_data/in_'+str(1)+'.png')

我们测试几组数据,发现准确率拿来玩玩尚可。max_loc就是匹配出来的位置信息,我们只需要按照位置进行拖动即可。

完整程序

完整流程

1.实例化浏览器

2.点击登陆,弹出滑动验证框

3.分别新建标签页打开背景图及拼图

4.全屏截图后按照尺寸裁剪

5.模糊匹配两张图片,获取匹配结果位置信息

6.将位置信息转为页面上的位移距离

7.拖动滑块到指定位置

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

import time

import cv2

import canndy_test

from selenium import webdriver

from selenium.webdriver import ActionChains

# 新建selenium浏览器对象,后面是geckodriver.exe下载后本地路径

browser = webdriver.Firefox()

# 网站登陆页面

url = '欧模网-账户登录'

# 浏览器访问登录页面

browser.get(url)

handle = browser.current_window_handle

# 等待3s用于加载脚本文件

browser.implicitly_wait(3)

# 点击登陆按钮,弹出滑动验证码

btn = browser.find_element_by_class_name('login_btn1')

btn.click()

# 获取iframe元素,切到iframe

frame = browser.find_element_by_id('tcaptcha_iframe')

browser.switch_to.frame(frame)

time.sleep(1)

# 获取背景图src

targetUrl = browser.find_element_by_id('slideBg').get_attribute('src')

# 获取拼图src

tempUrl = browser.find_element_by_id('slideBlock').get_attribute('src')

# 新建标签页

browser.execute_script("window.open('');")

# 切换到新标签页

browser.switch_to.window(browser.window_handles[1])

# 访问背景图src

browser.get(targetUrl)

time.sleep(3)

# 截图

browser.save_screenshot('temp_target.png')

w = 680

h = 390

img = cv2.imread('temp_target.png')

size = img.shape

top = int((size[0] - h) / 2)

height = int(h + ((size[0] - h) / 2))

left = int((size[1] - w) / 2)

width = int(w + ((size[1] - w) / 2))

cropped = img[top:height, left:width]

# 裁剪尺寸

cv2.imwrite('temp_target_crop.png', cropped)

# 新建标签页

browser.execute_script("window.open('');")

browser.switch_to.window(browser.window_handles[2])

browser.get(tempUrl)

time.sleep(3)

browser.save_screenshot('temp_temp.png')

w = 136

h = 136

img = cv2.imread('temp_temp.png')

size = img.shape

top = int((size[0] - h) / 2)

height = int(h + ((size[0] - h) / 2))

left = int((size[1] - w) / 2)

width = int(w + ((size[1] - w) / 2))

cropped = img[top:height, left:width]

cv2.imwrite('temp_temp_crop.png', cropped)

browser.switch_to.window(handle)

# 模糊匹配两张图片

move = canndy_test.matchImg('temp_target_crop.png', 'temp_temp_crop.png')

# 计算出拖动距离

distance = int(move / 2 - 27.5) + 2

draggable = browser.find_element_by_id('tcaptcha_drag_thumb')

ActionChains(browser).click_and_hold(draggable).perform()

# 拖动

ActionChains(browser).move_by_offset(xoffset=distance, yoffset=0).perform()

ActionChains(browser).release().perform()

time.sleep(10)

tips:可能会存在第一次不成功的情况,虽然拖动到了指定位置但是提示网络有问题、拼图丢失。可以进行循环迭代直到拼成功为止。通过判断iframe中id为slideBg的元素是否存在,如果成功了则不存在,失败了会刷新拼图让你重新拖动。

1

2

3

4

5

6

7

8

9

10

11

if(isEleExist(browser,'slideBg')):

    # retry

  else:

    return

def isEleExist(browser,id):

  try:

    browser.find_element_by_id(id)

    return True

  except:

    return False


python爬虫--利用selenium+opencv识别滑动验证并模拟登陆知乎

原文:python爬虫--利用selenium+opencv识别滑动验证并模拟登陆知乎 - eliwang - 博客园

滑动验证距离

分别获取验证码背景图和滑块图两张照片,然后利用opencv库,通过高斯模糊和Canny算法进行处理,然后通过matchTemplate方法进行两张图的匹配,获得滑动距离。需要注意的是,知乎验证码在进行操作的时候,需要在原有基础上再向右偏移10px距离

    def get_distance(self, bg_img_path='./bg.png', slider_img_path='./slider.png'):
        """获取滑块移动距离"""

        # 背景图片处理
        bg_img = cv.imread(bg_img_path, 0)  # 读入灰度图片
        bg_img = cv.GaussianBlur(bg_img, (3, 3), 0)  # 高斯模糊去噪
        bg_img = cv.Canny(bg_img, 50, 150)  # Canny算法进行边缘检测
        # 滑块做同样处理
        slider_img = cv.imread(slider_img_path, 0)
        slider_img = cv.GaussianBlur(slider_img, (3, 3), 0)
        slider_img = cv.Canny(slider_img, 50, 150)
        # 寻找最佳匹配
        res = cv.matchTemplate(bg_img, slider_img, cv.TM_CCOEFF_NORMED)
        # 最小值,最大值,并得到最小值, 最大值的索引
        min_val, max_val, min_loc, max_loc = cv.minMaxLoc(res)
        # 例如:(-0.05772797390818596, 0.30968162417411804, (0, 0), (196, 1))
        top_left = max_loc[0]  # 横坐标
        return top_left

滑块运动轨迹

模拟人的行为,到缺口位置时,继续向后滑动一段距离,然后再回退到大致准确位置

    def get_tracks(self, distance):
        '''滑动轨迹 '''

        tracks = []
        v = 0
        t = 0.2  # 单位时间
        current = 0  # 滑块当前位移
        distance += 10  # 多移动10px,然后回退
        while current < distance:
            if current < distance * 5 / 8:
                a = random.randint(1, 3)
            else:
                a = -random.randint(2, 4)
            v0 = v  # 初速度
            track = v0 * t + 0.5 * a * (t ** 2)  # 单位时间(0.2s)的滑动距离
            tracks.append(round(track))  # 加入轨迹
            current += round(track)
            v = v0 + a * t
        #回退到大致位置
        for i in range(5):
            tracks.append(-random.randint(1, 3))
        return tracks

鼠标滑动操作

通过selenium中的鼠标动作链,按照滑动轨迹进行滑动

 def mouse_move(self,slide,tracks):
        '''鼠标滑动'''

        #鼠标点击滑块并按照不放
        ActionChains(self.driver).click_and_hold(slide).perform()
        #按照轨迹进行滑动,
        for track in tracks:
            ActionChains(self.driver).move_by_offset(track, 0).perform() 
        ActionChains(self.driver).release(slide).perform()

规避知乎selenium检测

使用selenium自动化测试爬取知乎的时候出现了:错误代码10001:请求异常请升级客户端后重新尝试,这个错误的产生是由于知乎可以检测selenium自动化测试的脚本

使用chrome的远程调试模式结合selenium来遥控操作chrome进行抓取,这样就会规避selenium被网站检测到

添加环境变量

将chrome.exe的目录添加到系统环境变量,比如 C:\Program Files\Google\Chrome\Application ,这样就可以直接在命令行输入 chrome.exe 启动浏览器

打开cmd窗口,执行命令

chrome.exe --remote-debugging-port=9222 --user-data-dir="E:\eliwang\selenium_data"

注意端口不要被占用,user-data-dir用来指明配置文件的路径,自定义

此时会开启浏览器,并打开一个新的标签页

selenium接管的主要代码

options.add_experimental_option("debuggerAddress", "127.0.0.1:9222")

 关闭浏览器窗口

1、使用浏览器对象的close()方法,quit()方法不行。

2、手动打开,手动关闭

完整登陆代码

# coding:utf-8

import cv2 as cv
import time
import random
from selenium import webdriver
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait as WAIT
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By
from urllib.request import urlretrieve


class Zhihu_login:
    '''知乎模拟登陆'''

    def __init__(self):
        options = webdriver.ChromeOptions()
        #操控chrome浏览器
        options.add_experimental_option("debuggerAddress", "127.0.0.1:9222")
        self.driver = webdriver.Chrome(options=options)
        self.wait = WAIT(self.driver, 5)
        self.url = 'https://www.zhihu.com/'
        self.bg_img_path = './bg.png'
        self.slider_img_path = './slider.png'


    def run(self):
        '''执行入口'''

        self.driver.get(self.url)
        try:
            if WAIT(self.driver,3).until(EC.presence_of_element_located((By.ID,'Popover15-toggle'))):
                print('登陆成功')
                self.save_cookie()
                self.driver.close()
        except:
            # 切换到密码登陆
            self.wait.until(EC.element_to_be_clickable((By.XPATH, '//div[contains(@class,"SignFlow-tabs")]/div[2]'))).click()
            name_input = self.driver.find_element_by_name('username')
            name_input.clear()
            name_input.send_keys('账号')
            pass_input = self.driver.find_element_by_name('password')
            pass_input.clear()
            pass_input.send_keys('密码')
            self.wait.until(EC.element_to_be_clickable((By.XPATH, '//button[@type="submit"]'))).click()  # 点击登陆按钮
            time.sleep(1)
            #进行滑动验证,最多尝试5次重新验证
            if self.slide_verify():
                print('登陆成功')
                self.save_cookie()
                self.driver.close()
            else:
                print('第1次登陆失败')
                for i in range(4):
                    print('正在尝试第%d次登陆'%(i+2))
                    if self.slide_verify():
                        print('第%d次登陆成功'%(i+2))
                        self.save_cookie()
                        self.driver.close()
                        return
                    print('第%d次登陆失败' % (i + 2))
                print('登陆失败5次,停止登陆')
                self.driver.close()


    def slide_verify(self):
        '''滑动验证'''

        slider_button = self.wait.until(EC.element_to_be_clickable((By.XPATH, '//div[@class="yidun_slider"]')))
        self.bg_img_url = self.wait.until(EC.presence_of_element_located((By.XPATH, '//img[@class="yidun_bg-img"]'))).get_attribute('src')  # 获取验证码背景图url
        self.slider_img_url = self.wait.until(EC.presence_of_element_located((By.XPATH, '//img[@class="yidun_jigsaw"]'))).get_attribute('src')  # 获取验证码滑块图url
        urlretrieve(self.bg_img_url, self.bg_img_path)
        urlretrieve(self.slider_img_url, self.slider_img_path)
        distance = self.get_distance(self.bg_img_path, self.slider_img_path)
        distance += 10  # 实际移动距离需要向右偏移10px
        tracks = self.get_tracks(distance)
        self.mouse_move(slider_button,tracks)
        try:
            element = self.wait.until(EC.presence_of_element_located((By.ID,'Popover15-toggle')))
        except:
            return False
        else:
            return True

    def save_cookie(self):
        cookie = {}
        for item in self.driver.get_cookies():
            cookie[item['name']] = item['value']
        print(cookie)
        print('成功获取登陆知乎后的cookie信息')


    def mouse_move(self,slide,tracks):
        '''鼠标滑动'''

        #鼠标点击滑块并按照不放
        ActionChains(self.driver).click_and_hold(slide).perform()
        #按照轨迹进行滑动,
        for track in tracks:
            ActionChains(self.driver).move_by_offset(track, 0).perform()
        ActionChains(self.driver).release(slide).perform()


    def get_distance(self, bg_img_path='./bg.png', slider_img_path='./slider.png'):
        """获取滑块移动距离"""

        # 背景图片处理
        bg_img = cv.imread(bg_img_path, 0)  # 读入灰度图片
        bg_img = cv.GaussianBlur(bg_img, (3, 3), 0)  # 高斯模糊去噪
        bg_img = cv.Canny(bg_img, 50, 150)  # Canny算法进行边缘检测
        # 滑块做同样处理
        slider_img = cv.imread(slider_img_path, 0)
        slider_img = cv.GaussianBlur(slider_img, (3, 3), 0)
        slider_img = cv.Canny(slider_img, 50, 150)
        # 寻找最佳匹配
        res = cv.matchTemplate(bg_img, slider_img, cv.TM_CCOEFF_NORMED)
        # 最小值,最大值,并得到最小值, 最大值的索引
        min_val, max_val, min_loc, max_loc = cv.minMaxLoc(res)
        # 例如:(-0.05772797390818596, 0.30968162417411804, (0, 0), (196, 1))
        top_left = max_loc[0]  # 横坐标
        return top_left


    def get_tracks(self, distance):
        '''滑动轨迹 '''

        tracks = []
        v = 0
        t = 0.2  # 单位时间
        current = 0  # 滑块当前位移
        distance += 10  # 多移动10px,然后回退
        while current < distance:
            if current < distance * 5 / 8:
                a = random.randint(1, 3)
            else:
                a = -random.randint(2, 4)
            v0 = v  # 初速度
            track = v0 * t + 0.5 * a * (t ** 2)  # 单位时间(0.2s)的滑动距离
            tracks.append(round(track))  # 加入轨迹
            current += round(track)
            v = v0 + a * t
        #回退到大致位置
        for i in range(5):
            tracks.append(-random.randint(1, 3))
        return tracks


if __name__ == '__main__':
    Zhihu_login().run()

  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值