一.内容简介:
实现对QQ自动模拟登录,以及解决滑块验证。
二.模块描述:
(1).qq_login.py:主程序
(2).test_distance.py:定位滑块,获取x轴方向
(3).info.json:个人账户(json数据格式)
三.实现:
(1)分析url:https://i.qq.com/;F12调试打开
(2)问题一:关于iframe框架作用域问题,会影响接下来定位不到元素。
(3)问题二:关于iframe多层跳入与跳出问题,会影响接下来定位不到滑块元素。
代码一:
import os
import re
import sys
sys.path.append(os.path.abspath(os.path.dirname(__file__)))
import time
import json
from parsel import Selector
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from test_distance import get_distance
class QQLogin(object):
def __init__(self):
self.bw = webdriver.Chrome()
def get_qq_page(self, url):
# 创建brower对象
bw = self.bw
# 全窗口显示
bw.maximize_window()
bw.get(url)
# 跳过作用域
bw.switch_to.frame('login_frame')
# page = bw.page_source
# 定位选择使用账号密码登录
swither_login = WebDriverWait(bw, 10, 0.5).until(EC.presence_of_element_located((By.ID, "switcher_plogin")))
swither_login.click()
# 获取个人用户信息(账号,密码)
with open('info.json', 'r', encoding="utf-8") as fr:
account = json.load(fr)
# 定位账号输入框
user_area = WebDriverWait(bw, 5, 0.5).until(EC.presence_of_element_located((By.ID, "u")))
user_area.click()
bw.find_element_by_id('u').send_keys(account["username"])
# 定位密码输入框
pwd_area = WebDriverWait(bw, 5, 0.5).until(EC.presence_of_element_located((By.ID, "p")))
pwd_area.click()
bw.find_element_by_id('p').send_keys(account["pwd"])
# 点击登录
login_submit = WebDriverWait(bw, 5, 0.5).until(EC.presence_of_element_located((By.ID, "login_button")))
time.sleep(1)
login_submit.click()
# 跳出iframe框架,到最外层
bw.switch_to.default_content()
# 定位下一个iframe2框架
iframe2 = bw.find_element_by_css_selector(".login_wrap > iframe")
# 跳进指定iframe2框架
bw.switch_to.frame(iframe2)
time.sleep(1)
# 跳进指定iframe3框架
iframe3 = bw.find_element_by_css_selector('#newVcodeIframe > iframe')
bw.switch_to.frame(iframe3)
time.sleep(1)
NO.1
NO.2
代码二:
前提是已经解决iframe问题。主要获取滑块背景图和滑块图
def get_area_img(self, brower):
# 定位滑块元素,获取滑块图片
slide_block = brower.find_element_by_id("slideBlock")
# 截图保存图片
slide_block.screenshot('./tag_img.png')
# 获取滑块图片的url
bg_img_url = brower.find_element_by_xpath('//img[@id="slideBg"]').get_attribute('src')
# print(bg_img)
options = webdriver.ChromeOptions()
# 设置无头浏览,以防关闭上一个窗口
options.add_argument('--headless')
bw2 = webdriver.Chrome(options=options)
bw2.get(bg_img_url)
# print(brower.page_source)
# 滑块图定位
bg_img = bw2.find_element_by_css_selector('body > img')
bg_img.screenshot('./bg_img.png')
NO.1:滑块背景图
NO.2:滑块图片
代码三:
展示目录
test_distance.py
import cv2
def detail_img(old_bg_img):
bg_img1 = cv2.imread(old_bg_img)
# 修改图片尺寸
bg_img_res = cv2.resize(bg_img1, (280, 163), )
# 保存修改后的图片
cv2.imwrite('bg_img_res.png', bg_img_res)
def get_distance(bg_image, slider_image):
detail_img('./bg_img.png')
bg_img = cv2.imread(bg_image)
slider_img = cv2.imread(slider_image)
# cv2.GaussianBlur: https://www.freesion.com/article/2473908556/
# 高斯滤波,(9,9)高斯内核:都必须为正数和奇数,也可以为零
bg_gus = cv2.GaussianBlur(bg_img, (9, 9), 0)
slider_guas = cv2.GaussianBlur(slider_img, (3, 3), 0)
# 识别图片边缘
# 127表示最小阈值,260表示最大阈值,用于进一步删选边缘信息(可以自己调试)
bg_edge = cv2.Canny(bg_gus, 127, 260)
slider_edge = cv2.Canny(slider_guas, 127, 255)
# RGB模式是(255,0,0),BGR模式是(0,0,255)
bg_img_gray = cv2.cvtColor(bg_edge, cv2.COLOR_GRAY2RGB)
slider_img_gray = cv2.cvtColor(slider_edge, cv2.COLOR_GRAY2RGB)
# 显示处理后的图片
# cv2.imshow('v1', bg_img_gray)
# cv2.imshow('v2', slider_img_gray)
# 在滑块背景图中匹配滑块。参数cv.TM_CCOEFF_NORMED是opencv中的一种归一化相关系数匹配法
res = cv2.matchTemplate(bg_img_gray, slider_img_gray, cv2.TM_CCOEFF_NORMED)
# min_val, max_val:矩阵最大值最小值
# min_loc, max_loc:索引最小值,最大值
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
# x轴坐标
tl = max_loc
th, tw = slider_img_gray.shape[:2]
br = (tl[0]+tw, tl[1]+th)
# 方框标记,可以在‘out.png’图片查看标记
cv2.rectangle(bg_img, tl, br, (0, 0, 255), 1)
cv2.imwrite('out.png', bg_img)
# print(min_val, max_val, min_loc, max_loc)
cv2.waitKey(0)
# print(tl[0])
return tl[0]
if __name__ == '__main__':
get_distance('./bg_img_res.png', './tag_img.png')
NO.1:边缘检测结果图
NO.2:标记结果图(红色方框是自己生成的):
代码四(完整):
# -*- coding:utf8 -*-
import os
import re
import sys
sys.path.append(os.path.abspath(os.path.dirname(__file__)))
import time
import json
from parsel import Selector
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from test_distance import get_distance
class QQLogin(object):
def __init__(self):
self.bw = webdriver.Chrome()
self.headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36',
'Connection': 'keep-alive',
'Host': 'aegis.qq.com'
}
def get_qq_page(self, url):
bw = self.bw
bw.maximize_window()
bw.get(url)
# 跳过作用域
bw.switch_to.frame('login_frame')
# page = bw.page_source
swither_login = WebDriverWait(bw, 10, 0.5).until(EC.presence_of_element_located((By.ID, "switcher_plogin")))
swither_login.click()
with open('info.json', 'r', encoding="utf-8") as fr:
account = json.load(fr)
user_area = WebDriverWait(bw, 5, 0.5).until(EC.presence_of_element_located((By.ID, "u")))
user_area.click()
bw.find_element_by_id('u').send_keys(account["username"])
pwd_area = WebDriverWait(bw, 5, 0.5).until(EC.presence_of_element_located((By.ID, "p")))
pwd_area.click()
bw.find_element_by_id('p').send_keys(account["pwd"])
# 点击登录
login_submit = WebDriverWait(bw, 5, 0.5).until(EC.presence_of_element_located((By.ID, "login_button")))
time.sleep(1)
login_submit.click()
# 跳出上层的iframe框架
bw.switch_to.default_content()
iframe2 = bw.find_element_by_css_selector(".login_wrap > iframe")
# 跳进指定iframe2框架
bw.switch_to.frame(iframe2)
time.sleep(1)
# 跳进指定iframe3框架
iframe3 = bw.find_element_by_css_selector('#newVcodeIframe > iframe')
bw.switch_to.frame(iframe3)
time.sleep(1)
self.get_area_img(bw)
self.get_distance(bw)
def get_area_img(self, brower):
# # 获取去全图图片
# full_img_screen = brower.get_screenshot_as_png()
# # 左上(左上节点), 右下(右下节点)
# cut_info = (774, 161, 1054, 320)
# # 将图片转成二进制流形式
# full_img = Image.open(BytesIO(full_img_screen))
# # 二进制流截图
# cut_img = full_img.crop(cut_info)
# # 保存区域截图
# cut_img.save("./yz.png")
# 定位滑块元素,获取滑块图片
slide_block = brower.find_element_by_id("slideBlock")
slide_block.screenshot('./tag_img.png')
bg_img_url = brower.find_element_by_xpath('//img[@id="slideBg"]').get_attribute('src')
# print(bg_img)
options = webdriver.ChromeOptions()
options.add_argument('--headless')
bw2 = webdriver.Chrome(options=options)
bw2.get(bg_img_url)
# print(brower.page_source)
# 背景图定位
bg_img = bw2.find_element_by_css_selector('body > img')
bg_img.screenshot('./bg_img.png')
def get_distance(self, brower):
# 获取距离
distance = get_distance('./bg_img_res.png', './tag_img.png')
# 定位按钮
slider_block = WebDriverWait(brower, 10, 0.5).until(EC.presence_of_element_located((By.ID, "slideBlock")))
# 创建动作链
action = webdriver.ActionChains(brower)
action.click_and_hold(slider_block).perform()
page = brower.page_source
element = Selector(page)
# 获取slideBlock css样式
slideBlock_style = element.css('#slideBlock::attr("style")').get()
slideBlock_left_val = re.findall(r'left: (\d+|\d+.\d+)px;', slideBlock_style)[0]
print(distance, slideBlock_left_val)
diff_distance = float(int(distance) - int(slideBlock_left_val))
action.move_by_offset(diff_distance, 0)
action.release().perform()
def main(self):
self.get_qq_page('https://i.qq.com/')
if __name__ == '__main__':
qq_login = QQLogin()
qq_login.main()
备注:有些图片未能识别滑块缺口,导致不能自动登录。( 仅供学习使用,违法必究!)