利用python selenium+cv2破解qq空间登录滑动验证码(无法获得完整图情况)(上)

之前动手用python写了一个qq空间自动点赞的脚本,登录方法采用的是selenium获取网页截图然后返回二维码截图,用手机qq扫一扫进行登录.

然后又用flask简单的写成了网页,放到了自己的服务器上,这样每次只需要输入网址就会弹出二维码,手机qq扫一扫便可以登录,十分方便.

但由于qq空间的机制,cookie有效期为48小时.因此就必须每两天进行一次登录(有邮箱提醒).虽然还算比较方便,但是对于程序猿来说这些还不够完美.

以上为另一种思路,供大家参考,接下来我将讲述我如何利用cv2实现破解滑动验证码

1.首先,下载cv2库

根据python官方文档

 

我选择了

pip install opencv-contrib-python

2.通过selenium获取一定数量的验证码,看看有没有共同的规律

直接放代码,但我不知道为什么每次只能获取4次,然后就停止了.可能是tx的某种反爬侦测.

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
import time
import requests

url='https://i.qq.com/'
username=123456789#其实内容无所谓因为不滑动验证码是不会post账号密码的
password=123456789

opt=Options()
opt.add_argument('--headless')
driver=webdriver.Chrome(chrome_options=opt)
i=13
while i<30:
    driver.get(url)
    login_frame=driver.find_element_by_id('login_frame')
    driver.switch_to_frame(login_frame)
    driver.find_element_by_xpath('/html/body/div[1]/div[9]/a[1]').click()
    nameI=driver.find_element_by_xpath('/html/body/div[1]/div[5]/div/div[1]/div[3]/form/div[1]/div/input').send_keys(username)
    pwdI=driver.find_element_by_xpath('/html/body/div[1]/div[5]/div/div[1]/div[3]/form/div[2]/div[1]/input').send_keys(password)
    submit=driver.find_element_by_xpath('/html/body/div[1]/div[5]/div/div[1]/div[3]/form/div[4]/a/input').click()
    time.sleep(1)
    frame=driver.find_element_by_id('tcaptcha_iframe')
    driver.switch_to_frame(frame)
    imgUse=driver.find_element_by_xpath('/html/body/div[1]/div[3]/div[2]/div[1]/div[2]/img').get_attribute('src')
    imgNeed=driver.find_element_by_xpath('/html/body/div[1]/div[3]/div[2]/div[1]/div[3]/img').get_attribute('src')
    imgU=requests.get(imgUse)
    imgN=requests.get(imgNeed)
    imgU=imgU.content
    imgN=imgN.content
    with open('E:/Download/qq空间滑动验证码/'+str(i)+'.jpg','wb') as f:
        f.write(imgU)    
    with open('E:/Download/qq空间滑动验证码/'+str(i)+'s.jpg','wb') as f:
        f.write(imgN)
    i+=1

这些就是我获得到的图片和小方块

放大观看每一张图

其中一张

会发现每个图片的拼图部分都有一个白边

这就给了我们定位的好方法

3.利用cv库根据轮廓来分出拼图的位置----根据阈值转化成二值化图

用到函数有

cv2.imread(文件file,标记)

标记:1是彩色,0是灰度

cv2.namedWindow(窗口名,窗口模式)

 窗口模式:cv2.WINDOW_AUTOSIZE(自动大小),cv2.WINDOW_NORMAL(手动大小)

cv2.imshow(窗口名,图片变量名)

字面意思,不解释

cv2.waitKey(毫秒)

没有这行的话会闪过去 

cv2.threshold (图片变量名, 阈值, 填充色, 填充方法)

源图片必须是单通道,所以刚开始的cv2.read()要加参数0,表示读取灰度图

阈值在0~255之间,我不了解阈值的大小,所以进行了一番不同阈值的显示测试

填充色即字面意思

填充方法见下面链接大神的博客,我先给出截图

需要注意的是返回值是两个,需要用两个变量来接收 

另一位大神关于cv阈值的简单解释

简单的利用代码看了不同阈值下的成果(ps自己也初学cv,没研究怎么一起显示)

import cv2

for i in range(1,9):
    img=cv2.imread('./jpg/'+str(i)+'.jpg',0)#0为灰度图,注意路径,我设置的是程序所在目录的子文件夹
    re,img1=cv2.threshold(img,95,255,0)#返回了两个值,但我们用不上第一个返回值
    re,img2=cv2.threshold(img,150,255,0)
    re,img3=cv2.threshold(img,220,255,0)
    re,img4=cv2.threshold(img,240,255,0)
    re,img5=cv2.threshold(img,250,255,0)
    re,img6=cv2.threshold(img,260,255,0)
    cv2.namedWindow('img',cv2.WINDOW_AUTOSIZE)
    cv2.imshow('img',img1)
    cv2.waitKey(0)#不加这个的话会一闪而逝
    cv2.imshow('img',img2)
    cv2.waitKey(0)
    cv2.imshow('img',img3)
    cv2.waitKey(0)
    cv2.imshow('img',img4)
    cv2.waitKey(0)
    cv2.imshow('img',img5)
    cv2.waitKey(0)
    cv2.imshow('img',img6)
    cv2.waitKey(0)

效果如下

95
95
150

 

220

 

240

 

250

 

260

需要注意的是,接下来我们要使用的轮廓,只会找连续的颜色,因此阈值设置太高的话,线条不连续,会检测不到

4.获得轮廓

用到的新的函数有

