selenium自动登录QQ邮箱(附带滑动解锁)

问题分析:登录+滑动解锁

其实登录账号的部分本来很简单,用selenium打开QQ邮箱官网:https://mail.qq.com 然后切换frame输入帐号

和密码点击登录即可,但是部分账号,或者可以说是异地登录的QQ账号需要滑动解锁验证码才能继续登录(下图)
在这里插入图片描述
看到这张图我们应该不难想到:

1.我们需要模拟人拖动按钮
2.按钮拖动的距离=拼图间的距离
这个明确了之后那接下来我们先看看拼图间的距离到底怎么算。登录虽然不难,但还是写一下,免得说我偷懒0.0

1.1 登录

# coding = utf-8
# QQ邮箱自动登录—————滑动验证码
import os
import random
import sys
import time

import cv2
import numpy as np
import requests
from PIL import Image as Im
from selenium import webdriver
from selenium.webdriver import ActionChains

class QQ_Email(object):	
	root = sys.path[0] + "/pic/"
	driver = webdriver.Chrome(executable_path="chromedriver.exe")
	# 代码1.1 目前只用到webdriver和time库 其他的会在下面用到
	def Email(self, user, pwd):
	    """
	    :param user: 账号
	    :param pwd: 密码
	    """
	    # 1.1 登录邮箱
	    start_url = "https://mail.qq.com"
	    time.sleep(2)
	    driver.get(start_url)
	    time.sleep(2)
	
	    # 切换frame。login_frame是该登录窗口iframe的id
	    driver.switch_to.frame("login_frame")
	
	    try:
	        # 点击选择帐号密码登录
	        driver.find_element_by_id("switcher_plogin").click()
	    except Exception as e:
	        print(e)
	
	    # 休息1s
	    time.sleep(1)
	
	    # 输入帐号 将user填入id是u的输入框
	    driver.find_element_by_id("u").clear()
	    driver.find_element_by_id("u").send_keys(user)
	    time.sleep(1)
	    # 输入密码 将pwd填入id是p的输入框
	    driver.find_element_by_id("p").clear()
	    driver.find_element_by_id("p").send_keys(pwd)
	    time.sleep(1)
	
	    # 点击登录按钮之前的URL
	    click_before_url = driver.current_url
	
	    # 点击登录 登录按钮的id是login_button
	    driver.find_element_by_id("login_button").click()
	    time.sleep(2)

# main方法
if __name__ == '__main__':
    # 为了实现异地登录 随意定义一个QQ号(反正我们的目的是滑动解锁0.0),如果直接提示帐号密码错误没有验证码的话就再随意编一个QQ号
    run = QQ_Email()
    run.Email("1453000281", "qwgejhw")

运行一下 应该就能看到我们要的滑动验证码了

1.2 获取验证码图片

我们在运行完上面的代码之后验证码应该出来了,首先我们需要将其中的拼图和完整图片下载下来用于后面的距离计算。
查看页面源码,找到大、小两张图片的url↓
点击大图查看元素
在这里插入图片描述
点击小图查看元素↓
在这里插入图片描述
以上选中的这两张图片就是我们后面要用来计算滑动距离的图片
要获取到图片需要两步:

1.获取到图片的链接(如上图红框框中已经能看到了)
2.根据链接将图片下载到本地处理
回到刚才的代码 我们需要先加个判断来识别是否出现了滑动验证码(有的时候会直接提示帐号密码错误)——一定要切换到frame框架中再进行识别是否出现滑动验证码
只要判断这个"安全验证"的提示就可以说明是有滑动验证码的,反之没有。
在这里插入图片描述

	if "安全验证" in driver.page_source:
	   print("有验证码")
	   os.system("pause")
	   self.Verification_code()
	else:
    	print("无安全验证,登录成功!!!")

这块代码写完我们基本上实现了登录和判断是否出现滑动验证码的功能,不多BB我们继续↓

