深度解析:【验证码识别】OpenCV挑战网易易盾滑动验证码
使用java+selenium+OpenCV破解网易易盾滑动验证码戳这里→康康你手机号在过多少网站注册过!!!友情推荐:新一代安全短信谷歌图形验证码在AI面前已经形同虚设,所以谷歌宣布退出验证码服务,那么当所有的图形验证码都被破解时《腾讯防
使用java + selenium + OpenCV破解网易易盾滑动验证码
戳这里→
康康你手机号在过多少网站注册过!!!
友情推荐:
新一代安全短信
谷歌图形验证码在AI 面前已经形同虚设,所以谷歌宣布退出验证码服务, 那么当所有的图形验证码都被破解时
《腾讯防水墙滑动拼图验证码》
《百度旋转图片验证码》
《网易易盾滑动拼图验证码》
《顶象区域面积点选验证码》
《顶象滑动拼图验证码》
《极验滑动拼图验证码》
《使用深度学习来破解 captcha 验证码》
《验证码终结者-基于CNN+BLSTM+CTC的训练部署套件》
* 验证码地址:https://dun.163.com/trial/jigsaw
* 使用OpenCv模板匹配
* Java + Selenium + OpenCV
产品样例
接下来就是见证奇迹的时刻!
注意!!!
·
在模拟滑动时不能按照相同速度或者过快的速度滑动,需要向人滑动时一样先快后慢,这样才不容易被识别。
模拟滑动代码↓↓↓
/**
* 模拟人工移动
* @param driver
* @param element页面滑块
* @param distance需要移动距离
*/
public
static
void
move
(
WebDriver driver
,
WebElement element
,
int
distance
)
throws
InterruptedException
{
int
randomTime
=
0
;
if
(
distance
>
90
)
{
randomTime
=
250
;
}
else
if
(
distance
>
80
&&
distance
<=
90
)
{
randomTime
=
150
;
}
List
<
Integer
>
track
=
getMoveTrack
(
distance
-
2
)
;
int
moveY
=
1
;
try
{
Actions actions
=
new
Actions
(
driver
)
;
actions
.
clickAndHold
(
element
)
.
perform
(
)
;
Thread
.
sleep
(
200
)
;
for
(
int
i
=
0
;
i
<
track
.
size
(
)
;
i
++
)
{
actions
.
moveByOffset
(
track
.
get
(
i
)
,
moveY
)
.
perform
(
)
;
Thread
.
sleep
(
new
Random
(
)
.
nextInt
(
300
)
+
randomTime
)
;
}
Thread
.
sleep
(
200
)
;
actions
.
release
(
element
)
.
perform
(
)
;
}
catch
(
Exception
e
)
{
e
.
printStackTrace
(
)
;
}
}
/**
* 根据距离获取滑动轨迹
* @param distance需要移动的距离
* @return
*/
public
static
List
<
Integer
>
getMoveTrack
(
int
distance
)
{
List
<
Integer
>
track
=
new
ArrayList
<
>
(
)
;
// 移动轨迹
Random random
=
new
Random
(
)
;
int
current
=
0
;
// 已经移动的距离
int
mid
=
(
int
)
distance
*
4
/
5
;
// 减速阈值
int
a
=
0
;
int
move
=
0
;
// 每次循环移动的距离
while
(
true
)
{
a
=
random
.
nextInt
(
10
)
;
if
(
current
<=
mid
)
{
move
+=
a
;
// 不断加速
}
else
{
move
-=
a
;
}
if
(
(
current
+
move
)
<
distance
)
{
track
.
add
(
move
)
;
}
else
{
track
.
add
(
distance
-
current
)
;
break
;
}
current
+=
move
;
}
return
track
;
}
操作过程
/**
* 获取网易验证滑动距离
*
* @return
*/
public
static
String dllPath
=
"C://chrome//opencv_java440.dll"
;
public
double
getDistance
(
String bUrl
,
String sUrl
)
{
System
.
load
(
dllPath
)
;
File bFile
=
new
File
(
"C:/EasyDun_b.png"
)
;
File sFile
=
new
File
(
"C:/EasyDun_s.png"
)
;
try
{
FileUtils
.
copyURLToFile
(
new
URL
(
bUrl
)
,
bFile
)
;
FileUtils
.
copyURLToFile
(
new
URL
(
sUrl
)
,
sFile
)
;
BufferedImage bgBI
=
ImageIO
.
read
(
bFile
)
;
BufferedImage sBI
=
ImageIO
.
read
(
sFile
)
;
// 裁剪
cropImage
(
bgBI
,
sBI
,
bFile
,
sFile
)
;
Mat s_mat
=
Imgcodecs
.
imread
(
sFile
.
getPath
(
)
)
;
Mat b_mat
=
Imgcodecs
.
imread
(
bFile
.
getPath
(
)
)
;
//阴影部分为黑底时需要转灰度和二值化,为白底时不需要
// 转灰度图像
Mat s_newMat
=
new
Mat
(
)
;
Imgproc
.
cvtColor
(
s_mat
,
s_newMat
,
Imgproc
.
COLOR_BGR2GRAY
)
;
// 二值化图像
binaryzation
(
s_newMat
)
;
Imgcodecs
.
imwrite
(
sFile
.
getPath
(
)
,
s_newMat
)
;
int
result_rows
=
b_mat
.
rows
(
)
-
s_mat
.
rows
(
)
+
1
;
int
result_cols
=
b_mat
.
cols
(
)
-
s_mat
.
cols
(
)
+
1
;
Mat g_result
=
new
Mat
(
result_rows
,
result_cols
,
CvType
.
CV_32FC1
)
;
Imgproc
.
matchTemplate
(
b_mat
,
s_mat
,
g_result
,
Imgproc
.
TM_SQDIFF
)
;
// 归一化平方差匹配法TM_SQDIFF 相关系数匹配法TM_CCOEFF
Core
.
normalize
(
g_result
,
g_result
,
0
,
1
,
Core
.
NORM_MINMAX
,
-
1
,
new
Mat
(
)
)
;
Point matchLocation
=
new
Point
(
)
;
MinMaxLocResult mmlr
=
Core
.
minMaxLoc
(
g_result
)
;
matchLocation
=
mmlr
.
maxLoc
;
// 此处使用maxLoc还是minLoc取决于使用的匹配算法
Imgproc
.
rectangle
(
b_mat
,
matchLocation
,
new
Point
(
matchLocation
.
x
+
s_mat
.
cols
(
)
,
matchLocation
.
y
+
s_mat
.
rows
(
)
)
,
new
Scalar
(
0
,
255
,
0
,
0
)
)
;
Imgcodecs
.
imwrite
(
bFile
.
getPath
(
)
,
b_mat
)
;
return
matchLocation
.
x
+
s_mat
.
cols
(
)
-
sBI
.
getWidth
(
)
+
12
;
}
catch
(
Throwable
e
)
{
e
.
printStackTrace
(
)
;
return
0
;
}
finally
{
bFile
.
delete
(
)
;
sFile
.
delete
(
)
;
}
}
/**
* 图片亮度调整
*
* @param image
* @param param
* @throws IOException
*/
public
void
bloding
(
BufferedImage image
,
int
param
)
throws
IOException
{
if
(
image
==
null
)
{
return
;
}
else
{
int
rgb
,
R
,
G
,
B
;
for
(
int
i
=
0
;
i
<
image
.
getWidth
(
)
;
i
++
)
{
for
(
int
j
=
0
;
j
<
image
.
getHeight
(
)
;
j
++
)
{
rgb
=
image
.
getRGB
(
i
,
j
)
;
R
=
(
(
rgb
>>
16
)
&
0xff
)
-
param
;
G
=
(
(
rgb
>>
8
)
&
0xff
)
-
param
;
B
=
(
rgb
&
0xff
)
-
param
;
rgb
=
(
(
clamp
(
255
)
&
0xff
)
<<
24
)
|
(
(
clamp
(
R
)
&
0xff
)
<<
16
)
|
(
(
clamp
(
G
)
&
0xff
)
<<
8
)
|
(
(
clamp
(
B
)
&
0xff
)
)
;
image
.
setRGB
(
i
,
j
,
rgb
)
;
}
}
}
}
// 判断a,r,g,b值,大于256返回256,小于0则返回0,0到256之间则直接返回原始值
private
int
clamp
(
int
rgb
)
{
if
(
rgb
>
255
)
return
255
;
if
(
rgb
<
0
)
return
0
;
return
rgb
;
}
/**
* 生成半透明小图并裁剪
*
* @param image
* @return
*/
private
void
cropImage
(
BufferedImage bigImage
,
BufferedImage smallImage
,
File bigFile
,
File smallFile
)
{
int
y
=
0
;
int
h_
=
0
;
try
{
// 2 生成半透明图片
bloding
(
bigImage
,
75
)
;
for
(
int
w
=
0
;
w
<
smallImage
.
getWidth
(
)
;
w
++
)
{
for
(
int
h
=
smallImage
.
getHeight
(
)
-
2
;
h
>=
0
;
h
--
)
{
int
rgb
=
smallImage
.
getRGB
(
w
,
h
)
;
int
A
=
(
rgb
&
0xFF000000
)
>>>
24
;
if
(
A
>=
100
)
{
rgb
=
(
127
<<
24
)
|
(
rgb
&
0x00ffffff
)
;
smallImage
.
setRGB
(
w
,
h
,
rgb
)
;
}
}
}
for
(
int
h
=
1
;
h
<
smallImage
.
getHeight
(
)
;
h
++
)
{
for
(
int
w
=
1
;
w
<
smallImage
.
getWidth
(
)
;
w
++
)
{
int
rgb
=
smallImage
.
getRGB
(
w
,
h
)
;
int
A
=
(
rgb
&
0xFF000000
)
>>>
24
;
if
(
A
>
0
)
{
if
(
y
==
0
)
y
=
h
;
h_
=
h
-
y
;
break
;
}
}
}
smallImage
=
smallImage
.
getSubimage
(
0
,
y
,
smallImage
.
getWidth
(
)
,
h_
)
;
bigImage
=
bigImage
.
getSubimage
(
0
,
y
,
bigImage
.
getWidth
(
)
,
h_
)
;
ImageIO
.
write
(
bigImage
,
"png"
,
bigFile
)
;
ImageIO
.
write
(
smallImage
,
"png"
,
smallFile
)
;
}
catch
(
Throwable
e
)
{
System
.
out
.
println
(
e
.
toString
(
)
)
;
}
}
/**
*
* @param mat
* 二值化图像
*/
public
static
void
binaryzation
(
Mat mat
)
{
int
BLACK
=
0
;
int
WHITE
=
255
;
int
ucThre
=
0
,
ucThre_new
=
127
;
int
nBack_count
,
nData_count
;
int
nBack_sum
,
nData_sum
;
int
nValue
;
int
i
,
j
;
int
width
=
mat
.
width
(
)
,
height
=
mat
.
height
(
)
;
// 寻找最佳的阙值
while
(
ucThre
!=
ucThre_new
)
{
nBack_sum
=
nData_sum
=
0
;
nBack_count
=
nData_count
=
0
;
for
(
j
=
0
;
j
<
height
;
++
j
)
{
for
(
i
=
0
;
i
<
width
;
i
++
)
{
nValue
=
(
int
)
mat
.
get
(
j
,
i
)
[
0
]
;
if
(
nValue
>
ucThre_new
)
{
nBack_sum
+=
nValue
;
nBack_count
++
;
}
else
{
nData_sum
+=
nValue
;
nData_count
++
;
}
}
}
nBack_sum
=
nBack_sum
/
nBack_count
;
nData_sum
=
nData_sum
/
nData_count
;
ucThre
=
ucThre_new
;
ucThre_new
=
(
nBack_sum
+
nData_sum
)
/
2
;
}
// 二值化处理
int
nBlack
=
0
;
int
nWhite
=
0
;
for
(
j
=
0
;
j
<
height
;
++
j
)
{
for
(
i
=
0
;
i
<
width
;
++
i
)
{
nValue
=
(
int
)
mat
.
get
(
j
,
i
)
[
0
]
;
if
(
nValue
>
ucThre_new
)
{
mat
.
put
(
j
,
i
,
WHITE
)
;
nWhite
++
;
}
else
{
mat
.
put
(
j
,
i
,
BLACK
)
;
nBlack
++
;
}
}
}
// 确保白底黑字
if
(
nBlack
>
nWhite
)
{
for
(
j
=
0
;
j
<
height
;
++
j
)
{
for
(
i
=
0
;
i
<
width
;
++
i
)
{
nValue
=
(
int
)
(
mat
.
get
(
j
,
i
)
[
0
]
)
;
if
(
nValue
==
0
)
{
mat
.
put
(
j
,
i
,
WHITE
)
;
}
else
{
mat
.
put
(
j
,
i
,
BLACK
)
;
}
}
}
}
}
// 延时加载
private
static
WebElement
waitWebElement
(
WebDriver driver
,
By by
,
int
count
)
throws
Exception
{
WebElement webElement
=
null
;
boolean
isWait
=
false
;
for
(
int
k
=
0
;
k
<
count
;
k
++
)
{
try
{
webElement
=
driver
.
findElement
(
by
)
;
if
(
isWait
)
System
.
out
.
println
(
" ok!"
)
;
return
webElement
;
}
catch
(
org
.
openqa
.
selenium
.
NoSuchElementException
ex
)
{
isWait
=
true
;
if
(
k
==
0
)
System
.
out
.
(
"waitWebElement("
+
by
.
toString
(
)
+
")"
)
;
else
System
.
out
.
(
"."
)
;
Thread
.
sleep
(
50
)
;
}
}
if
(
isWait
)
System
.
out
.
println
(
" outTime!"
)
;
return
null
;
}
注意:有一个问题还没有解决,还无法区分阴影部分是黑色还是白色。 因为两种的情况不同,所以处理方式也不同。阴影部分为黑底时需要转灰度和二值化,为白底时不需要。黑底使用归一化平方差匹配算法 TM_SQDIFF ,而白底使用相关系数匹配算法 TM_CCOEFF。
有找到区分方法的大佬可以私信作者或者在评论区留言哦。
戳这里→
康康你手机号在过多少网站注册过!!!
友情推荐:
新一代安全短信