模拟验证Word文件下载.docx
《模拟验证Word文件下载.docx》由会员分享,可在线阅读,更多相关《模拟验证Word文件下载.docx(10页珍藏版)》请在冰豆网上搜索。
assert(img:
ok(),"
下载验证码失败"
Crop(4,3,56,18)
save("
c:
\\test\\test.jpg"
)--保存到硬盘
--折分图片,指定一行四列
img2,img3,img4,img5=img:
split(1,4);
img2:
\\test\\0001.jpg"
)
img3:
\\test\\0002.jpg"
img4:
\\test\\0003.jpg"
img5:
\\test\\0004.jpg"
image.del(img);
如何确定图片后缀名
在整个验证码识别过程中,格式与后缀名一定不能搞错,否则就会失败。
通常:
asp的验证码是bmp格式,php的验证码是png格式,其他验证码很多是jpg格式。
简单的,在验证码上右键点选“图片另存为”,就可以看到格式(不一定准确)。
另外,你可以用UltraEdit等以二进制方式打开看文件头部
首先下载:
str=web.getURL("
string.save(str,"
\\test.bin"
然后用UE打开test.bin看文件头部(第一行)
jpg文件头部有JFIF字眼
png文件头部有PNG字眼
gif文件头部有GIF字眼
如果你搞不清楚,这时候就不要指定后缀名
//vwww.***.com/test.asp"
"
这样就可以下载了
二、生成验证码样本数据库
复制下面的代码并粘贴到fap程序的「脚本区块」内,然后点击"
回放运行"
,最后再点击"
读取源代码"
。
你就可以在ApeML源代码最后面的「数据区块」中看到生成的验证码样本了。
将「数据区块」的内容复制需要使用验证码识别的fap模拟程序中覆盖「数据区块」即可。
localtkey={A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0};
--在字典中添加所有数字键
fori=0,9,1do
tkey[tostring(i)]=0;
end;
--如果一个字符有多个样本,例如5A.jpg5B.jpg5C.jpg
fork,vinpairs(tkey)do
if((#k)~=2)then--如果元素键名不是两位字符
tkey[k.."
A"
]=0;
B"
C"
tkey[k]=nil;
--删除单字符的键名
end;
--k参数为键,v参数表示值一个典型的tkeyle迭代器回调函数
loadtkey=function(k,v)
localimg=image.new();
img:
load("
C:
\\test\\"
..k.."
.jpg"
assert(img:
.."
\n不是有效的图片"
bpp
(1);
bpp(24);
--通过上面两句,轻松去掉验证码上的杂色杂点
Crop(1,0,9,10);
--修剪单个字符
median
(2);
--中值滤波进一步去杂点
tkey[k]=string.encode(img:
getBytes("
*.jpg"
),"
--因为转换到字符串还是二进制,所以用base64进行编码
image.del(img);
--遍历表tkey的所有元素,调用loadtkey加载图片文件
fork,vinpairs(tkey)do
loadtkey(k,v);
--把所有图片保存到数据岛,
ape:
saveTable(tkey,"
验证码样本"
三、验证码识别
将下面的代码添加到fap模拟程序最前面的init脚本区块中即可
--从数据区块读取base64编码的图片数据
codekey=ape:
loadTable("
localtimg={};
--这是一个图像数组,用来储存还原后的验证码样本的图片数据
--必须进行一个转换,因为codekey里面只是base64编码的普通字符串,而timg将是真正的图片对象(二进制数据)
--还原到图片对象
toImage=function(k,v)
localstr=string.decode(v,"
--首先进行base64解码,将纯文本转换为二进制数据
setBytes(str,"
--将二进制数据还原为图像
timg[k]=img;
--载入验证码样本
tkey=ape:
--验证样本
toImage(k,v);
--转换为图像
--转换图片验证码到字符串的函数
functionImgToString(img)
functiontest(imgX)--test是一个被包含在函数中的内部函数
sleep(0);
locallimit=(60*20)+(60*20);
--最小相似度local关键字声明为局部变量
localchr="
;
--读取的字符
--testimg是一个被包含在函数中的内部函数,作为table.foreach的回调函数,k参数表示键,v参数表示值
testimg=function(k,v)
--调用image.testXX()函数得出相似度,类似的函数还有image.testX()image.test()
localn=imgX:
testXX(timg[k]);
if(n<
limit)then--比较最小相似度
limit=n;
chr=k.."
--遍历timg表,并调用testimg函数
fork,vinpairs(timg)do
testimg(k,v);
returnstring.left(chr,1);
--返回读取到的字符串首字符(如果每个字符有多个样本)
--修剪图片
image.Crop(img,4,3,56,18)
--上面的过程必须与下载样本时的代码完全一致。
--使用split函数分割图片
localimg2,img3,img4,img5=img:
win.messagePrint("
正在检测图片,请稍候...."
returntest(img2)..test(img3)..test(img4)..test(img5);
需要识别验证码的地方添加类似下面的代码:
img=image.new()
)
--因为刷新了验证码与页面不一致,把验证码画到屏幕上
localx,y=mouse.getPos()
paint(x,y,60,20)
localstr=ImgToString(img);
--下面我们把验证码的每个字符都转换为大写,并控制键盘顺序按键
code1=string.upper(string.sub(str,1,1));
code2=string.upper(string.sub(str,2,2));
code3=string.upper(string.sub(str,3,3));
code4=string.upper(string.sub(str,4,4));
key.press(100,code1,code2,code3,code4);
上面我们用了模拟按键的方法输入验证码。
实际上大多时候可以用更简单的方法,如下:
ele=wb:
getEle("
验证码控件名字"
ele:
setAttribute("
value"
str)
为什么我的验证码与页面上不一样
因为我们使用img:
getURL读取验证码时已经刷新了验证码。
所以验证码与页面上显示的并不一样,您只需要识别最新的验证码即可。
如何直接获取页面的上图片,而不是重新下载
有些验证码是绑定页面的,必须识别页面上的验证码才行。
那么可以使用image.capture函数直接抓屏屏幕上的图片即可。
请参考:
image.capture函数。
更好的方法是使用ele:
exec("
Copy"
)函数直接拷贝页面上的图片到剪贴板。
然后使用img:
getClipBD()获取图片。
请参考:
)函数img:
getClipBD()函数
四、关于剪切图片
看上面的示意图,Crop就是选取绿色方框内的区域去清除绿色方框外面的区域.
必须保证里面的面积正好可以平均分成四块(假设这里是四个验证码字符)
这样以后调用img:
split(1,4)就正好分成四个字符了
分成四份的小图片其宽度应当正好是上面的红色小方块的宽度。
高度与绿色方框一样,我这里画的参次不齐是为了让大家看清楚。
如果你Crop的参数值不对,那么split就出错了.
下载验证码图片以后,可以使用图像编辑软件打开高倍放大。
五、使用种子填充算法去除验证码上的干扰线
模拟精灵识别验证码的能用是强大的,一个函数即可以去除杂色杂点。
bpp
(1)
bpp(24)
经过上面两句代码的处理,速度很快,所有背景、干扰点、杂色荡然无存。
但是有时候验证码中有大量的干扰线,并且位置随机变动的太历害,
这时候我们在处理验证码以前首先去除这些干扰线并准确的去除背景提取字符.
下面是一个模拟精灵初步处理后的验证码图片.已经去除了杂色、杂点.但是上面还是有干扰线.
一个可选的办法是用中值滤波再处理一下。
一个函数调用就可以,但
是这样虽然去掉了干扰线,原来的字符也被少量的破坏了。
下面是使用种子填充算法去除干扰线的源代码,不但能去除杂点,
而且可以去除周围的空白(提取位置随机变化的验证码),
稍加修改还能有更多的用途.
下面是自动处理以后的效果
下面是全部的源代码:
--[[
用一个table结构{x=0;
y=0}表示图像上的「坐标点」
用一组点构成table结构表示图像上的一条「线」。
所有相连的黑色的点被认为是一条「连通线」。
找出最长的一条「连通线」,被认为是字符,其他的认为是杂点。
算法原理与种子填充算法相似。
首先让用img:
bpp函数处理为黑白图片,并初步去除杂色。
先找到一个黑点,创建一个表示「坐标点」对象,并添加到「连通线」中。
然后在黑点周围8个点中,再找黑色的点,找到就添加到「连通线」,这样一直递归下去
直到遍历图像所有点,可能有几块。
清除杂点使用方法
image.scan(img);
清除杂点并切去掉周围的空白
image.scan(img,true);
--]]
functionimage.scan(img,crop)
--用一个table数组记录所有的「连通线」
image.scan的参数必须是一个有效的图片"
localtlines={};
--首先计算出图片的高度宽度,避免重复的调用
localw=img:
width();
localh=img:
height();
--[[以table形式定义一个数组,对应图象中的每个点。
作用相当一个开关,首先值为false,但黑点首次被遍历到时。
把这个值变为true。
下次,再找到这个点时忽略。
避免重复加入连通线。
--]]
localtchked={};
fori=0,w,1
do
tchked[i]={};
forj=0,h,1
do
tchked[i][j]=false;
-----去噪
--首先计算出各点的颜色值,避免在循环递归中重复的取
localtcl={};
fori=0,w,1
tcl[i]={};
tcl[i][j]=img:
getPos(i,j);
--[[
算点数函数
参数x,y坐标
参数tab所属连通线;
--]]
local
function
seed(x,y,tab)
---出界了则返回
if(x<
0
ory<
orx>
wory>
h)
then
return;
---点的颜色为白色时,返回,不处理。
if(tcl[x][y]==16777215)
---值为1,则计数加1,返回
if
(tchked[x][y])
return;
else
table.insert(tab,{x=x,y=y}
--添加到连通线里
tchked[x][y]=true;
---当值为0时,把值置为1。
seed(x+1,y-1,tab);
seed(x,y-1,tab);
seed(x-1,y-1,tab);
seed(x-1,y,tab);
seed(x+1,y,tab);
seed(x-1,y+1,tab);
seed(x,y+1,tab);
returnseed(x+1,y+1,tab);
--这里可以用一个尾调用(参考教程中的函数部份),加快递归的速度。
---------------------------
----遍历图像中的所有点
---如果是黑色的点,而且没有被计过数,则调用seed函数。
if(tcl[i][j]==0
and
(nottchked[i][j]))
then
localtab={}
seed(i,j,tab);
table.insert(tlines,tab);
--添加一条连通线
--现在tlines里记录了的有的连通线,我们现在需要根据连通线的长度排序
sproc=
function(l,l2)
return
table.maxn(l)>
table.maxn(l2);
--长的连通线排到前面
table.sort(tlines,sproc)
--把图像全部画成白色的点
forj=0,h,1
setPos(i,j,16777215);
--然后把最长的一条连通线画上去
fori,pointin
ipairs(tlines[1])
setPos(point.x,point.y,0);
--如果需要去掉周围的空白
if(crop)then
localn=table.maxn(tlines[1])
--排序最长连通线中的所有坐标点
function(pt,pt2)
return
(pt.x<
pt2.x);
--*左的排前面
table.sort(tlines[1],sproc);
localx,x2=tlines[1][1].x,tlines[1][n].x;
(pt.y<
pt2.y);
--*上的排前面
localy,y2=tlines[1][1].y,tlines[1][n].y;
Crop(x,y,x2+1,y2)