用c++实现获取ie网页的验证码图片识别后输入到验证码框

1.主要方法

利用IHTMLDocument2得到ie的网页,结合上一篇的js扩展程序中的方法,通过execScript()执行脚本将验证码图片base64编码为字符串。然后c++程序获取图片的base64字符串解码后通过tesseract进行识别,将识别后的结果通过execScript()写到验证码框中。

2.遇到的一些问题

1.获取ie的网页。用FindWindow()FindWindowEx()结合spy++找到网页的窗口句柄,经过试验发现浏览器总是将当前的网页置于子窗口的最前面。
2.由于execScript()并不返回js的执行结果,所以我将验证码图片的base64编码以

结点的形式保存在中,之后用获取结点的html内容的方法获取图片。
3.获取验证码图片要使用canvas,但是drawImage()这个图片的加载需要时间,因此要将drawImage()以及编码和插入结点操作放到图片的onload()回调函数中。但是这其中有个问题一直没弄明白:我将上述2、3操作放到一个function中,然后调用function,但是由于drawImage()的原因(我认为是的)插入的结点值总是空的,不用function,去掉“{”“}”只执行内部的代码,插入的节点值就对了。
4.base64解码后利用opencv的imdecode(),特别注意canvas的toDataURL()得到的字符串有一部分不是编码。
5.在之前的execScript()中我写入了一个input(code)的function,作用是将code写入到验证码框。在得到识别结果后,将结果与input()拼接起来,再执行一次js程序。但是识别结果的字符串后面总是有两个‘\n’;因此要除去。

3.展望

  • 首先是将这部分与之前的用户名和密码程序结合起来。
  • tesseract-OCR这部分因为没有自己做样本库,所以精度很低。弄懂tesseract是如何工作的,如何自己训练样本库

4.程序的主要代码

