之前动手用python写了一个qq空间自动点赞的脚本,登录方法采用的是selenium获取网页截图然后返回二维码截图,用手机qq扫一扫进行登录.
然后又用flask简单的写成了网页,放到了自己的服务器上,这样每次只需要输入网址就会弹出二维码,手机qq扫一扫便可以登录,十分方便.
但由于qq空间的机制,cookie有效期为48小时.因此就必须每两天进行一次登录(有邮箱提醒).虽然还算比较方便,但是对于程序猿来说这些还不够完美.
以上为另一种思路,供大家参考,接下来我将讲述我如何利用cv2实现破解滑动验证码
根据python官方文档
我选择了
pip install opencv-contrib-python
直接放代码,但我不知道为什么每次只能获取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
这些就是我获得到的图片和小方块
放大观看每一张图
会发现每个图片的拼图部分都有一个白边
用到函数有
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)
效果如下
需要注意的是,接下来我们要使用的轮廓,只会找连续的颜色,因此阈值设置太高的话,线条不连续,会检测不到
用到的新的函数有
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_TREE和cv2.CHAIN_APPROX_NONE
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)
但是单纯的画轮廓,每张图都检测到了几百个轮廓,这可不行
以下是看云上的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)