如何定制一款12306抢票浏览器――处理预定页面和验证码自动识别功能(三)

2014-11-24 08:18:58 · 作者: · 浏览: 6
CString cstrReadValue(bstrValue);
if ( 0 == cstrReadValue.Compare(cstrValue) ) {
hRes = spOption->put_selected(VARIANT_TRUE);
break;
}
}
} while (0);
return hRes;
}
如此自动填写乘客信息的操作就完成了。
验证码的自动识别
说来惭愧,这个模块本来是我这个软件的一个亮点。可是随着12306将验证码生成方法改变,导致我原来的逻辑产生了很大的误差。其实图像识别这块,我使用的是第三方库tesseract-ocr。之前12306的验证码相对比较简单,但是仍然加入了噪点和干扰线。使得tesseract-ocr识别率非常不准。于是我写了一个bmp文件格式分析和图片转换类去处理原始验证码图片,使得验证码变得清晰,同时提高了tesseract-ocr的识别准确率。我列一些以前的处理结果对比图

\\

\\

\\

网上有使用2012编译tesseract-ocr的介绍。我做了点改动:在tesseract-ocr的init函数中,提供了一个指定相关目录的参数,但是代码底层却优先读取了系统环境变量TESSDATA_PREFIX的值作为相关目录。我修改了源代码中的这部分:即只使用我指明的程序路径,而不是使用系统环境变量TESSDATA_PREFIX的值。
我封装了一个文字识别的类COcr。其内容也很简单
[cpp]
BOOL COcr::Init(const CString& cstrSetupFloder)
{
std::string sSetupFloder = CW2A(cstrSetupFloder.GetString());
int nstatus = m_Tesseract.Init(sSetupFloder.c_str(), "eng", tesseract::OEM_TESSERACT_ONLY);
if ( nstatus < 0 ) {
return FALSE;
}
m_Tesseract.SetPageSegMode(tesseract::PSM_SINGLE_BLOCK);
nstatus = m_Tesseract.SetVariable( "tessedit_char_whitelist", "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwsyz" );
return nstatus > 0 TRUE : FALSE;
}
BOOL COcr::GetText( const CString& cstrImgPath, CString & cstrText )
{
std::string sImgPath = CW2A(cstrImgPath.GetString());
STRING text_out;
if (!m_Tesseract.ProcessPages(sImgPath.c_str(), NULL, 0, &text_out)) {
return FALSE;
}
std::string sText = text_out.string();
cstrText = CA2W(sText.c_str());
return TRUE;
}
简单说明下上述代码。代码第4行,我们设置了语言是eng,即英语体系。因为目前12306的验证码还只是数字和字母。代码第9行,告诉tesseract-ocr验证码中只是包含0~9A~Za~z字符。之前12306的验证码只有数字和大写字母,所以那个时候设置这个参数为0~9A~Z是非常必要的。
代码识别模块ok后,就是如何保存验证码图片的问题了。
如何保存验证码图片
仔细看过12306验证码区域的HTML代码的朋友,应该知道,该处的IMG的src不是指向的是一个图片,而是一个随机地址。
[html]
我之前想通过Src 下载图片的方法明显是行不通的。那么就得使用截屏技术了。下面的代码,将验证码区域复制到剪贴板中,然后再将剪贴板中的图片保存为一个32位真彩色的bmp图片。
[cpp]
HRESULT CDeal12306WebPage::SaveImg( CComPtr spElement,
const CString& cstrFilePath )
{
HRESULT hr = E_FAIL;
do {
CComPtr spDispDoc;
hr = spElement->get_document(&spDispDoc);
CHECKHRPOINTER(hr, spDispDoc);
CComPtr spMainDoc;
hr = spDispDoc->QueryInterface(IID_IHTMLDocument2, (LPVOID*)&spMainDoc);
CHECKHRPOINTER(hr, spMainDoc);
CComPtr spBody;
hr = spMainDoc->get_body(&spBody);
CHECKHRPOINTER(hr, spBody);
CComPtr spBody2;
hr = spBody->QueryInterface(IID_IHTMLElement2, (LPVOID*)&spBody2);
CHECKHRPOINTER(hr, spBody2);
CComPtr spDisp;
hr = spBody2->createControlRange(&spDisp);
CHECKHRPOINTER(hr, spDisp);
CComPtr spControlRange;
hr = spDisp->QueryInterface(IID_IHTMLControlRange, (LPVOID*)&spControlRange);
CHECKHRPOINTER(hr, spControlRange);
CComPtr spControlElem;
hr = spElement->QueryInterface(IID_IHTMLControlElement, (LPVOID*)&spControlElem);
CHECKHRPOINTER(hr