cv2.findContours(图片变量(二值图),轮廓的检索模式,轮廓的近似办法)

 图片变量要二值图,这也是为什么在第三步需要用到阈值

轮廓的检索模式有四种:

    cv2.RETR_EXTERNAL只检测外轮廓
    cv2.RETR_LIST检测的轮廓不建立等级关系
    cv2.RETR_CCOMP建立两个等级的轮廓,上面的一层为外边界,里面的一层为内孔的边界信息。如果内孔内还有一个连通物体,这个物体的边界也在顶层。
    cv2.RETR_TREE建立一个等级树结构的轮廓。

轮廓的近似方法(部分):

    cv2.CHAIN_APPROX_NONE存储所有的轮廓点(占内存)
    cv2.CHAIN_APPROX_SIMPLE只保留终点坐标,例如一个矩形轮廓只需4个点来保存轮廓信息(省内存)

咱们采用cv2.RETR_TREEcv2.CHAIN_APPROX_NONE

注意!!! 

报错:too many values to unpack说明你接收用的变量数量不对

cv2.findContours函数的返回值有过很多变化

  openCV2.0版本返回值有两个

  openCV3.0版本返回值有三个

  openCV4.0版本返回值又变成了只有两个

我使用4.0版本,因此返回值是 轮廓,轮廓的索引 

  其中轮廓是一个list,索引是个高级的东西.我也不知道是啥反正用不上 

cv2.drawContours(图片变量(三通道), 轮廓, 第几个轮廓,颜色, 线宽度)

图片变量要三通道图,不过后来试了下灰度图也不报错了,但是灰度图不方便观察轮廓线,还是推荐三通道图

轮廓就是之前获得的那个list

第几个轮廓是个int类型的变量,-1代表所有轮廓一起加载

颜色可以是个元组,如(255,0,0)表示蓝色.或者直接int类型的255也是蓝色 

线宽度,字面意思,-1表示填充模式(不懂)

import cv2

for i in range(1,9):
    img=cv2.imread('./jpg/'+str(i)+'.jpg',0)
    re,img1=cv2.threshold(img,125,255,0)
    cv2.namedWindow('img',cv2.WINDOW_AUTOSIZE)
    cv2.imshow('img',img1)
    contours,b=cv2.findContours(img1.copy(),cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
    cv2.waitKey(0)
    
    for j in range(0,len(contours)-1):
        image=cv2.imread('./jpg/'+str(i)+'.jpg',1)#获取三通道图
        image=cv2.drawContours(image, contours, j, 255, 3)#绘制轮廓
        cv2.imshow('img',image)
        cv2.waitKey(0)

但是单纯的画轮廓,每张图都检测到了几百个轮廓,这可不行 

随便某一个轮廓

5.轮廓的筛选 

以下是看云上的openCV-Python中文教程节选片段

openCV-Python中文教程 

 首先,我们可以根据轮廓面积进行筛选

经过我的测试,正常的拼图面积在6000-8000之间

因此,咱们先这样

import cv2

for i in range(1,9):
    img=cv2.imread('./jpg/'+str(i)+'.jpg',0)
    re,img1=cv2.threshold(img,125,255,0)
    cv2.namedWindow('img',cv2.WINDOW_AUTOSIZE)
    cv2.imshow('img',img1)
    contours,b=cv2.findContours(img1.copy(),cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
    cv2.waitKey(0)
    
    for j in range(0,len(contours)-1):
        area=cv2.contourArea(contours[j])
        if area<6000 or area>8000:
            continue
        print(area)
        image=cv2.imread('./jpg/'+str(i)+'.jpg',1)
        image=cv2.drawContours(image, contours, j, 255, 3)#绘制轮廓
        cv2.imshow('img',image)
        cv2.waitKey(0)

这样基本就筛选出了准确的图案

但光有面积是不够的,你会发现这样的情况 

这个图案的面积是7385.5,也在我们的筛选范围内

因此第二步,我们要根据图案的重心位置来进行进一步判断 

根据我的测试,拼图出现的位置只在后方,即x坐标大于500的地方

因此,代码改写如下

import cv2

for i in range(1,9):
    img=cv2.imread('./jpg/'+str(i)+'.jpg',0)
    re,img1=cv2.threshold(img,125,255,0)
    cv2.namedWindow('img',cv2.WINDOW_AUTOSIZE)
    cv2.imshow('img',img1)
    contours,b=cv2.findContours(img1.copy(),cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
    cv2.waitKey(0)
    
    for j in range(0,len(contours)-1):
        M = cv2.moments(contours[j])  # 计算第一条轮廓的各阶矩,字典形式
        try:#防止分母等于0报错
            center_x = int(M["m10"] / M["m00"])
            center_y = int(M["m01"] / M["m00"])
        except:
            continue#如果分母等于0,那一定不是我们的目标,因此可以直接跳过当前循环
        area=cv2.contourArea(contours[j])
        if area<6000 or area>8000 or center_x<500:
            continue
        print(area)
        print(center_x)
        image=cv2.imread('./jpg/'+str(i)+'.jpg',1)
        image=cv2.drawContours(image, contours, j, 255, 3)#绘制轮廓
        image=cv2.circle(image, (center_x, center_y), 7, 128, -1)#绘制中心点
        cv2.imshow('img',image)
        cv2.waitKey(0)
       

至此,我们已经可以准确的定位到拼图的位置,接下来需要的就是计算出需要移动的距离,以及selenium模拟人的操作

我会整理下我的代码全部放到porn啊不是,全部放到github上。地址在这里:qq空间自动点赞

 

  • 5
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值