vb.net+cefsharp+VBA+js+ddddocr混合编程网络图片ocr识别

vb.net+cefsharp+VBA+js+ddddocr混合编程网络图片ocr识别

网友【wodewan】共享的文章《带带弟弟OCR,纯VBA本地获取网络验证码整体解决方案》(https://club.excelhome.net/forum.php?mod=viewthread&tid=1666823&extra=page%3D1&ordertype=1&page=1),的确好。好就好在,代码开源、心思巧妙、对于不同类型的验证码图片,识别率在90%及以上。向网友【wodewan】和DdddOcr作者致以崇敬。

在【wodewan】共享代码下,彻底解决了:俗话说得,最后一公里。那就是最终得到了验证码文字。由于本人非专业码字,仅是业余爱好,所以代码部分是否美观简洁强悍不是俺的菜,俺信奉的是能否解决问题,其次才是去追求技术升级。第一步都踏不出去,何来其它,呵呵。

分步解决思路:

一、获取网络验证码图片

至于技术网友提出的那些个专业术语,例如“跨域、同源”俺技术不到位暂时也不想看,管它咋滴,只要能够达到目的(识别出图片中的字符)就行。

二、识别验证码图片中的内容为可编辑文字

主要针对【字符】类型图片中的字符测试。其它的类型(相似图、拉动等)暂不涉及。

三、自动填写三码(准考、身份、验证)【用户名、密码、验证码】,(查询)按钮一点击。

由于不能展示实际网站,只能测试到OCR识别图片中字符这一步。至于编程获得查询结果,分析结果,那就另外的话题了,在技术层面,这都不是问题。

四、编程环境:

操作系统:widows server 2022

编程语言:微软visual studio 2022(vb.net、c#)、VBA(office Excel 2022)

64位浏览器控件:谷歌内核cefsharp (chromium_90.6.7_windows64)(chromium_109.1.110_windows64)技术版(可以展示图文音视)。

为啥不用其他cefsharp版本呢?因为要考虑兼容windows 7(10),不支持xp。

为啥是64位而不是32位,或者兼有?能逮住老鼠的都是好猫,碰到哪个用哪个,不纠结。

为啥不用微软的【WebBrowser 】控件呢?不是不想用,是有些网站不支持该浏览器控件,显示不出来网络验证码图片。

其它:网页js、ddddocr库

五、技术实现思路描述:

1、在vb.net form窗体中,必须的引入语句

Imports System.IO

Imports System.Runtime.InteropServices

Imports CefSharp

‘根据自己的需要增减

【Imports System.ComponentModel

Imports System.Data.OleDb

Imports System.Data.SqlClient

Imports System.IO

Imports System.Runtime.InteropServices

Imports System.Text.RegularExpressions

Imports CefSharp】

2、form窗体load阶段

System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = False

'程序初始化的时候设置了这个属性,而且在你的控件中使用的都是微软Framework类库中的控件的话,系统就不会再抛出错误了。

【一】、获取网页中的验证码图片
1、三种截图获取验证码图片方式

位置截图、网页直接截图、JS元素截图

(1)优缺点:

位置截图:需要计算图片在网页中的位置大小,不能遮盖要截取图片的部位。

网页直接截图:无论是否遮盖网页,都能截取到图片,但需要后期对网页截图计算验证码图片在网页中的位置大小

JS元素截图:无论是否遮盖网页,都能从网页中截取到验证码图片。

提示:还有更多方式可以获取到,暂不介绍了。

推荐:【JS元素截图】

2、JS元素截图

通过元素ID属性(更简便)获取所需验证码图片信息。

以网页中的img元素为例,获取它的src网址

Dim ID关键词As String="img元素ID"

Dim JS语句 As String = "var divElement = document.getElementById('" + ID关键词 + "');divElement.src"

3、必要的转换过程

不能直接用vb.net PictureBox 控件使用获得的验证码图片网址获取图像,否则得到的是不停变换的网络验证码图片,而不是想要得到的那个正确的图片

提示:经过这三步,已经得到了网页上的验证码图片,下一步就是使用网友【wodewan】共享的文章《带带弟弟OCR,纯VBA本地获取网络验证码整体解决方案》中的代码,去识别验证码图片并转换为可编辑验证码字符

4、保存前三步获取的图片

(也可以参考其他网友关于不保存直接识别的文章,这里首先实际解决获取网页中的验证码图片,识别字符问题)

【二】、vb.net封装VBA,识别验证码

'调用VBA,获取识别后的验证码

CallVbaFunction()

提示:网友【wodewan】共享的Excel VBA,需要在原有基础上改写并增加一个新的Function 返回值(path As String)函数,以便vb.net从其函数中获取验证码返回值

【三】、分别获取三码,并进行自动填写

将(准考、身份、验证)【用户名、密码、验证码】分别填写到网页的对应元素中,自动填写和登录

例如:

准考码填写框类型:input,ID=ksbh

身份码填写框类型:input,ID=zjhm

验证码填写框类型:input,ID=yzm

提示:自动填写代码,建议写入到Timer控件中,然后通过按钮开启

TM登录.Enabled = True

---------------------------

【四】、需要改进的部分:

1、由于网页网络验证码图片识别过程中,需要保存图片、需要调用Excel VBA,会造成返回值微妙的延迟。

暂时没有解决将网友【wodewan】共享的Excel VBA改写为vb.net直接执行

2、如果涉及多人查询,可以自行添加access等数据库轮查

3、元素的ID查询,需要手工确认。如果没有ID,需要使用其它js语句(根据:元素name等)去准确定位网页元素

4、可以逐步添加【准考码、身份码、密码】防错机制

5、有的网站网络验证码,并不是放在网页的img元素中,而是放在div等容器中,实现图片提取过程无非就是先提取div的背景图片网址,然后新建一个img放入页面中,然后就可以了。

当然,也可以通过直接截取div等元素的图片方式获取到图片,实现过程也不复杂,无非就是定位元素获取图片。

六、ddddocr识别库简单介绍

对(jpg、png)等各种类型的图片,ddddocr识别率相当高,经过测试,实际识别率90%以上。

不需要对获取的图片进行处理,在直接获取到的彩色图片模式下的识别率就足以了,所以也就省略了那些黑白化、二值化、分割的过程。

* vb.net代码的过程实现

参考了诸多网友的c#代码,就不一一感谢了。都是开源共享的。本来想将全部代码改写为vb.net语言,无奈水平所限,暂时没有实现。

一、获取网页中的验证码图片

Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click

     '---------------------------------------------------------------------------------

     '1、获取ID元素图片URL

     Dim ID关键词 As String = "valiCode" '图片验证码,元素的ID

     Dim JS语句 As String

     '获取ID元素的src

     '<img id="valiCode" style="cursor: pointer;height:22px; line-height:22px; vertical-align:middle;" src="//i2.wp.com/" οnclick="this.src=this.src+'?'" alt="看不清?点击更换">

     JS语句 = "var img元素 = document.getElementById('" + ID关键词 + "');img元素.src"

     'http://

     Dim JS语句返回值 As JavascriptResponse = Await ChromiumWebBrowser1.EvaluateScriptAsync(JS语句)

     Dim 网址 As String = JS语句返回值.Result

     '验证获取的值是否正确,正式运行时注销该语句

     TextBox6.Text = 网址

     '----------------------------------------------------------------------------------------------------

     '2、将指定ID元素中的图片,转换成base64编码,并显示到vb.net的PictureBox图片控件中

     JS语句 = "var image = document.getElementById('" + ID关键词 + "');//说明

     var canvas1 = document.createElement('canvas');

       var context = canvas1.getContext('2d');

       canvas1.width = image.naturalWidth;

       canvas1.height = image.naturalHeight;

       context.drawImage(image, 0, 0);

     var imageURL = canvas1.toDataURL();imageURL "

     '获取指定ID元素图片的base64编码

     Dim response = Await ChromiumWebBrowser1.EvaluateScriptAsync(JS语句)

     '3、-----------------必要的转换过程,不能直接用PictureBox 控件使用获得的验证码图片网址获取图像,否则得到的是不停变换的网络验证码图片,而不是想要得到的那个正确的图片

     '将base64编码中的无效字符去除

     Dim base64Image = response.Result

     Dim Base64Qua As String = base64Image.Replace("data:image/png;base64,", "").Trim()

     '从数据库拿到保存的base64文件,将前面的前缀去除

     ' 去除前缀base64编码,转换为 byte[] 类型

     Dim imageBytes As Byte() = Convert.FromBase64String(Base64Qua)

     ' 创建内存流并加载图像数据,到PictureBox图片控件

     Using ms As New MemoryStream(imageBytes)

         Dim img As Drawing.Image = Drawing.Image.FromStream(ms)

         ' 设置 PictureBox 控件的图像属性

         PictureBox3.Image = img

         '--------------------

         Dim 图像宽度 = img.Width * 4

         Dim 图像高度 = img.Height * 4

         Dim 图片信息 = "图像宽度 " & 图像宽度 & vbCrLf & "图像高度 " & 图像高度 & vbCrLf

         '图片宽度 256        图片高度 101,放大到这时最清晰

         '图像宽度 100

         '图像高度 40

         TextBox5.Text = 图片信息

         '--------------------

         'BackColor背景色:透明色 Transparent

         PictureBox3.BackColor = Color.Transparent

         'Normal 表示图像将放置在控件的左上角;如果图像比控件大,则会对其下边缘和右边缘进行剪裁。

         'StretchImage,Zoom,都会改变图片大小以适应picturebox,但是zoom感觉会被调整的更小

         'StretchImage 表示图像的大小将调整为控件的大小。

         'zoom:图像的大小递增或者递减原有大小的比例,使原有图片不变形

         'AutoSize 表示控件的大小将调整为图像的大小。

         'CenterImage 表示图像将在控件中居中;如果图像比控件大,则会对图片的各外边缘进行剪裁。

         PictureBox3.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom

         '等比例显示放大后的图像

         PictureBox3.Width = 图像宽度

         PictureBox3.Height = 图像高度

         'PictureBox4,直接加载得到的网络图片网址,是为了和转换后得到的显示在PictureBox3的那个正确图片作比较

         'PictureBox4.LoadAsync(网址)

         PictureBox4.ImageLocation = 网址

         TextBox6.Text = "放大后宽度 " & PictureBox3.Width & vbCrLf & "放大后高度 " & PictureBox3.Height & vbCrLf

     End Using

 End Sub

二、保存彩色图片

Sub 保存彩色图片()

    '1、获取验证码图片

    '2、保存彩色图片

    '3、图片识别文字()

    '----------------------------

    '保存彩色图片

    'Application.StartupPath 应用程序路径

    'System.IO.Directory.GetCurrentDirectory() 应用程序路径,不包括

    Dim 获取程序运行路径 As String = System.IO.Directory.GetCurrentDirectory()

    Dim 保存图片路径 = 获取程序运行路径 & "access数据库网络验证码图片"

    Dim 网络验证码图片名称 = "网络验证码图片.jpg"

    Dim 图片路径名称 = 保存图片路径 & 网络验证码图片名称

    '检查目录、文件是否存在。如果目录不存在就新建,文件存在就删除、或者覆盖

    'tempFilePath 为文件路径,检查路径是否存在,不存在则创建

    If Not System.IO.Directory.Exists(保存图片路径) Then

        System.IO.Directory.CreateDirectory(保存图片路径) '调用 CreateDirectory() 函数来创建文件夹

    End If

    '判断和创建可以放在一起。

    '创建空文件夹:

    'Directory.CreateDirectory(文件夹完整路径)

    '系统会自动判断文件夹是否存在,不存在就创建判断并创建空文件:

    'Using fs As New FileStream("f.txt", FileMode.OpenOrCreate)

    '    '你可以用这个FileStream做其它事情

    'End Using

    '---保存放大后图片-----------------

    '宽度、高度,放大因子为4倍

    Dim 保存放大后图片 As New System.Drawing.Bitmap(PictureBox3.Image, PictureBox3.Image.Width * 5, PictureBox3.Image.Height * 5)   '新建一个放大的图片

    保存放大后图片.Save(图片路径名称, System.Drawing.Imaging.ImageFormat.Jpeg)   '保存放大后图片

    'PictureBox1.Image.Save(图片路径名称, System.Drawing.Imaging.ImageFormat.Jpeg)

    图片识别文字()

End Sub

三、图片识别文字

Public Sub 图片识别文字()

    '1、获取验证码图片

    '2、保存彩色图片

    '3、图片识别文字()

    '----------------------------

    '-------------------------网友【wodewan】共享的文章《带带弟弟OCR,纯VBA本地获取网络验证码整体解决方案》中的代码,改写自原来的【main】,是为了在vb.net中得到验证码文字返回值,而不去操作Excel其它显示过程

    'Function 返回值(path As String)

    '    Dim address, str As String, bytearr() As Byte

    '    str = Pic2Base64(path)

    '    bytearr = StrConv(str, vbFromUnicode)

    '    Init

    '    address = Classification(bytearr(0))

    '    返回值 = StringFromPointerA(address)

    '    Shutdown

    'End Function

    '-------------------------

    Dim 获取程序运行路径 As String = System.IO.Directory.GetCurrentDirectory()

    Dim 保存图片路径 = 获取程序运行路径 & "access数据库网络验证码图片"

    Dim 网络验证码图片名称 = "网络验证码图片.jpg"

    Dim 图片路径名称 = 保存图片路径 & 网络验证码图片名称

    Dim Path = 图片路径名称

    '自定义Excel宏文件路径文件名

    Dim 要找的文件 = 获取程序运行路径 & "access数据库DdddOcr-V24.xlsm"

    Dim vbaApp As Object = CreateObject("Excel.Application") '创建Excel应用对象

    Try

        vbaApp.Visible = False 'False   'True 设置Excel为可见状态(可选)

        '打开工作簿并获取活动工作表

        Dim workbook As Object = vbaApp.Workbooks.Open(要找的文件)

        'Dim worksheet As Object = workbook.ActiveSheet

        '仅调用VBA函数,获取返回值,不显示Excel界面

        Dim result As String '= CStr(vbaApp.Run(GetStr(Path)))

        '给VBA的Function 返回值(path As String)函数,传递值,并返回结果

        result = CStr(vbaApp.Run("返回值", Path))

        'TextBox6.Text = "VBA函数返回结果:" & result

        TB验证码.Text = ""

        TB验证码.Text = result '显示到验证码文本框,以便下一步自动填写

        '关闭工作簿

        workbook.Close()

        'Marshal.ReleaseComObject(vbaApp)

        'vbaApp.Close()

        vbaApp.Quit

    Finally

        '释放Excel应用对象

        Marshal.ReleaseComObject(vbaApp)

        vbaApp = Nothing

    End Try

End Sub

四、自动登录

提示:需要一个计时器控件(timer)    

Private Sub TM登录_Tick(sender As Object, e As EventArgs) Handles TM登录.Tick

    Dim 准考码 = TextBox1.Text

    Dim 身份码 = TextBox2.Text

    Dim 验证码 = TextBox7.Text

    '用户名 <input class="user_name_input" name="username" id="username" type="text">

    Dim 准考码填写框ID = "ksbh"

    Dim 准考码填写 As String = "var test = document.getElementById('" + 准考码填写框ID + "');test.focus();test.value='" & 准考码 & "';"

    '网页加载情况

    '如果网页正在执行回发或客户端控件正在加载内容,则为 true;否则为 false。

    Dim 是否加载网页完毕 = ChromiumWebBrowser1.IsLoading()

    If 是否加载网页完毕 = False Then

        ChromiumWebBrowser1.ExecuteScriptAsync(准考码填写)

        Dim 身份码填写框ID = "zjhm"

        Dim 身份码填写 As String = "var test1 = document.getElementById('" + 身份码填写框ID + "');test1.focus();test1.value='" & 身份码 & "';"

        ChromiumWebBrowser1.ExecuteScriptAsync(身份码填写)

        '-------------------------

        Dim 验证码填写框ID = "yzm"

        Dim 验证码填写 As String = "var test1 = document.getElementById('" + 验证码填写框ID + "');test1.focus();test1.value='" & 验证码 & "';"

        ChromiumWebBrowser1.ExecuteScriptAsync(验证码填写)

        '-------------------------

        '登录

        Dim 查询按钮ID = "cx"

        '点击查询按钮

        ChromiumWebBrowser1.ExecuteScriptAsync("document.getElementById('" + 查询按钮ID + "').click();")

        '停止重复执行填写登录

        TM登录.Enabled = False

    End If

End Sub

五、对应的Excel VBA代码

自定义Excel路径文件名:DdddOcr-V24.xlsm

模块代码:

Option Explicit

#If VBA7 And Win64 Then

'声明32位和64位Access Excel等VBA兼容的API函数

'当VBA7和Win64都是True时(只有64的Excel才是这种情况),使用第一条Declare语句。在其他版本中,使用第二条Declare语句

Public Declare PtrSafe Function SetCurrentDirectory Lib "kernel32" Alias "SetCurrentDirectoryA" (ByVal lpPathName As String) As Long  '注意这是API函数的申明

Declare PtrSafe Sub Init Lib ".DdddOcr.dll" ()

Declare PtrSafe Sub Shutdown Lib ".DdddOcr.dll" Alias "Close" ()

Declare PtrSafe Function Classification Lib ".DdddOcr.dll" (ByRef aa As Byte) As LongPtr

Private Declare PtrSafe Function lstrlenA Lib "kernel32.dll" (ByVal lpString As LongPtr) As Long

Private Declare PtrSafe Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (ByVal Destination As LongPtr, ByVal Source As LongPtr, ByVal Length As Long)

#Else

#End If

   

Sub main()

    Dim path As String, i As Long, pathbase As String

     SetCurrentDirectory (Application.ActiveWorkbook.path) '很重要,设置当前激活目录

    pathbase = ThisWorkbook.path

    'For i = 1 To 8

        'path = ThisWorkbook.path & "pics" & i & ".png"

        path = ".网络验证码图片网络验证码图片.jpg" '1.png

        'Debug.Print (GetStr(path))

        'ThisWorkbook.Sheet1.Cells(1, 1) = GetStr(path)

        Sheets("Sheet1").Cells(1, 1) = GetStr(path)

      

       

    'Next

End Sub

Sub 返回值2()

Dim 当前文件所在目录路径, 当前文件所在目录路径2, 当前文件所在目录路径3, 当前文件所在目录路径4, 当前文件所在目录路径上一级目录 As String

SetCurrentDirectory (Application.ActiveWorkbook.path) '很重要,设置当前激活目录

'设置TextBox控件的属性,主要是:VBA TextBox换行

  With Sheet1.TextBox1

       '.Left = 50 '设置文本框左边位置

       '.Top = 50 '设置文本框顶部位置

       .MultiLine = True '将文本框设置为多行模式

       .ScrollBars = fmScrollBarsVertical '显示垂直滚动条

       '.Text = "这是一个" & vbCrLf & "多行文本框" '设置初始文本内容

  End With

'本函数用来在Excel文档中测试,需要在Sheet1添加一个activeX文本框TextBox1控件

'获取当前目录路径

'当前文件所在目录路径 = ThisWorkbook.path

当前文件所在目录路径2 = Application.ActiveWorkbook.path

'当前文件所在目录路径3 = Application.PathSeparator

'当前文件所在目录路径4 = ActiveWorkbook.path

当前文件所在目录路径上一级目录 = CurDir(ActiveWorkbook.path)

'Sheet1.TextBox1.Text = ThisWorkbook.path

'Sheet1.TextBox1.Text = Application.ActiveWorkbook.path

'Sheet1.TextBox1.Text = Application.PathSeparator

'Sheet1.TextBox1.Text = ActiveWorkbook.path

'获取上一级目录

 'MsgBox CurDir(ActiveWorkbook.path)

 'Sheet1.TextBox1.Text = CurDir(ActiveWorkbook.path)

Sheet1.TextBox1.Text = 当前文件所在目录路径 & vbCrLf & 当前文件所在目录路径2 & vbCrLf & 当前文件所在目录路径3 & vbCrLf & 当前文件所在目录路径4 & vbCrLf & 当前文件所在目录路径上一级目录

当前文件所在目录路径4 = ThisWorkbook.path

'当前文件所在目录路径上一级目录 = Mid(当前文件所在目录路径4, 1, InStrRev(当前文件所在目录路径4, ""))

'InStrRev()函数返回一个字符串在另一个字符串中的第一次出现。搜索从右到左。

'InStrRev(当前文件所在目录路径4, "")-1:-1,返回值不包括

当前文件所在目录路径上一级目录 = Left(当前文件所在目录路径4, InStrRev(当前文件所在目录路径4, ""))

Sheet1.TextBox1.Text = 当前文件所在目录路径上一级目录

'配合使用,才能获取:当前文件所在目录路径上一级目录。或者使用其他截取字段方式获取

ChDrive ActiveWorkbook.path

ChDir ActiveWorkbook.path

ChDir ".."

Sheet1.TextBox1.Text = CurDir(ActiveWorkbook.path)

End Sub

'用于vb,net调用后,返回识别结果

Function 返回值(path As String)

Dim address, str As String, bytearr() As Byte

    SetCurrentDirectory (Application.ActiveWorkbook.path) '很重要,设置当前激活目录

    str = Pic2Base64(path)

    bytearr = StrConv(str, vbFromUnicode)

    Init

        address = Classification(bytearr(0))

        返回值 = StringFromPointerA(address)

    Shutdown

End Function

'获取识别结果

Function GetStr(path As String)

    SetCurrentDirectory (Application.ActiveWorkbook.path) '很重要,设置当前激活目录

    Dim address, str As String, bytearr() As Byte

    str = Pic2Base64(path)

    bytearr = StrConv(str, vbFromUnicode)

   Init

        address = Classification(bytearr(0))

        GetStr = StringFromPointerA(address)

    Shutdown

End Function

'图片(地址)转换为Base64

Public Function Pic2Base64(strPicPath As String) As String

    Const adTypeBinary = 1 ' Binary file is encoded

    Dim objXML, objDocElem, objStream

    Set objStream = CreateObject("ADODB.Stream")

    objStream.Type = adTypeBinary

    objStream.Open

    objStream.LoadFromFile (strPicPath)

    Set objXML = CreateObject("MSXml2.DOMDocument")

    Set objDocElem = objXML.createElement("Base64Data")

    objDocElem.DataType = "bin.base64"

    objDocElem.nodeTypedValue = objStream.Read()

    Pic2Base64 = objDocElem.Text

    objStream.Close

    Set objXML = Nothing

    Set objDocElem = Nothing

    Set objStream = Nothing

End Function

'由字符串指针(内存地址)获取字符串

Public Function StringFromPointerA(ByVal pointerToString As LongPtr) As String

  

   

    Dim tmpBuffer()    As Byte

    Dim byteCount      As Long

    Dim retVal         As String

    byteCount = lstrlenA(pointerToString)

    If byteCount > 0 Then

        ReDim tmpBuffer(0 To byteCount - 1) As Byte

        Call CopyMemory(VarPtr(tmpBuffer(0)), pointerToString, byteCount)

    End If

    retVal = StrConv(tmpBuffer, vbUnicode)

    StringFromPointerA = retVal

End Function

提示1:VBA主要函数3个,其它函数或者过程可以忽略

1、vb.net调用【返回值】,得到可编辑验证码字符

Function 返回值(path As String)

2、图片(地址)转换为Base64

Public Function Pic2Base64(strPicPath As String) As String

3、由字符串指针(内存地址)获取字符串

Public Function StringFromPointerA(ByVal pointerToString As LongPtr) As String

提示2:VBA设置外部DLL库(非系统函数库)和其他文件的相对路径

问题:在使用网友【wodewan】《带带弟弟OCR,纯VBA本地获取网络验证码整体解决方案》VBA时,我们会调用外部的Dll文件,而此DLL文件会随着打包程序的发放,而放在不同的目录。由于Dll文件就放置在执行文件的一个相对路径下,这时在程序内部定义调用Dll文件时,其定义的路径为相对路径。然而很多时候程序并不能识别到定义的Dll文件路径。如何让程序自动识别相对路径,即为本问题所在!

解决办法:设置指定路径为当前系统目录即可。使用的API函数为SetCurrentDirectoryA

例子:

'下面的例子是,在VBA要执行的代码里,需要调用外部Dll文件"DdddOcr.dll"里的三个函数【Init、Shutdown、Classification】,而外部这个Dll文件放在当前激活文件目录下"DdddOcr.dll",在执行三个函数前,直接使用SetCurrentDirectoryA设置当前目录即可。

'另外,保存图片文件的目录【相对路径】也存储在当前目录之下

'DdddOcr.dll、DdddOcr-V2.xlsm、网络验证码图片(文件夹),要位于同一个文件内,也就是Excel文件DdddOcr-V2.xlsm所在的路径内

'系统函数无须指定所在路径

致谢:剑客Training
http://blog.163.com/shikang999@1 ... 896201510225924807/

提示3、VBA获取:当前文件所在目录路径、当前文件所在目录路径上一级目录

Sub 返回值2()

Dim 当前文件所在目录路径, 当前文件所在目录路径2, 当前文件所在目录路径3, 当前文件所在目录路径4, 当前文件所在目录路径上一级目录 As String

'设置TextBox控件的属性,主要是:VBA TextBox换行

  With Sheet1.TextBox1

       '.Left = 50 '设置文本框左边位置

       '.Top = 50 '设置文本框顶部位置

       .MultiLine = True '将文本框设置为多行模式

       .ScrollBars = fmScrollBarsVertical '显示垂直滚动条

       '.Text = "这是一个" & vbCrLf & "多行文本框" '设置初始文本内容

  End With

'本函数用来在Excel文档中测试,需要在Sheet1添加一个activeX文本框TextBox1控件

'获取当前目录路径

当前文件所在目录路径 = ThisWorkbook.path

当前文件所在目录路径2 = Application.ActiveWorkbook.path

当前文件所在目录路径3 = Application.PathSeparator

当前文件所在目录路径4 = ActiveWorkbook.path

当前文件所在目录路径上一级目录 = CurDir(ActiveWorkbook.path)

'Sheet1.TextBox1.Text = ThisWorkbook.path

'Sheet1.TextBox1.Text = Application.ActiveWorkbook.path

'Sheet1.TextBox1.Text = Application.PathSeparator

'Sheet1.TextBox1.Text = ActiveWorkbook.path

'获取上一级目录

'MsgBox CurDir(ActiveWorkbook.path)

'Sheet1.TextBox1.Text = CurDir(ActiveWorkbook.path)

Sheet1.TextBox1.Text = 当前文件所在目录路径 & vbCrLf & 当前文件所在目录路径2 & vbCrLf & 当前文件所在目录路径3 & vbCrLf & 当前文件所在目录路径4 & vbCrLf & 当前文件所在目录路径上一级目录

End Sub

提示4、VBA获取上一级目录

'配合使用,才能获取:当前文件所在目录路径上一级目录。

ChDrive ActiveWorkbook.path

ChDir ActiveWorkbook.path

ChDir ".."

Sheet1.TextBox1.Text = CurDir(ActiveWorkbook.path)

--------------------

或者使用其他截取字段方式获取

'首先获得当前目录路径

当前文件所在目录路径4 = ThisWorkbook.path

'使用mid、left函数截取字段方式

前文件所在目录路径上一级目录 = Mid(当前文件所在目录路径4, 1, InStrRev(当前文件所在目录路径4, ""))

或者

'-1,获取的路径不包括斜杠

当前文件所在目录路径上一级目录 = Left(当前文件所在目录路径4, InStrRev(当前文件所在目录路径4, "")-1)

* vb.net获取div元素背景图片

分为以下3个步骤:

比直接从网页img元素中获取多了一个步骤

'0、因为要在网站的网页中插入一个新建的img元素(通过网址)用来显示div背景图片,为了防止生成多个相同ID的元素,首先检查是否存在指定的ID元素,如果有,就删除,以便新建

'因为id本身的对象不能删除自己 ,所以要先通过 idObject.parentNode得到父节点,然后把本id对象传入进行删除.

'1、获取div背景图片URL地址

'将获取的图片网址的无效部分去除,只保留网址部分

''前5个字符:url("

'后2个字符:")

'2、创建<img>元素

'将<img>元素添加到网页中的某个元素中,通常是用img,document.body.afterend,不扰乱验证码网页原来布局

'3、将指定ID元素中的图片,转换成base64编码,并显示到vb.net的PictureBox3图片控件中

'获取指定ID元素图片的base64编码

'将base64编码中的无效字符去除,前缀去除

' 去除前缀base64编码,转换为 byte[] 类型

' 创建内存流并加载图像数据,到PictureBox图片控件

'等比例放大图像,以便观察图片

其它识别、自动填写等代码,参照

【* vb.net代码的过程实现】

Vb.net代码:

Private Async Sub Button56_Click(sender As Object, e As EventArgs) Handles Button56.Click

    '0、为了防止生成多个相同ID的元素,首先检查是否存在指定的ID元素,如果有,就删除,以便新建

    '因为id本身的对象不能删除自己 ,所以要先通过 idObject.parentNode得到父节点,然后把本id对象传入进行删除.

    Dim cc1 As String  = "function remove (id) { var idObject = document.getElementById(id); if (idObject != null) idObject.parentNode.removeChild(idObject); };remove ('xuni') "

    ChromiumWebBrowser1.ExecuteScriptAsync(cc1)

    '----------------------------------------------------------------------------------------------------

    '1、获取div背景图片地址

    cc1 = "var divElement = document.getElementById('code_img');var style = window.getComputedStyle(divElement);style.backgroundImage; "

    'url("https://")

    Dim cc2 = Await ChromiumWebBrowser1.EvaluateScriptAsync(cc1)

    Dim cc3 = cc2.Result

    Dim 网址 = Microsoft.VisualBasic.Mid(cc3, 6)

    '将获取的图片网址的无效部分去除,只保留网址部分

    ''前5个字符:url("

    '后2个字符:")

    'https://

    网址 = Microsoft.VisualBasic.Left(网址, Len(网址) - 2)

    'TextBox6.Text = cc2.Result

    '----------------------------------------------------------------------------------------------------

    '2、创建<img>元素

    cc1 = "var img = document.createElement('img');"

    '设置图片的路径和替代文本

    cc1 = "var img = document.createElement('img');img.src = '" + 网址 + "';img.alt ='图片';"

    '将<img>元素添加到网页中的某个元素中

    '将<img>元素添加到网页中

    'document.body.firstChild 页面标签<body>的第一个子元素,也就是插入body中,并放在最前面

    'beforebegin: 元素自身的前面。

    'afterbegin: 插入元素内部的第一个子节点之前。

    'beforeend: 插入元素内部的最后一个子节点之后。

    'afterend: 元素自身的后面。

    'document.body.insertBefore(a,b);的意思是在b的前面插入a

    'insertBefore(新节点,旧节点):即将新节点插入到旧节点之前。

    cc1 = "var img = document.createElement('img');img.src = '" + 网址 + "';img.alt ='图片';document.body.insertBefore(img, document.body.firstChild);"

    '----------------------------------------------------------------------------------------------------

    '在网页前面插入一个新的img图片,

    'alt 属性是一个必需的属性,它规定在图像无法显示时的替代文本。

    cc1 = "var img = document.createElement('img');img.id='xuni';img.src = '" + 网址 + "';img.alt ='图片';document.body.insertBefore(img,document.body.firstChild);"

    cc1 = "var img = document.createElement('img');img.id='xuni';img.src = '" + 网址 + "';document.body.insertBefore(img,document.body.afterend)"

    ''将其属性设置成隐藏

    ''必须保持显性显示,才能进行第3步,否会出错

    'cc1 = "var img = document.createElement('img');img.id='xuni';img.src = '" + 网址 + "';img.style.hidden = 'true';"

    ChromiumWebBrowser1.ExecuteScriptAsync(cc1)

    'Dim unused = ChromiumWebBrowser2.GetMainFrame().EvaluateScriptAsync(cc1)

    '----------------------------------------------------------------------------------------------------

    '3、将指定ID元素中的图片,转换成base64编码,并显示到vb.net的PictureBox3图片控件中

    'code_img  xuni

    cc1 = "var image = document.getElementById('xuni');//说明

    var canvas1 = document.createElement('canvas');

      var context = canvas1.getContext('2d');

      canvas1.width = image.naturalWidth;

      canvas1.height = image.naturalHeight;

      context.drawImage(image, 0, 0);

    var imageURL = canvas1.toDataURL();imageURL "

    '获取指定ID元素图片的base64编码

    Dim response = Await ChromiumWebBrowser1.EvaluateScriptAsync(cc1)

    '将base64编码中的无效字符去除

    Dim base64Image = response.Result

    Dim Base64Qua As String = base64Image.Replace("data:image/png;base64,", "").Trim()

    '从数据库拿到保存的base64文件,将前面的前缀去除

    ' 去除前缀base64编码,转换为 byte[] 类型

    Dim imageBytes As Byte() = Convert.FromBase64String(Base64Qua)

    ' 创建内存流并加载图像数据,到图片控件

    Using ms As New MemoryStream(imageBytes)

        Dim img As Drawing.Image = Drawing.Image.FromStream(ms)

        ' 设置 PictureBox 控件的图像属性

        PictureBox3.Image = img

        '--------------------

        Dim 图像宽度 = img.Width * 4

        Dim 图像高度 = img.Height * 4

        Dim 图片信息 = "图像宽度 " & 图像宽度 & vbCrLf & "图像高度 " & 图像高度 & vbCrLf

        '图片宽度 256        图片高度 101,放大到这时最清晰

        '图像宽度 100

        '图像高度 40

        TextBox5.Text = 图片信息

        '--------------------

        'BackColor背景色:透明色 Transparent

        PictureBox3.BackColor = Color.Transparent

        '图片宽度 256        图片高度 101,放大到这时最清晰

        'Normal 表示图像将放置在控件的左上角;如果图像比控件大,则会对其下边缘和右边缘进行剪裁。

        'StretchImage,Zoom,都会改变图片大小以适应picturebox,但是zoom感觉会被调整的更小

        'StretchImage 表示图像的大小将调整为控件的大小。

        'zoom:图像的大小递增或者递减原有大小的比例,使原有图片不变形

        'AutoSize 表示控件的大小将调整为图像的大小。

        'CenterImage 表示图像将在控件中居中;如果图像比控件大,则会对图片的各外边缘进行剪裁。

        PictureBox3.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom

        '等比例放大图像

        'PictureBox3.Width = 图像宽度 * 4

        'PictureBox3.Height = 图像高度 * 4

        PictureBox3.Width = 图像宽度 '* 4

        PictureBox3.Height = 图像高度 '* 4

        TextBox6.Text = "放大后宽度 " & PictureBox3.Width & vbCrLf & "放大后高度 " & PictureBox3.Height & vbCrLf

    End Using

    cc1 = "function remove (id) { var idObject = document.getElementById(id); if (idObject != null) idObject.parentNode.removeChild(idObject); };remove ('xuni') "

    ChromiumWebBrowser1.ExecuteScriptAsync(cc1)

    '----------------------------------------------------------------------------------------------------

End Sub

*关于cefshap、操作系统、IE浏览器几点提示

CefSharp中的cef:

Chromium Embedded Framework (CEF)是个基于Google Chromium项目的开源Web browser控件,支持Windows, Linux, Mac平台。除了提供C/C++接口外,也有其他语言的移植版。因为基于Chromium浏览器引擎(开源软件),所以CEF支持Webkit & Chrome中实现的HTML5的特性,并且在性能上面,也比较接近Chrome。CEF还提供的如下特性:自定义插件、自定义协议、自定义JavaScript对象和扩展;可控制的resource loading, navigation, context menus等等。

简言之,Cef支持HTML,CSS,JavaScript,可以在CS中像web网站那样操作。与winform中自带的webBrowser一样,只是CefSharp是独立的,基于Chrome浏览器,而webBrowser基于IE浏览器。

而CefSharp后面的sharp就是c#语言的简称,应用于c#语言框架中使用的cef,所以还有适用于Java的,go的,Python的cef版本。

微软于2022年6月15日永久关闭IE浏览器,放弃对IE浏览器的安全维护和版本升级。继任者是微软旗下的 Edge 浏览器。但Edge 浏览器的核心,也是经历过多个内核以后,之后采用了谷歌浏览器内核cef。

Internet Explorer (IE) 11 是 Internet Explorer 的最后一个主要版本。 自 2022 年 6 月 15 日起,某些版本的 Windows 10 不再支持 Internet Explorer 11 桌面应用程序。

操作系统:Windows 7,2023 年 1 月 10 日终止支持。Microsoft Edge 版本 109 是支持此操作系统的最后一个浏览器版本。 Microsoft Edge 版本 109于 2023 年 1 月 12 日当周发布。

chrome浏览器控件cef109.1.18(2022 年 11 月发布),是支持win7的最后版本。

chrome浏览器控件cef49(2016年1月发布),是支持Windows XP和vista的最后版本。

cef84以后的版本不支持flash(已淘汰技术),这是最后一个支持flash的版本。

微软WebBrowser 控件不支持H5(H5 指的是 HTML5,即网页使用的 HTML 代码 —— 第五代超文本标记语言。)等最新技术,缺乏对移动等设备(电脑、平板、手机)操作系统(安卓、iOS、WP)的支持,不支持互动形式的多媒体页面。网站开发及其技术等原因,已经放弃了对微软IE浏览器和微软WebBrowser 控件的支持。最主要的是部分网站不支持在微软WebBrowser 控件中显示验证码图片。

同样,谷歌公司的cef由于版本众多,控件接口参数互相之间不兼容,使用环境、命令名称不同。

Cef暂不支持"Any CPU"。需要明确指定编译方式,例如X86、X64等。

Chromium 105 之后的版本开始,对于 H265 的支持比较完善。

其中H.264是一种专利视频格式,使用H.264需要向MPEG-LA公司交纳专利费并获取许可证,虽然目前暂未收费,但不保证将来永远免费。也正是基于这个原因,Google才重金收购了开发VP8技术的On2公司并在此基础上推出了免费开放的VP8/WebM格式。按照Google官方说法,Chrome/Chromium今后不再支持H.264这样的专利格式,只支持VP8/WebM这样的开放视频格式。因此,如果我们基于开源的Chromium做二次开发,就可能会面临一个需要解决的问题:不支持H.264视频。

总之,由于操作系统、版权、技术迭代等原因,在二次开发时,要谨慎选择使用的内嵌浏览器核心。

如果要支持Windows7操作系统,就不要使用高于109版本的谷歌浏览器核心cefshap组件。

总结

通过上边的代码,就完整的完成了从网页元素中获取验证码的过程。

虽然描述起来篇幅很多,但实际操作起来步骤极为简单。

无非就是三步:获取图片、识别图片字符、自动登录网站