出现滑动验证码的时候我们先点击刷新

在这里插入图片描述
此处要加入两个方法用来解决: 下载图片的问题和计算拼图还原的问题

我们先下载图片到本地 然后通过处理图片来计算拼图还原的距离

    def pic_download(self, url, pic_type):
        """
        :param url:  是图片的链接
        :param pic_type: 区分左侧小拼图和大图,大图传big,小图传small
        :return: 图片下载到本地,返回图片保存路径
        """
        url = url
        path = root + pic_type + '.png'
        try:
            if not os.path.exists(root):
                # os.mkdir(root)
                os.makedirs(root)
            if os.path.exists(path):
                os.remove(path)
            r = requests.get(url)
            r.raise_for_status()
            # 使用with语句可以不用自己手动关闭已经打开的文件流
            with open(path, "wb") as f:  # 开始写文件,wb代表写二进制文件
                f.write(r.content)
                # print(f.name)
            print("{}图片下载完成".format(pic_type))
            return f.name

        except Exception as e:
            print("获取失败!" + str(e))
            self.pic_download(url, pic_type)

到这里图片下载的方法就ok了↑ ,可以直接调用**self.pic_download()**方法下载图片,然后继续写计算拼图还原的方法↓

    def get_distance(self, small_url, big_url):
        """
        :param small_url: 是小图的路径(本地)
        :param big_url: 是大图的路径(本地)
        :return: 计算出移动的距离
        """
        # 引用上面的图片下载
        otemp = self.pic_download(small_url, 'small')

        time.sleep(2)

        # 引用上面的图片下载
        oblk = self.pic_download(big_url, 'big')

        # 计算拼图还原距离
        target = cv2.imread(otemp, 0)
        template = cv2.imread(oblk, 0)
        w, h = target.shape[::-1]
        temp = root + 'temp.jpg'
        targ = root + 'targ.jpg'
        cv2.imwrite(temp, template)
        cv2.imwrite(targ, target)
        target = cv2.imread(targ)
        target = cv2.cvtColor(target, cv2.COLOR_BGR2GRAY)
        target = abs(255 - target)
        cv2.imwrite(targ, target)
        target = cv2.imread(targ)
        template = cv2.imread(temp)
        result = cv2.matchTemplate(target, template, cv2.TM_CCOEFF_NORMED)
        x, y = np.unravel_index(result.argmax(), result.shape)
        # 缺口位置
        print((y, x, y + w, x + h))

        # 调用PIL Image 做测试
        image = Im.open(oblk)

        xy = (y + 20, x + 20, y + w - 20, x + h - 20)
        # 切割
        imagecrop = image.crop(xy)
        # 保存切割的缺口
        imagecrop.save(sys.path[0] + "/pic/new_image.jpg")
        return y

到这里计算拼图还原的距离的方法基本上就完成了↑

有了下载图片计算拼图还原的方法 我们就可以直接调用self.get_distance方法计算拼图还原的距离

    def Verification_code(self):
        # 切换最初的frame中
        driver.switch_to.default_content()

        # 切换frame
        driver.switch_to.frame('login_frame')

        # 切换带有刷新按钮的frame
        driver.switch_to.frame(driver.find_element_by_xpath('//*[@id="newVcodeIframe"]/iframe'))

        # 点击刷新 id为e_reload
        driver.find_element_by_id('e_reload').click()

        # 获取图片链接
        big_url = driver.find_element_by_id('slideBg').get_attribute('src')
        small_url = driver.find_element_by_id('slideBlock').get_attribute('src')

        # 下载图片并计算拼图还原的距离
        y = self.get_distance(small_url, big_url)

        # 获取当前网页链接,用于判断拖动验证码后是否成功,如果拖动后地址没变则为失败
        slide_before_url = driver.current_url

        # 获取蓝色拖动按钮对象
        element = driver.find_element_by_id('tcaptcha_drag_button')

        # 计算distance(记住要用浮点型数字,不然计算后会取整)
        distance = y * (280.0 / 680.0) - 31
        print('distance:', distance)

