BC開篇-拋磚引玉,爬取圖片式價(jià)格

Basic Coder 寄語(yǔ)

很高興Basic coder終于和大家見面了,相信關(guān)注的同學(xué)一定會(huì)有各種疑問,這是個(gè)什么樣的公眾號(hào),它的主要內(nèi)容是什么,關(guān)注它能獲得什么。正如Basic coder字面上意義一樣,它是一個(gè)關(guān)于基礎(chǔ)編程的公眾號(hào),旨在分享任何具體編程問題的解決方案,在這里你看不到高深莫測(cè),高度抽象的大牛演講稿,也不會(huì)看到漫天飛舞,層出不窮的高大上名詞,但這里有最實(shí)際的,經(jīng)過(guò)檢驗(yàn)的解決方案,也許不是最好的,但一定會(huì)越來(lái)越好,它們最終的目的是讓每個(gè)想要編程,喜歡創(chuàng)造的人更快更好的完成自己的目標(biāo)。個(gè)人一直認(rèn)為國(guó)內(nèi)編程門檻居高不下,本身就是有問題的,應(yīng)該有人去做點(diǎn)什么讓全民編程的時(shí)代盡快到來(lái),屆時(shí)軟件設(shè)計(jì)更多的是高層次的邏輯功能組合,是一門更需要靈感,創(chuàng)意的工程設(shè)計(jì)學(xué)科,而不是一門抓亂頭發(fā),把大部分時(shí)間花在易錯(cuò),繁瑣的下層細(xì)節(jié)實(shí)現(xiàn)上的高門檻專業(yè)。舉個(gè)例子可能更加能說(shuō)明問題,我很想為自己或者以后的孩子設(shè)計(jì)一款智能語(yǔ)音機(jī)器人,運(yùn)用上自己設(shè)計(jì)的很多交互邏輯,從而可以作為小孩子從小的玩具伴侶,或者放在辦公桌上解悶,亦可以放在車上當(dāng)車載小助手。從一個(gè)傳統(tǒng)程序設(shè)計(jì)者的角度出發(fā),想要完成這個(gè)系統(tǒng),需要一個(gè)嵌入式計(jì)算機(jī)以及對(duì)應(yīng)的開發(fā)軟件(市面上也有這樣的套件),然后是學(xué)習(xí)和運(yùn)用開源語(yǔ)音識(shí)別算法等。可以想象整個(gè)過(guò)程加上踩坑,又是一個(gè)漫長(zhǎng)的過(guò)程。在互聯(lián)網(wǎng)信息時(shí)代,分享其實(shí)是件非常容易的事,相信只要有人去做,愿意分享(這也是開源的意義之一),越來(lái)越多的人就會(huì)從中受益,并且創(chuàng)造出更多好玩好用的東西。可能大家可能會(huì)聯(lián)想到開源軟件,Basic Coder和開源軟件都是通過(guò)分享來(lái)實(shí)現(xiàn)自身價(jià)值,而Basic Coder的內(nèi)容范圍更廣,小到幾行代碼,大到一個(gè)軟件,只要能幫助他人節(jié)約研究和學(xué)習(xí)成本的問題解決方案,都是Basic Coder所肯定的。BC永遠(yuǎn)歡迎并且十分渴求您的來(lái)稿。

介紹:電商圖片式價(jià)格

其實(shí)本篇為15年3月寫的文章,CSDN原文鏈接,作為開篇之作,實(shí)在有點(diǎn)寒磣,主要目的在于拋磚引玉。電商,曾一度成為了互聯(lián)網(wǎng)行業(yè)的代名詞,而眾多電商之間的競(jìng)爭(zhēng)也都一直存在,從而引發(fā)了另外一個(gè)需求:能夠快速的獲取競(jìng)爭(zhēng)對(duì)手相同商品(競(jìng)品)的價(jià)格,從而可以實(shí)時(shí)的調(diào)整自己的價(jià)格,在同行業(yè)競(jìng)爭(zhēng)中變得尤為關(guān)鍵。攜程旅行網(wǎng)是國(guó)內(nèi)最大的在線旅游提供商,其部分酒店的價(jià)格為了防止競(jìng)爭(zhēng)對(duì)手爬取,采用了圖片形式。 其他電商平臺(tái)也曾紛紛效仿。然而仔細(xì)分析,圖片式價(jià)格實(shí)質(zhì)也是自欺欺人罷了。我們以攜程網(wǎng)為例,討論如何高效破解其圖片式價(jià)格。

注: 目前攜程多數(shù)頁(yè)面酒店似乎已經(jīng)不再使用圖片式價(jià)格。

相關(guān)工作:攜程圖片價(jià)格識(shí)別分析

先上一張圖,看看這個(gè)價(jià)格是怎么來(lái)的。


圖(1)

