網(wǎng)站信息抓取實踐

導(dǎo)語:

成績抓取主要涉及到三塊技術(shù):web、數(shù)字圖像處理與機器學(xué)習(xí)

目標(biāo)


從目標(biāo)官網(wǎng)上,根據(jù)數(shù)據(jù)庫中已有的身份證和姓名,抓取其所有成績,并存入數(shù)據(jù)庫中。

步驟


  • 1 獲取驗證碼
  • 2 識別驗證碼
  • 3 查詢成績
  • 4 成績?nèi)霂?/li>

獲取驗證碼

原始驗證碼如下,是一個數(shù)學(xué)表達式。圖中的數(shù)字是經(jīng)過旋轉(zhuǎn)和扭曲處理的,還隨機分布著點和線。破解難度中等。


識別驗證碼

  • 1 圖像二值化
    干擾點和干擾線的灰度值,比字符的灰度值小,因此可以利用這個特征,通過圖像二值化,去除干擾點和干擾線。使用opencv庫,首先計算圖像的灰度直方圖,并歸一化,根據(jù)灰度直方圖的分布找到二值化的閾值后,再二值化。
  void procBinary(Mat image,Mat& threshold_image,int index)
  {
      float range[] = {float(min_grey),float(max_grey)} ;
      const float* hist_range = {range};
      bool uniform = true;
      bool accumulate = false;
      Mat hist;
      //計算灰度直方圖
      calcHist(&image,1,0,Mat(),hist,1,&max_grey,&hist_range,uniform,accumulate);
      //歸一化
      normalize(hist,hist,0,image.rows,NORM_MINMAX,-1,Mat());
      int threhold = 0;
      for(int j = 0; j < hist.size().height; j++){
          if(abs(hist.at<float>(0,j)-image.rows)<precision){
              threhold = j;
          }
      }
      //根據(jù)閾值二值化
      threshold(image,threshold_image,threhold+1,max_grey,THRESH_BINARY);
  }

預(yù)處理后的效果如下:


  • 2 字符分割

破解驗證碼的重點和難點就在于能否成功分割字符,這一點也是機器視覺里的一道難題,對物件的識別能力。對于顏色相同又完全粘連的字符,比如google的驗證碼,目前是沒法做到5%以上的識別率的。不過google的驗證碼基本上人類也只有30%的識別率

官網(wǎng)上的驗證碼,字符有一定幾率會粘連,因此會一定程度上影響驗證碼識別的準確率。
使用掃描線法,從最左側(cè)開始從左到右掃描,如果沒有遇到任何文字的像素,就則往右一個像素然后再掃描,如果遇到有文字像素存在,就記錄當(dāng)前橫坐標(biāo),繼續(xù)向右掃,突然沒有文字像素的時候,就說明到了兩個字符直接的空白部分。再根據(jù)記錄的位置分割字符。

  • 3 標(biāo)準化
    主要是對字符進行縮放,使之成為32x32的圖片。
    這里并沒有使用算法對旋轉(zhuǎn)扭曲的字符進行處理,原因有兩點1、訓(xùn)練數(shù)據(jù)中的字符也是旋轉(zhuǎn)的,不處理對識別率的影響不大,2、如果每次都對旋轉(zhuǎn)、扭曲的算法進行處理,計算量也大。


  • 4 使用卷積神經(jīng)網(wǎng)絡(luò)(CNN)
生成訓(xùn)練數(shù)據(jù)

如果手動對幾千張圖片分類,需要大量的人工操作了,至少得好幾個小時。觀察接口發(fā)現(xiàn),官網(wǎng)上是使用securimage開源庫生成的驗證碼,使用的是SI_CAPTCHA_MATHEMATIC模式。


既然能夠拿到驗證碼生成程序,那么訓(xùn)練數(shù)據(jù)就很容易得到。在生成驗證碼時,不生成隨機干擾點與線,將驗證碼對應(yīng)的值作為圖像名的一部分保存。

//驗證碼數(shù)據(jù)生成
do {
       $signs = array('+', '-', 'x');
       $left  = mt_rand(1, 10);
       $right = mt_rand(1, 10);
       $sign  = $signs[mt_rand(0, 2)];
       $equal = '=';
       $out   = '?';

       switch($sign) {
             case 'x': $c = $left * $right; break;
             case '-': $c = $left - $right; break;
             default:  $c = $left + $right; break;
        }
} while ($c <= 0); // no negative #'s or 0
$this->code         = "$c";
$this->code_display = "$left $sign $right $equal $out"
//將驗證碼對應(yīng)的值作為圖像名的一部分保存
$filename = "data/".uniqid()."_".$this->code_display.".png";
imagepng($this->im,$filename);
imagedestroy($this->im);