IHTMLDocument2* GetDocInterface(HWND hWnd)
{
    //必须动态载入OLEACC.dll动态链接库
    HINSTANCE hInst = ::LoadLibrary(_T("OLEACC.DLL"));
    IHTMLDocument2* pDoc2 = NULL;

    if (hInst != NULL){
        if (hWnd != NULL){
            //COM接口指针很危险,因为使用过程中需要每一个使用者都要严格并且正确的AddRef和Release,
            CComPtr<IHTMLDocument> spDoc = NULL;
            LRESULT lRes;
            //因为WM_HTML_GETOBJECCT非Windows标准消息,得手动注册  
            UINT nMsg = ::RegisterWindowMessage(_T("WM_HTML_GETOBJECT"));//返回一个引用,一个类似句柄,到指定的对象。
            ::SendMessageTimeout(hWnd, nMsg, 0L, 0L, SMTO_ABORTIFHUNG, 1000, (DWORD*)&lRes);//该函数将指定的消息发送到一个或多个窗口
            LPFNOBJECTFROMLRESULT pfObjectFromLresult = (LPFNOBJECTFROMLRESULT)::GetProcAddress(hInst, "ObjectFromLresult");
            //ObjectFromLresult函数检索是一个基于先前生成的对象的引用访问的对象请求的接口指针。
            //QueryInterface,客户可以通过此函数来查询某个组件是否支持某个特定的接口。若支持QueryInterface将返回一个指向些接口的指针,不支持返回值将是一个错误代码。
            if (pfObjectFromLresult != NULL){
                HRESULT hr;
                hr = pfObjectFromLresult(lRes, IID_IHTMLDocument, 0, (void**)&spDoc);
                if (SUCCEEDED(hr)){
                    CComPtr<IDispatch> spDisp;//自动化接口IDispatch允许客户端程序探明一个运行中的对象到底支持什么属性和方法。[
                    CComQIPtr<IHTMLWindow2> spWin;
                    spDoc->get_Script(&spDisp);
                    spWin = spDisp;
                    CString strjs ("\
                 function getCode(){\
                    var tags = document.getElementsByTagName('*');\
                    for (var i = 0; i);             
                    CComBSTR bstrjs = strjs.AllocSysString();
                    CComBSTR bstrlan = SysAllocString(L"javascript");
                    VARIANT varRet;
                    spWin->execScript(bstrjs, bstrlan, &varRet);
                    spWin->get_document(&pDoc2);

                    fstream outfile;
                    outfile.open("D:\\myfile.txt");
                    CComPtr<IHTMLElementCollection> pCollec;
                    pDoc2->get_all(&pCollec);

                    if (NULL == pCollec)
                    {
                        return 0;
                    }
                    VARIANT varName;
                    long len = 0;
                    pCollec->get_length(&len);
                    for (int i = 0; i < len; i++)
                    {
                        varName.vt = VT_I4;
                        varName.llVal = i;
                        CComPtr<IHTMLElement> pElement;
                        CComPtr<IDispatch> pDisp;
                        pCollec->item(varName, varName, &pDisp);
                        if (NULL == pDisp)
                        {
                            continue;
                        }
                        pDisp->QueryInterface(IID_IHTMLElement, (LPVOID*)&pElement);//查询某个组件是否支持某个特定的接口
                        if (NULL != pElement)
                        {                   
                            BSTR bstimage;
                            if (i > 1){
                                pElement->get_id(&bstimage);
                                if (bstimage != NULL)
                                {
                                    string strimage = _com_util::ConvertBSTRToString(bstimage);
                                    if (strimage == "alxm")
                                    {
                                        BSTR image;
                                        pElement->get_innerHTML(&image);
                                        if (image != NULL)
                                        {
                                            string strig = _com_util::ConvertBSTRToString(image);   
                                            strig.replace(0, 22, "");

                                            ZBase64 base64;
                                            int a;
                                            string imgdecode = base64.Decode(strig.c_str(), strig.length(), a);
                                            std::vector<uchar> data(imgdecode.begin(), imgdecode.end());
                                            Mat img_decode;
                                            img_decode = imdecode(data, CV_LOAD_IMAGE_COLOR);
                                            IplImage qImg;
                                            qImg = IplImage(img_decode); // cv::Mat -> IplImage
                                            cvSaveImage("./out.png", &qImg);
                                            char * str = "out.png";
                                            tesseract::TessBaseAPI  api;
                                            api.Init(NULL, "eng", tesseract::OEM_DEFAULT);
                                            //设置字符识别的白名单,只有数字和字母
                                            api.SetVariable("tessedit_char_whitelist", "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
                                            STRING text_out;
                                            if (!api.ProcessPages(str, NULL, 0, &text_out))
                                            {
                                                return 0;
                                            }
                                            string ocrresult = text_out.string();
                                            outfile << ocrresult << endl;
                                            string str1 = "inputcode(\"" + ocrresult + string("\");");
                                            int n = str1.find("\n");
                                            str1.replace(n, 2, "");
                                            /*
                                            str1 = str1 + "var oldp=document.getElementById(\"alxm\");\
                                            if (oldp != null){ oldp.innerHTML=\"\";}";
                                            */
                                            strjs = str1.c_str();
                                            CComBSTR bstrjs = strjs.AllocSysString();
                                            CComBSTR bstrlan = SysAllocString(L"javascript");
                                            VARIANT varRet;
                                            spWin->execScript(bstrjs, bstrlan, &varRet);
                                            break;
                                        }
                                    }
                                }                           

                            }

                        }

                    }

                }
            }
            ::FreeLibrary(hInst);
            return pDoc2;
        }
    }
}


void CgeturlDlg::OnBnClickedOk()
{
    // TODO:  在此添加控件通知处理程序代码
    HWND IE = ::FindWindow(_T("IEFrame"), 0);//IEFrame  
    HWND hWnd1 = ::FindWindowEx(IE, 0, _T("Frame Tab"), NULL); //(ExplorerWnd, _T("Internet Explorer_Server"));
    HWND hWnd2 = ::FindWindowEx(hWnd1, 0, _T("TabWindowClass"), NULL);
    HWND hWnd3 = ::FindWindowEx(hWnd2, 0, _T("Shell DocObject View"), NULL);
    HWND hWnd = ::FindWindowEx(hWnd3, 0, _T("Internet Explorer_Server"), NULL);
    if (hWnd == NULL)
    {
        AfxMessageBox(CString("not find Frame"));
        return;
    }
    DWORD threadId = GetWindowThreadProcessId(hWnd, 0);
    GetDocInterface(hWnd);
}

相关链接:
base64编解码
tesseract在vs中的使用
IHTMLDocument2的使用

你可能感兴趣的:(浏览器)