写到这里 基本上我们可以计算出拼图还原的距离了。
是不是开始看着觉得很有道理…突然看到最后两行代码 ** distance = y * (280.0 / 680.0) - 31 是什么意思**?
别着急慢慢解释…通过上面的代码已经知道了 y 就是图片还原的距离,但是我们还少考虑了2点:

图片的起始位置其实不是最左侧,而是向右偏移了一点
我们从下载到本地的图片尺寸是否跟网页上的图片尺寸一致 ? 答案当然是否定的。
我们先看一下拼图起始的位置
在这里插入图片描述
很清晰的能看到拼图到左边的有一段距离 那到底是多少呢 ? 我已经找人用专业的工具加粗样式测过了30-32左右
以上是拼图到左侧的距离 然后我们再看一下我们在本地处理并计算的图片尺寸和网页上的图片有什么区别
先看本地处理过后的图片
在这里插入图片描述
很明显能够看到长是680

我们再看一下网页上的…没错还是我找的人用专业工具给测的…280*160(px),笨笨的老方法帮你们理解一下
在这里插入图片描述
在这里插入图片描述
所以我们讲了这么多会发现 :

按钮需要滑动的距离(网页) = 拼图的还原距离(本地图片) * (网页上的长度 / 本地图片的长度) -31(多出来的起始位置)

也就是前面会让人疑惑的 distance = y * (280.0 / 680.0) - 30 当然 这些都因实际情况而定

到了这一步 可以说我们最难的部分已经解决了

有了滑动距离 我们就只剩拖动按钮这一步了,先看代码

		# 接着上面的 distance = y * (280 / 680) - 31 继续
        # 模拟人为拖动按钮
        has_gone_dist = 0
        remaining_dist = distance
        # distance += randint(-10, 10)
        # 按下鼠标左键
        ActionChains(driver).click_and_hold(element).perform()
        time.sleep(0.5)
        while remaining_dist > 0:
            ratio = remaining_dist / distance
            if ratio < 0.2:
                # 开始阶段移动较慢
                span = random.randint(5, 8)
            elif ratio > 0.8:
                # 结束阶段移动较慢
                span = random.randint(5, 8)
            else:
                # 中间部分移动快
                span = random.randint(10, 16)
            ActionChains(driver).move_by_offset(span, random.randint(-5, 5)).perform()
            remaining_dist -= span
            has_gone_dist += span
            time.sleep(random.randint(5, 20) / 100)

        ActionChains(driver).move_by_offset(remaining_dist, random.randint(-5, 5)).perform()
        ActionChains(driver).release(on_element=element).perform()

到这里按钮拖动就已经完成了,但图片分析不是人在操作毕竟有误差,所以我们需要判断滑动按钮是否已经成功,如果失败了我们得让程序继续循环去刷新验证码然后拖动直到成功为止
在这里插入图片描述

		# 紧接着上面的拖动按钮代码
		# 获取当前的网页地址
        slide_after_url = driver.current_url

        # frame切回到上一层
        driver.switch_to.parent_frame()

        # 判断拖动按钮后网页地址是否有改变,如果变了则说明登录成功(失败则停留在该页面)
        if slide_before_url == slide_after_url:
            try:
            	# 切换到最初的frame框架中
            	driver.switch_to.default_content()
            	# 将frame切换到 login_frame(也就是之前的登录frame)--(有两层frame框架,逐层切入)
            	driver.switch_to.frame(login_frame')
            	driver.switch_to.frame("tcaptcha_iframe")
                print(driver.find_element_by_id('guideText').text)
                print('滑动失败!')
                self.Verification_code()  # 若滑动失败,则重复此操作
            except:
                print('帐号密码有误!')

        else:
            print('登录成功!')
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值