將securimage生成驗證碼,并自動歸類到相應(yīng)的文件夾中


訓(xùn)練

卷積神經(jīng)網(wǎng)絡(luò)(Convolutional Neural Network,CNN)是一種前饋神經(jīng)網(wǎng)絡(luò),它的人工神經(jīng)元可以響應(yīng)一部分覆蓋范圍內(nèi)的周圍單元,對于大型圖像處理有出色表現(xiàn)。
CNN可以用來識別位移、縮放及其他形式扭曲不變性的二維圖形。由于CNN的特征檢測層通過訓(xùn)練數(shù)據(jù)進行學(xué)習(xí),所以在使用CNN時,避免了顯示的特征抽取,而隱式地從訓(xùn)練數(shù)據(jù)中進行學(xué)習(xí)。

代碼實現(xiàn)參照nladuo,使用LeNet5卷積神經(jīng)網(wǎng)絡(luò),以32x32的圖片作為輸入,對于字符的變形、旋轉(zhuǎn)、干擾線等扭曲都可以很好的識別,可以實現(xiàn)以下效果。


卷積神經(jīng)網(wǎng)絡(luò)會自己不斷的對訓(xùn)練集進行學(xué)習(xí)迭代,每次迭代都會對識別率有所改進。
具體步驟如下:

  • 1 將測試數(shù)據(jù)放到training_set目錄下
  • 2 設(shè)置字符集
std::string label_strs[13] = {
    "-", "x", "+", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"
};
  • 3 將4/5的數(shù)據(jù)作為訓(xùn)練集,將1/5的數(shù)據(jù)作為測試集
  • 4 開始訓(xùn)練
  //進度軸
  progress_display disp(train_images.size());
  timer t;
  //最小批處理尺寸
  int minibatch_size = 100;
  //迭代次數(shù)
  int num_epochs = 50;
  //一輪訓(xùn)練后的回調(diào)函數(shù)
  auto on_enumerate_epoch = [&](){
  std::cout << t.elapsed() << "s elapsed." << std::endl;
  tiny_cnn::result res = nn.test(test_images, test_labels);
  std::cout << res.num_success << "/" << res.num_total << std::endl;
  disp.restart(train_images.size());
  t.restart();
  };
  auto on_enumerate_minibatch = [&](){
  disp += minibatch_size;
  };
  // 開始訓(xùn)練
  nn.train<mse>(optimizer, train_images, train_labels, minibatch_size, num_epochs, on_enumerate_minibatch, on_enumerate_epoch);

將神經(jīng)網(wǎng)絡(luò)的權(quán)重輸出到"weights"中。
訓(xùn)練結(jié)束后,根據(jù)輸出可以看到單個字符有1806/1870=97%的識別率,假設(shè)驗證碼有4個字符,那么有0.97^4=0.88左右的識別率??紤]到驗證碼分割大概有20%概率不會成功,整體驗證碼識別率應(yīng)該在0.88*0.8= 70%左右。


之前驗證碼的識別結(jié)果如下

查詢成績

官網(wǎng)將cookie中的PHPSESSID值作為用戶的身份標(biāo)識,因此在獲取驗證碼和查詢成績時設(shè)置同樣的PHPSESSID即可。

$content    = $this->curlHelper->request($this->config["captchaUrl"]);
file_put_contents("bin/captcha$index.png",$content);
exec("cd bin;./recognizer captcha$index.png",$ret);

需要注意的是,如果網(wǎng)站有反爬蟲機制,對訪問頻次和ip有限制,則需要使用ip池,通過代理訪問。最大重試次數(shù)的設(shè)置,由于150000pow((1-0.7),10)=0.885*,因此將最大重試次數(shù)設(shè)置為10時,循環(huán)15萬個身份證,基本都能識別出來

成績?nèi)霂?/h4>

數(shù)據(jù)庫中有15萬個有身份證,最終從官網(wǎng)上能查到成績的有41836選手,188077條記錄

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容