58同城上的手机号码大多数是图片格式,目的也是防止爬虫软件抓取,但作为一个专门开发爬虫的程序猿,必须拿下它才能安心,否则睡觉做梦也会想着该怎么破这该死的图片号码的!
这里我们利用Google的开源项目:Tesseract-ocr(项目地址:https://github.com/tesseract-ocr)
其实,Tesseract的网上的教程其实有很多,关于它的介绍,我在这里就不说了,直接讲重点!
先是要初始化Tesseract,这里我们用默认的识别库,根据58同城号码图片的特点,我们这样初始化下:
//程序里需要引用:Tesseract.dll以及程序根目录下要有tessdata\\eng.traineddata的识别库文件
Tesseract.TesseractEngine te = new TesseractEngine(Application.StartupPath + "\\tessdata", "eng", EngineMode.Default);//初始化,这里利用默认的识别库
te.SetVariable("tessedit_char_whitelist", "0123456789");//设置识别的字符白名单
te.DefaultPageSegMode = PageSegMode.SingleLine;//设置识别模式为单行模式
注意的是.NET版本貌似必须3.5及以上,否则Tesseract初始化总是不通过。此问题之前困扰了我好久。
首先我们拿到58同城上的图片号码地址如下:
http://image.58.com/showphone.aspx?t=v55&v=6E0C227B5A963FC4VD7B70A4FC12D1D01
下载获取得到下面的图片:
先对图片进行二值化算法(就是变成只有黑白的算法,搜索引擎一搜一堆)得到下面的图片:
这种单色的图片对于OCR引擎来说就友好多了,识别算法:
我们设定一个Bitmap类型变量bTelImg存储这个二值化后的号码图片,String类型sTelNumber用来存数识别结果,利用下面的算法得到识别结果:
Page PG = te.Process(PixConverter.ToPix(bTelImg), PageSegMode.SingleLine);
sTelNumber= KeyReplace(PG.GetText());
识别下:
额... 一万只草泥马奔腾而过,错这么多咋办?难道要用Tesseract的高级训练算法训练个自己的库出来?都说简单识别了,别搞那么复杂好不好,我好懒的!
其实58上这个图片是动态生成的,所以每次访问得到的图片都不一样,包括号码间隔等。第一次下载的图片因为字符图片粘连的问题,导致识别结果不正确,我们对同样的地址再下载一次图片:
二值化:
识别:
哈哈,终于对了!
证明这个免费的OCR引擎直接下载下来不用复杂的训练还是有效的,下面我们在不改变识别算法的前提下,提高识别率(毕竟对于这种纯数字的图片,想OCR的识别率高,只能训练或者写专门的OCR引擎了)
因为第一次识别结果错了,第二次再下载图片,结果却对了。所以我们可以从识别结果下手,不对我们就重新下载图片,再次识别,直到正确或者设定个阀值,达到阀值,不正确我也没办法了!免费的只能这样了!
因为这里我们识别的是手机号码,所以知道手机号码的规律,我们再对结果进行判断,就可以初步判定结果的争取性了!
- 手机号码必须为11位纯数字(因为我们设置的白名单是纯数字,所以保证结果是11位就可以了)
- 手机号码必须为13,15,18开头的(这个能排除一大部分错误了)
嗯,差不多这两条结果规则可以有效提高识别率了。算法我这里就不写了,是个程序猿都会吧?
至此,一个简单的58 的手机号码图片就完成了。其他诸如电话号码,简单字符验证码,原理也差不多。希望能给初学者一点帮助,后面有机会再和大家讲讲更高级的OCR识别方法。