stone2083

          爬取交通違章信息的腳本

          會開車了,也慢慢不規矩起來了,于是乎,違章信息也慢慢多起來了,但是無法第一時間通知到自己。
          雖說,有個網站:http://www.hzti.com/service/qry/violation_veh.aspx?pgid=&type=1&node=249
          可以查詢非現場違章情況,
          不過:
          1.我是懶人,主動去查詢的時候不太樂意做
          2.車輛識別碼,永遠記不住
          3.每次輸驗證碼,挺麻煩的

          所以,我希望,只要有違規信息,能主動通知到我。
          程序員嘛,自己動手,豐衣足食。 :)

          實現思路:
          1.爬取http://www.hzti.com/service/qry/violation_veh.aspx?pgid=&type=1&node=249的信息
          2.使用飛信命令發送短信消息(http://www.it-adv.net/)--免費的哦。

          實現難點:
          1.驗證碼破擊
          我對圖片毫無研究,幸好它的驗證碼太簡單了,只有#000000(黑色)才是驗證碼信息,其他的都是無用信息。并且元素的位置都是固定的。
          2.網站用.net實現,在post信息的時候,有一陀無用的數據信息。所幸,post數據的時候,并沒有對數據做加密,不然都挺難判斷具體參數的意義


          驗證碼破解思路:
          1.自我學習:通過程序下載多張驗證碼圖片,去噪點,切割圖片元素,找出最清楚的0-9這10個元素圖片。作為樣本;
          2.破解:下載需要破解的驗證碼圖片,去噪點,切割圖片元素,和樣本像素比較,相似度在95%以上的視為一致。

          得到圖片信息并且去噪點的方法:
           1 // 得到圖片信息,去除噪點的圖片
           2 BufferedImage getBufferedImage(InputStream in) throws Exception {
           3     BufferedImage img = ImageIO.read(in);
           4     for (int i = 0; i < img.getWidth(); i++) {
           5         for (int j = 0; j < img.getHeight(); j++) {
           6             // 黑色的才是驗證碼
           7             if (!CODE_RGB.equals(Integer.toHexString(img.getRGB(i, j)))) {
           8                 img.setRGB(i, j, -1);
           9             }
          10         }
          11     }
          12     in.close();
          13     return img;
          14 }
          得到圖片像素信息(RGB值)
           1 int[][] getImageFeature(BufferedImage img) throws Exception {
           2     int[][] feature = new int[WIDTH][HEIGHT];
           3     for (int i = 0; i < WIDTH; i++) {
           4         for (int j = 0; j < HEIGHT; j++) {
           5             if (img.getWidth() < i || img.getHeight() < j) {
           6                 continue;
           7             }
           8             feature[i][j] = img.getRGB(i, j);
           9         }
          10     }
          11     return feature;
          12 }

          下載多張圖片,切割,保存,人工尋找最清晰的0-9這10個元素圖片
          public void learn(int count) throws Exception {
              
          for (int c = 0; c < count; c++) {
                  BufferedImage img 
          = getBufferedImage(new URL(IMG_URL).openStream());
                  
          // 保存樣本
                  for (int i = 1; i < 5; i++) {
                      BufferedImage bi 
          = img.getSubimage(i * X_BASE, Y_BASE, WIDTH, HEIGHT);
                      String name 
          = String.valueOf(c) + "_" + String.valueOf(i);
                      ImageIO.write(bi, IMAGE_FORMAT, 
          new File(IMG_REF + "learn/" + name + ".bmp"));
                  }
              }
          }
          備注:
          GifImageWriter有bug。對于BufferedImage的subBufferedImage輸出一直有問題。Sun很早就有人提交了bug,貌似還沒有修復。

          破解代碼(說破解,有點汗。。。其實壓根兒和破解沒關系)
           1 public String crack(InputStream in) throws Exception {
           2     BufferedImage img = getBufferedImage(in);
           3     StringBuilder ret = new StringBuilder();
           4 
           5     for (int c = 1; c < 5; c++) {
           6         BufferedImage bi = img.getSubimage(c * X_BASE, Y_BASE, WIDTH, HEIGHT);
           7         int[][] feature = getImageFeature(bi);
           8 
           9         for (ImageCode ic : imageCodes) {
          10             int success = 0;
          11             for (int i = 0; i < WIDTH; i++) {
          12                 for (int j = 0; j < HEIGHT; j++) {
          13                     if (feature[i][j] == ic.getFeature()[i][j]) {
          14                         success++;
          15                     }
          16                 }
          17             }
          18             // 判斷是否匹配上
          19             if (success * 100.0 / (WIDTH * HEIGHT) > 95) {
          20                 ret.append(ic.getCode());
          21 
          22                 continue;
          23             }
          24         }
          25 
          26     }
          27 
          28     return ret.toString();
          29 }
          其中,imageCodes是初始化準備的0-9這10個元素圖片的特征信息和對應的值

          之后的事情就簡單了。通過LiveHttpHeader找到Post提交的數據,通過shell腳本,模擬。搞定。
          主要用到curl命令
           1 curl -"ASP.NET_SessionId=$SESSION_ID" "$IMAGE_CODE_URL" -$TMP_CODE_BASE
           2 code=`java -cp lib/traffic.jar TrafficImgCracker $TMP_CODE_BASE`
           3 
           6 curl -"$AGENT" -$TRAFFIC_URL -"ASP.NET_SessionId=$SESSION_ID;isLoginedWeb=T;ImageV=$code" -"$QUERY_PARAM" "$TRAFFIC_URL" -$TMP_CONTENT_BASE
           7 
           8 now=`cat $TMP_CONTENT_BASE | grep -'<td class="xxcxsspoptds">' | sed 's/<[^>]*>//g' | sed 's/\s\+/ /g'`
           9 his=`cat $HIS_CONTENT_BASE | grep -'<td class="xxcxsspoptds">' | sed 's/<[^>]*>//g' | sed 's/\s\+/ /g'`
          10 
          11 if [ "$now" != "$his" ];then
          12     msg=`cat $TMP_CONTENT_BASE | grep -'<td class="xxcxsspoptds">' | sed 's/<[^>]*>//g' | sed 's/\s\+/ /g' | head -6`
          13     echo $msg
          14     #send
          15 fi
          shell一直不熟,代碼估計挺猥瑣的。。。


          最后通過飛信命令發送消息:
          1 $FETION_HOME/fetion --mobile=12345678901 --pwd=bugaosuni --to=12345678901 --msg-utf8="$msg"

          廣告下:
          飛信命令行,挺好的東東,像系統報警等,都能考慮用它來完成。

          最后,附上圖片破解的代碼:
            1 import java.awt.image.BufferedImage;
            2 import java.io.File;
            3 import java.io.FileInputStream;
            4 import java.io.InputStream;
            5 import java.net.URL;
            6 import java.util.ArrayList;
            7 import java.util.List;
            8 
            9 import javax.imageio.ImageIO;
           10 
           11 /**
           12  * 交通網圖片分析工具
           13  * 
           14  * @author <a href="mailto:li.jinl@alibaba-inc.com">Stone.J</a> 2010-10-10
           15  */
           16 public class TrafficImgCracker {
           17 
           18     private static final String IMG_URL      = "http://www.hzti.com/government/CreateCheckCode.aspx";
           19     private static final String IMG_REF      = "/home/stone/tmp/imgref/";
           20 
           21     private static final int    X_BASE       = 9;
           22     private static final int    Y_BASE       = 0;
           23     private static final int    WIDTH        = 7;
           24     private static final int    HEIGHT       = 22;
           25     private static final String IMAGE_FORMAT = "bmp";
           26     private static final String CODE_RGB     = "ff000000";
           27 
           28     private List<ImageCode>     imageCodes   = new ArrayList<ImageCode>(10);
           29 
           30     public static void main(String[] args) throws Exception {
           31         if (args.length != 1) {
           32             System.out.println("java TrafficImgCracker $file_path");
           33             System.exit(1);
           34         }
           35 
           36         String file = args[0];
           37 
           38         TrafficImgCracker cracker = new TrafficImgCracker();
           39         cracker.init();
           40         System.out.print(cracker.crack(file));
           41     }
           42 
           43     /**
           44      * init
           45      * 
           46      * @throws Exception
           47      */
           48     public void init() throws Exception {
           49         File ref = new File(IMG_REF);
           50         File[] files = ref.listFiles();
           51         for (File f : files) {
           52             if (!f.isFile()) {
           53                 continue;
           54             }
           55             String code = f.getName().substring(0, f.getName().indexOf("."));
           56             BufferedImage img = getBufferedImage(new FileInputStream(f));
           57             int[][] feature = getImageFeature(img);
           58             // add to image codes
           59             imageCodes.add(new ImageCode(code, feature));
           60         }
           61     }
           62 
           63     /**
           64      * crack
           65      * 
           66      * @param file
           67      * @return
           68      * @throws Exception
           69      */
           70     public String crack(String file) throws Exception {
           71         return crack(new FileInputStream(file));
           72     }
           73 
           74     /**
           75      * crack image code
           76      * 
           77      * @param in image input stream
           78      * @return code
           79      */
           80     public String crack(InputStream in) throws Exception {
           81         BufferedImage img = getBufferedImage(in);
           82         StringBuilder ret = new StringBuilder();
           83 
           84         for (int c = 1; c < 5; c++) {
           85             BufferedImage bi = img.getSubimage(c * X_BASE, Y_BASE, WIDTH, HEIGHT);
           86             int[][] feature = getImageFeature(bi);
           87 
           88             for (ImageCode ic : imageCodes) {
           89                 int success = 0;
           90                 for (int i = 0; i < WIDTH; i++) {
           91                     for (int j = 0; j < HEIGHT; j++) {
           92                         if (feature[i][j] == ic.getFeature()[i][j]) {
           93                             success++;
           94                         }
           95                     }
           96                 }
           97                 // 判斷是否匹配上
           98                 if (success * 100.0 / (WIDTH * HEIGHT) > 95) {
           99                     ret.append(ic.getCode());
          100 
          101                     continue;
          102                 }
          103             }
          104 
          105         }
          106 
          107         return ret.toString();
          108     }
          109 
          110     /**
          111      * learn
          112      * 
          113      * @param count learning count
          114      * @throws Exception
          115      */
          116     public void learn(int count) throws Exception {
          117         for (int c = 0; c < count; c++) {
          118             BufferedImage img = getBufferedImage(new URL(IMG_URL).openStream());
          119             // 保存樣本
          120             for (int i = 1; i < 5; i++) {
          121                 BufferedImage bi = img.getSubimage(i * X_BASE, Y_BASE, WIDTH, HEIGHT);
          122                 String name = String.valueOf(c) + "_" + String.valueOf(i);
          123                 ImageIO.write(bi, IMAGE_FORMAT, new File(IMG_REF + "learn/" + name + ".bmp"));
          124             }
          125         }
          126     }
          127 
          128     // 得到圖片信息,去除噪點的圖片
          129     private BufferedImage getBufferedImage(InputStream in) throws Exception {
          130         BufferedImage img = ImageIO.read(in);
          131         for (int i = 0; i < img.getWidth(); i++) {
          132             for (int j = 0; j < img.getHeight(); j++) {
          133                 // 黑色的才是驗證碼
          134                 if (!CODE_RGB.equals(Integer.toHexString(img.getRGB(i, j)))) {
          135                     img.setRGB(i, j, -1);
          136                 }
          137             }
          138         }
          139         in.close();
          140         return img;
          141     }
          142 
          143     // 得到圖片特征
          144     private int[][] getImageFeature(BufferedImage img) throws Exception {
          145         int[][] feature = new int[WIDTH][HEIGHT];
          146         for (int i = 0; i < WIDTH; i++) {
          147             for (int j = 0; j < HEIGHT; j++) {
          148                 if (img.getWidth() < i || img.getHeight() < j) {
          149                     continue;
          150                 }
          151                 feature[i][j] = img.getRGB(i, j);
          152             }
          153         }
          154         return feature;
          155     }
          156 
          157     /**
          158      * image code bean
          159      * 
          160      * @author <a href="mailto:li.jinl@alibaba-inc.com">Stone.J</a> 2010-10-10
          161      */
          162     public static class ImageCode {
          163 
          164         private String  code;
          165         private int[][] feature;
          166 
          167         public ImageCode(String code, int[][] feature){
          168             this.code = code;
          169             this.feature = feature;
          170         }
          171 
          172         public String getCode() {
          173             return code;
          174         }
          175 
          176         public void setCode(String code) {
          177             this.code = code;
          178         }
          179 
          180         public int[][] getFeature() {
          181             return feature;
          182         }
          183 
          184         public void setFeature(int[][] feature) {
          185             this.feature = feature;
          186         }
          187     }
          188 
          189 }

          ====================================================================
          10月29日編輯的內容:
          最近在學python,用python寫了個版本,發覺確實簡潔很多:
           1 import os, sys, Image
           2 
           3 class TrafficImgCracker(object):
           4     """
           5         交通信息網站圖片破解
           6     """
           7    
           8     def __init__(self, base):
           9         """
          10            base:參考圖片基本路徑
          11            codes:圖片code和feature的元組集合
          12         """
          13         self.base = base
          14         self.codes = []
          15         
          16         for i in [images for images in os.listdir(base) if images.endswith('bmp')]:
          17             self.codes.append((i[0], list(Image.open(base + i).getdata())))
          18 
          19 
          20     def crack(self, imgfile):
          21         img = Image.open(imgfile)
          22         ret = []
          23         for i in range(15):
          24             i = img.crop((i * 9, 0, i * 9 + 722))
          25             feature = [f if(f == 40else 15 for f in i.getdata()]
          26             ret.append(self._getcode(feature))
          27         return "".join(ret)
          28 
          29 
          30     def _getcode(self, feature):
          31         for code in self.codes:
          32             ref = zip(code[1], feature)
          33             if(len([x for x in ref if x[0] == x[1]]) * 100.0 / len(ref) > 95):
          34                 return code[0]
          35         return None





          posted on 2010-10-29 18:30 stone2083 閱讀(3138) 評論(8)  編輯  收藏 所屬分類: java

          Feedback

          # re: 爬取交通違章信息的腳本 2010-10-13 12:55 nkjava

          強人  回復  更多評論   

          # re: 爬取交通違章信息的腳本 2010-10-14 10:52 珂兒

          很強大  回復  更多評論   

          # re: 爬取交通違章信息的腳本[未登錄] 2010-10-14 20:49 java

          程序員嘛,自己動手,豐衣足食。確實很強大···  回復  更多評論   

          # re: 爬取交通違章信息的腳本[未登錄] 2010-10-14 23:19 阿風

          立哥有IT懶人氣質。哈哈  回復  更多評論   

          # re: 爬取交通違章信息的腳本 2010-10-15 18:18 stone2083

          @阿風
          其實是周末下午沒事情干,弄著玩的。
          之前沒有涉及過圖片相關的,趁機也稍微學習下。 :)  回復  更多評論   

          # re: 爬取交通違章信息的腳本 2010-10-29 14:31 wuzhengju

          抱著感興的態度,去看一下這網站

          發現一個更簡單的獲取他們的驗證碼

          Cache-Control private
          Content-Length 1753
          Content-Type text/html; charset=utf-8
          Server Microsoft-IIS/7.0
          X-AspNet-Version 2.0.50727
          Set-Cookie ImageV=5062; path=/
          X-Powered-By ASP.NET
          Date Fri, 29 Oct 2010 06:25:29 GMT



          ImageV就是他們驗證的值,寫在cookie,直接獲取cookie就行了  回復  更多評論   

          # re: 爬取交通違章信息的腳本 2010-10-29 18:46 stone2083

          @wuzhengju
          唉,我這個土人。。。
          之前思維一直停留在如何破解驗證碼上了。直到寫完這個東東,在寫腳本的時候:
          curl -A "$AGENT" -e $TRAFFIC_URL -b "ASP.NET_SessionId=$SESSION_ID;isLoginedWeb=T;ImageV=$code" -d "$QUERY_PARAM" "$TRAFFIC_URL" -o $TMP_CONTENT_BASE
          才發現在cookie中已經存在這個ImageV了。
          不過為時已晚,米已城粥。。。 :)
          ================================================
          好久不見了,最近工作還順利嗎?

            回復  更多評論   

          # re: 爬取交通違章信息的腳本 2010-10-29 21:32 wuzhengju

          @stone2083
          最近工作還好,要學的東西很多!
          得多向你學習!  回復  更多評論   

          主站蜘蛛池模板: 陇西县| 黄山市| 克拉玛依市| 嘉黎县| 遵义县| 繁昌县| 丹巴县| 如皋市| 河池市| 固镇县| 绥滨县| 德清县| 北安市| 明水县| 望都县| 西贡区| 洛川县| 梁河县| 新丰县| 铜鼓县| 汤原县| 咸阳市| 株洲市| 罗田县| 临漳县| 长沙市| 大埔区| 汉寿县| 库尔勒市| 库车县| 龙岩市| 志丹县| 福州市| 时尚| 玉环县| 虞城县| 红安县| 沂南县| 梁山县| 台前县| 莱阳市|