【技术原创】探讨一下京东商城价格图片解析算法的优化,附演示程序下载

      发现咱博客开启后技术性的文章写的并不多,这貌似违背当时的初衷啊。文章不多并不表示咱一直都闲着,尤其是在技术方面咱是因为太忙了才没时间写一写,总结总结的。今晚趁还有些精力赶紧将前段日子自己捣鼓的小东西(之一)给大家分享一下。

      前不久我不是扬言说要做两款小软件么,这次我们说到的话题就跟这个有关。我说的那个软件之一就是京东商城的相关软件–我想抓取价格做分析(京东不会提供这个接口),我相信这个肯定是对大家有所帮助的。京东商城的价格展示方式,除了书籍外,基本上都是通过图片来展示的–这个好处是防止有人过于轻易地抓取到价格信息。于是咱想准确地获取到价格的话就只能老实地分析图片喽(其实有的页面里,比如列表页面商品价格是有字符串表示形式的,但那个只用于无目标采集时有用,要是关注某个商品价格变动情况的话,作用就不大了,所以咱无法避免图像分析这一步)。

      图片分析一般有两种思路,一种是与样本库对比,这种方法简单有效,缺点是适应性较差,样本可能要跟着图片不停地变动;另一种就是形状分析,也就是判断图片中数字的形状来判断是哪个字,这种方法效率比前者低,但适应性强;以前我在看验证码识别相关文章时还找到过这种算法,但眼下要用的时候就再也找不着了,方便起见咱也只能用第一种,也就是样本库的方法识别了。

      这里我要先插播一下,之所以我能采取样本库的算法,是因为京东商城的价格显示很有特点(希望京东商城的人看到后表改掉哦,否则我又得重新找算法了),就是几个数字拼在一起然后再随机绘制了一条细线挡了一下,其中0~9这9个数字的字体样式和大小是始终不变的,也就是我们能将价格图片分割成单独的数字,然后去掉无效的坐标(也就是被细线挡住的部分),就创造了样本库对比的先前基础了。

      样本库对比如何做呢,最简单来讲,我们拿到一串数字,首先要把每个数字独立分割开来,图片中有几个数字就分割成几张图片,然后再针对每个图片进行分析处理。这样我们就需要针对每个数字建立一个样本,然后分析时将每个分割出来的图片与样本采集的点进行对比,命中最高的数字就是我们要的答案喽,但按实际运行效率来看,这种做法并不高效。假设我们每个数字图片采集8个点做样本,这样的话每个图片要比对10次(0~9每个数字一次),然后找出配对率最高的作为最终结果。事实上我这样做的结果也有很高的出错率(大概在5%的样子),为什么呢,因为8和0很像,3和8很像,6和8也很像,这样如果样本正好挡住关键位置或者其它位置使两者匹配点相等或者非正确值大于正确值的数量,这就使结果出错。说简单一点就是本来是要匹配8的,但如果正好8中间的点被划线了,很可能就被判断为3。想要提高准确率只能加大样本库,最后我将样本库加大到极限,也就是整个数字的所有像素都做样本比对,发现还是不能保证百分百识别正确。这种做法显然行不通。

      上面的样本库对比有些死板,因为他非要一个一个去对比,等对比完了找匹配数量最高的为最终结果,效率不高,可信度也不高。后来我就想有没有更好的样本库算法呢,我就从尽量减少比对次数上考虑,发现我们可以这样去判断:我们先去找带有个性的数字的特征,然后以此为基础否决或者确定一个个的数字,最终就能得到我们想要的结果。具体怎么说呢,就是我先找出0~9这9个数字,也就是9张图片中每个数字独具的特色(比如说1号数字,只有它这里在坐标(7,1)处是白色,只有它在坐标(3,4)处是红色),写这样一个算法计算每个数字的特色并不复杂,于是我很轻松地就把样本库统计出来了:

1:W(7,1), R(5,2), R(6,3), R(5,4), R(6,4), R(6,9)
2:R(3,9), R(1,11), R(9,11)
4:W(5,1), W(6,1), R(4,4), R(3,8), R(7,8), R(10,8), W(3,11), W(4,11)
5:R(3,4)
7:R(1,1), R(9,2), R(4,10)
9:R(4,7)

   解释一下上面表示的意思:第一个数字表示数字几的对比结果,W表示白色,R表示红色,第一行表示:只有数字1,才会在坐标(7,1)处出现白色,才会在坐标(5,2)处出现红色,才会在……这样我们就可以根据这些特征值直接判断是否是当前这个数字。考虑到有时特征值正好被细线经过成为无效数据(即不是W也不是R),每个数字要有至少三个不在同一条线上的点才能保证能被准确判断(是或者不是)。比如1这个数字,在坐标(7,1)处正好是无效点,(6,3)也是无效,那(5,4)跟前两者不在一条线上,就不大可能也无效了(除非京东价格图片改划曲线了),也就是说有这三个点作为依据,肯定能判断此数字是不是1了。根据这个理论,数字1、2、4、7能保证被正确识别,5,9则需要进一步验证。

      上面已经解决了至少有三个独特的点的数字识别问题,接下来我们就考虑5、9以及剩下的数字如何识别的问题。其实按我们的思路,前面已经可以准确识别数字1、2、4、7了,那么也就是说我们若到此时还没判断出数字是多少,但我们已经坚决否定了前面四个数字,那样的话,两个数字共享的特征,就可能有准确的判断结果了—因为其中一个已经有结果了嘛。于是我们再将与其它数字共享一个特征的数字找出来:

0:R(9,5)-9, R(1,6)-6, W(7,6)-1, R(9,6)-9
1:W(4,1)-4, R(4,2)-6, R(6,2)-4, R(5,3)-4, W(7,6)-0, R(5,9)-7, R(5,10)-7, R(6,10)-9
2:R(1,2)-3, R(4,8)-4, R(4,9)-7
3:R(1,2)-2, R(4,6)-8, R(1,10)-5
4:W(4,1)-1, R(6,2)-1, R(5,3)-1, R(7,3)-7, R(7,4)-7, R(4,8)-2, R(7,9)-9, W(5,11)-7, W(6,11)-7
5:R(9,1)-7, R(3,3)-6, R(4,5)-6, R(1,10)-3
6:R(4,2)-1, R(3,3)-5, R(4,5)-5, R(1,6)-0
7:R(9,1)-5, R(7,3)-4, R(7,4)-4, R(4,9)-2, R(5,9)-1, R(5,10)-1, W(5,11)-4, W(6,11)-4, W(7,11)-9
8:R(4,6)-3, R(3,7)-9
9:R(9,5)-0, R(9,6)-0, R(3,7)-8, R(7,9)-4, R(6,10)-1, W(7,11)-7

      上面第一行的意思是:数字0的共享特征是,它与数字9在坐标(9,5)上都是红色的,与数字6在坐标(1,6)上都是红色的,与数字1…… 依次往下,就形成了上面这张表。我们能得到什么呢,首先我们能判断1、2、4、7,如果成功了就是四者之一了,否则我们看共享表中数字1的特征,为什么这么看呢,因为前面的数字都被否决了,那么这些数字对应的共享特征表里的坐标,就变成了“独享”的。如数字1与数字6共享R(4,2),当否决了数字1时,那么R(4,2)这个条件就可以来直接判断是否是数字6。归纳一下,也就是说前面已经否决的数字,其共享的特征则成为另一方独享的特征(说得比较绕口),归纳算法如下:

1:W(7,1), R(5,2), R(6,3), R(5,4), R(6,4), R(6,9)
2:R(3,9), R(1,11), R(9,11)
7:R(1,1), R(9,2), R(4,10)
4:W(5,1), W(6,1), R(4,4), R(3,8), R(7,8), R(10,8), W(3,11), W(4,11)
9:R(4,7), R(7,9)|4, R(6,10)|1, W(7,11)|7
0:R(9,5)|9, W(7,6)|1, R(9,6)|9
5:R(3,4), R(9,1)|7, R(3,3)|6, R(1,10)|3, R(4,5)|6
6:R(4,2)|1, R(3,3)|5, R(4,5)|5, R(1,6)|0
3:R(1,2)|2, R(4,6)|8, R(1,10)|5
8:R(4,6)|3, R(3,7)|9

    上面便是最后算法依据的推导公式了,我来给大家解释一下:

    算法每行可断定某一个数字(成立或者不成立),第1行表示数字1的判断成立条件(也就是数字1的独享特征),本来三个就足够,我这里顺便将它的6个特征全部列上了;算法依次判断下去,直到判断条件成立,便找到当前数字;

     前四行不用多说,1、2、4、7列举的都是独享的特征,能一次性判断是否成立;第5行,也就是数字9的判断特征里写,第一个是R(4,7)独享特征,第二个是R(7,9)|4,表示与数字4共享的特征(如果判断到这一步了已经表示前面第四行的4已经被否决掉了,所以这个点成为它的独享特征了),再下一个是R(6,10)|1,到这里已经够三个不在一条直线上的点了,数字9也就可识别了。然后数字9的共享特征点又可以为剩下的点做判断依据,于是就有了上面的这个识别算法公式。这个公式要确保的条件是:

     每一行至少有三个不在同一直线上的特征点 ;

     本行若存在共享特征,则需共享方数字已经在本行之前被推导(如数字4的共享特征方1、4、7均已在之前被推导);

     说了这么多,我的算法终于介绍完了,不知道大家有何看法或者想讨论的,可以直接留言。

附件:点击这里下载演示程序

附源代码下载(2011-10-10更新):请参考文章《【技术原创】京东商城价格图片分析解析源代码下载(C#),附演示程序

14条评论

  1. 唐朝宰相说道:

    高手啊,高手,php学习改造中

    • Crazy说道:

      有想法可以交流交流哈

      • 徐海波说道:

        您好,有时间,能不能写个图片验证码识别的示例,我写了一个,没有杂色背景的验证码完全可以识别,但是有杂色背景的验证码图片就不能识别了,你看你能不能针对杂色背景图片的验证码做个示例,让我学习下,谢谢!!!如何写好了,希望能给我说一声,我跟着 学习下

        • Crazy说道:

          那你其实是需要一个算法将杂色背景去掉,或者只将要验证的字符部分取出来,可以根据颜色边界值等方法去除杂色,具体情况具体分析。

  2. 舒涛说道:

    你好~我想跟你学习下这个图片识别.
    可能我基础有点差啊. 已经很用心的看过你的源码了.
    但是在分析数据特征这一块 没有看懂.
    希望高手你指点指点.

    • Crazy说道:

      我也是业余时间搞的,不算是专门搞验证码或者图片识别的,只是最近出于兴趣在这方面有了一些想法,目前对于图片识别这块也只是有一些想法和方案,具体实施肯定没时间做了,项目比较忙,管理一个部门压力还是不小的。

      • 迷惘中....说道:

        我就是没看明白 你这个样本库 自己是怎么去做样本的.

        • Crazy说道:

          我自己是预先将整个图片切割成单个数字小图片,然后再分析出来的算法。之后就直接切割读取像素验证值了

  3. 孙俪说道:

    这个坐标怎么找啊

    • Crazy说道:

      把图片切割成单个的数字图片后,这样每个图片大小就都一样了,每张逐个坐标对比。如果你问的是怎样切割数字,那个坐标是固定的,也就是说每个数字的大小是一定的。

  4. 赵宇飞说道:

    你好,我感觉你的思路很复杂,我最近也在搞这块,我有个思路开始觉得挺容易实现的,大致思路是这样的:我会自己生成九个数字的图片,然后我会提取这九个样本数字的所有坐标,然后我再将京东上面的价格图片解析,现在已经做到能将里面的数字分离,具体就是得到每个数字的所有坐标,然后关键的就是通过数字的坐标与样本的坐标进行相似性衡量,但是这个相似性的比较难住了我,不知道你有没有好的办法?(就是假设相似的图像他们在坐标系中的坐标肯定有一定的相似性)

    • Crazy说道:

      我最开始就是这样弄的,样本儿对比。其实如果是这样做的话,还是在网上搜索成熟的算法好了,我这样写是提供另外一种想法,呵呵。

  5. LEO说道:

    这个技术貌似现在不行了吧,京东现在也不是图品了,应该怎么办呢?

    • Crazy说道:

      我也特意看了一下,京东现在不用图片了,而是直接使用数字,意味着你直接用正则表达式匹配一下就能出价格了,大大提升了抓取的性能啊

发表评论

电子邮件地址不会被公开。 必填项已用*标注