可以看到,這個(gè)數(shù)字5,是由p_h57_7這個(gè)CSS樣式定義的。而這個(gè)樣式里定義了一個(gè)背景圖片,注意這個(gè)地方后面跟了一個(gè)數(shù)字! 也就是 -1346。 然后,再看看這個(gè)圖片是怎樣的一張圖。打開鏈接就可以獲得,如圖(2)所示。


圖(2)

真實(shí)的圖片比這個(gè)要長(zhǎng),我截取了一段。這時(shí)候你可能聯(lián)想到了,上面的1346這個(gè)數(shù)字可能就是代表了這張圖片橫向第1346個(gè)像素所代表的數(shù)字。確實(shí)如此。不過(guò)這個(gè)位置的像素都是白色,真正的數(shù)字從往后兩個(gè)像素開始,也就是1348這個(gè)項(xiàng)目開始。這個(gè)像素處的數(shù)字正是5。

解決方案

獲得了價(jià)格所在圖片以及知道了具體價(jià)格數(shù)字所在位置,下面只需要通過(guò)簡(jiǎn)單的圖像處理就可以獲得這些數(shù)字! 圖像處理聽起來(lái)很高大上,其實(shí)咱們這里用到的圖像處理非常普通和簡(jiǎn)單。雖然背景圖像是會(huì)經(jīng)常動(dòng)態(tài)更新的,但這些數(shù)字都是一樣的格式。比如3這個(gè)數(shù)字,同一個(gè)尺寸的背景圖中的3都是一樣的。可能大家有個(gè)擔(dān)心就是萬(wàn)一價(jià)格弄成驗(yàn)證碼那樣,怎么辦?其實(shí)不用擔(dān)心,因?yàn)檫@是價(jià)格,很少有人能接受驗(yàn)證碼那樣扭曲的價(jià)格的,所以價(jià)格有它自身的特殊性。價(jià)格爬取往往是爬蟲的一部分,對(duì)于互聯(lián)網(wǎng)海量的數(shù)據(jù),爬蟲的目標(biāo)也要求快,準(zhǔn),狠!快字當(dāng)先。所以我們要用盡可能快的處理算法來(lái)識(shí)別每一個(gè)數(shù)字。反而這里如果使用傳統(tǒng)復(fù)雜的圖像識(shí)算法來(lái)做這件事,那爬完所有數(shù)據(jù)所用的時(shí)間將非常恐怖。
像前面所說(shuō),這些字符一共就12個(gè),分別是: . , 0 1 2 3 4 5 6 7 8 9。放大數(shù)字3和4,如圖3所示:



圖(3)

看他們的第一豎列,是不同的。所以可以根據(jù)第一豎列來(lái)區(qū)分3和4。其他的數(shù)字也都一樣。
為了加快速度,我們要先對(duì)圖片做2值化處理,即只要這個(gè)像素不是白色,就全部設(shè)置為1,是白色就設(shè)置為0. 在代碼中,我們使用了boolean數(shù)組來(lái)存儲(chǔ)2值化結(jié)果。然后從第一豎列開始,形成一顆決策樹。比如,若某兩個(gè)字符的第一豎列一樣,那么就繼續(xù)判斷第二豎列,以此類推,只要兩個(gè)字符不一樣,肯定會(huì)有不一樣的豎列。這樣可以盡早發(fā)現(xiàn)數(shù)字。
以下是識(shí)別算法部分代碼:

/** 獲取圖片像素的二值化二維數(shù)組——縱向優(yōu)先, 為true就是該點(diǎn)為白色,為false該點(diǎn)為黑色 
     * @param img 
     * @return 
     */  
    public static boolean [][] get2ValuePixesHeightFirst(BufferedImage img)  
    {  
        int width= img.getWidth();  
        int height= img.getHeight();  
        boolean [][] result = new boolean[width][height];  
        for(int i=0;i<width;i++)  
            for(int j=0;j<height;j++)  
            {  
                //透明(在RGB中為黑色)和白色 都設(shè)置為false;  
                result[i][j]=img.getRGB(i, j)==16777215 || img.getRGB(i, j)== 0?false:true;  
            }  
        return result;  
    }  
      
    // (-1) 空白   
    //(-2)stopSymbol   16 17 18  
    //(-3)comma                 19 20 21 22  
    // 1          8 9  17 18  
    // 2      6 7 8 9       16 17 18  
    // 3      6 7 8 9    15 16 17 18  
    // 4          12 13 14 15        
    // 5                 15 16 17 18  
    // 6            9 10 11 12 13 14 15 16  
    // 7      6 7      17 18  
    // 8        7 8 9 10 11  13 14 15 16 17  
    // 9          8 9 10 11 12 13  
    // 0          8 9 10 11 12 13 14 15 16  
      
      
    /** 判斷是否為空白豎列 
     * @param verticalLineArray 
     * @return 
     * @throws Exception 
     */  
    public static boolean isBlankLine(boolean [] verticalLineArray) throws Exception  
    {  
        if(verticalLineArray.length!=22)  
        {  
            throw new Exception("This is a new rule image. Can not recognize it!");  
        }  
        for(int i=0;i<verticalLineArray.length;i++)  
        {  
            if(verticalLineArray[i])  
            {  
                return false;  
            }  
        }  
        return true;  
    }  
      
    /** 識(shí)別數(shù)字 
     * @param verticalLineArray 
     * @return 
     * @throws Exception 
     */  
    public static char recognizeNumber( boolean [] verticalLineArray) throws Exception  
    {  
        if(verticalLineArray.length!=22)  
        {  
            throw new Exception("This is a new rule image. Can not recognize it!");  
        }  
        if(verticalLineArray[6-1])  
        {// 2 , 3,  7  
            if(verticalLineArray[8-1])  
            {// 2, 3  
                if(verticalLineArray[15-1])  
                {//3  
                    return '3';  
                }  
                else  
                {  
                    return '2';  
                }  
            }  
            else  
            {// 7  
                return '7';  
            }  
        }  
        else  
        {  
            if(verticalLineArray[7-1])  
            {//8  
                return '8';  
            }  
            else  
            {  
                if(verticalLineArray[8-1])  
                {//1 , 9 , 0  
                    if(verticalLineArray[10-1])  
                    {//9 , 0  
                        if(verticalLineArray[14-1])  
                        {  
                            return '0';  
                        }  
                        else  
                        {  
                            return '9';  
                        }  
                    }  
                    else  
                    {  
                        return '1';  
                    }  
                }  
                else  
                {  
                    if(verticalLineArray[9-1])  
                    {// 6  
                        return '6';  
                    }  
                    else  
                    {// 4 ,5 , '.' , ','  
                        if(verticalLineArray[12-1])  
                        {// 4  
                            return '4';  
                        }  
                        else  
                        {  
                            if(verticalLineArray[15-1])  
                            {  
                                return '5';  
                            }  
                            else  
                            {  
                                if(verticalLineArray[16-1])  
                                {  
                                    return '.';  
                                }  
                                else if(verticalLineArray[19-1])  
                                {  
                                    return ',';  
                                }  
                                else  
                                {  
                                    return '\\\\0';  
                                }  
                            }  
                        }  
                    }  
                }  
            }  
        }  
              
    }  

完整代碼見文末鏈接

結(jié)論

識(shí)別速度和效果,如圖4所示:



圖4

根據(jù)統(tǒng)計(jì),處理整個(gè)一張圖片需要8ms(以后所有相同的圖片不需要重復(fù)處理(可以放在緩存中間件中))。下次讀取,只需要0ms,幾乎不花時(shí)間。

完成代碼Github地址

參考文獻(xiàn):
無(wú)。

稿子說(shuō)明:BC希望您的來(lái)稿內(nèi)容分為5個(gè)部分:

  1. 介紹: 文章所解決問題的描述,相關(guān)概念介紹,解決方案的大致思想。
  2. 相關(guān)工作: 所解決問題的詳細(xì)描述,如果存在已知的解決方案,不妨列舉列舉,以及相關(guān)知識(shí)的描述。
  3. 解決方案: 詳細(xì)解決方案。有代碼的列出部分主要代碼。
  4. 結(jié)果分析: 取得結(jié)果的效果分析和效率分析。有需要可以加入與其他方案的對(duì)比。
  5. 相關(guān)資料,參考文獻(xiàn)等。如果有完整代碼可以給出代碼所在地址。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,882評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,208評(píng)論 3 414
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,746評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,666評(píng)論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,477評(píng)論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,960評(píng)論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,047評(píng)論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,200評(píng)論 0 288
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,726評(píng)論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,617評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,807評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,327評(píng)論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,049評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,425評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,674評(píng)論 1 281
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,432評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,769評(píng)論 2 372

推薦閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫(kù)、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,151評(píng)論 4 61
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,608評(píng)論 25 708
  • 上一次哈哈跟大家整理了兩句快速學(xué)五十音圖的口訣。不知道大家有沒有看哈。其實(shí)五十音的話是一個(gè)非常有規(guī)律的一個(gè)圖表,跟...
    dc3c12c58779閱讀 929評(píng)論 0 0
  • 人員的控制源于對(duì)企業(yè)績(jī)效的需求,如果一個(gè)企業(yè)人員爆滿績(jī)效很低,人員控制上不是好的現(xiàn)象。 就像我們公司根據(jù)工資占比的...
    楊平的閱讀 503評(píng)論 0 0
  • 第一眼看到這幅圖,我感覺作畫者是在描述一幅時(shí)間場(chǎng)景,過(guò)去和現(xiàn)在以及對(duì)逝去時(shí)光的留戀。所以姑且我就給這幅畫起個(gè)名字,...
    Nina張閱讀 939評(píng)論 0 0