一、前言
使用tesseract3.02識別有驗證碼的網站 安裝tesseract3.02 在VS nuget 搜索Tesseract即可。
二、項目結構圖
三、項目主要代碼
1 using System; 2 using System.Collections.Concurrent; 3 using System.Collections.Generic; 4 using System.Diagnostics; 5 using System.Drawing; 6 using System.IO; 7 using System.Net; 8 using System.Net.Cache; 9 using System.Text; 10 11 namespace Tesseract.Test.Tools 12 { 13 /// <summary> 14 /// http幫助類 15 /// </summary> 16 public class HttpHelper 17 { 18 /// <summary> 19 /// 異步事件 20 /// </summary> 21 public HttpHelper() 22 { 23 CookieContainer = new CookieContainer(); 24 Encoding = Encoding.UTF8; 25 } 26 27 /// <summary> 28 /// 訪問次數字典 29 /// </summary> 30 private ConcurrentDictionary<String, int> urlTryList = new ConcurrentDictionary<string, int>(); 31 32 /// <summary> 33 /// Cookie 容器 34 /// </summary> 35 public CookieContainer CookieContainer { set; get; } 36 37 /// <summary> 38 /// Post數據 39 /// </summary> 40 public String PostData { set; private get; } 41 42 /// <summary> 43 /// 頁面語言 44 /// </summary> 45 public Encoding Encoding { set; private get; } 46 47 /// <summary> 48 /// 驗證碼路徑 49 /// </summary> 50 public string CodePath { get; set; } 51 52 /// <summary> 53 /// 文件保存路徑 54 /// </summary> 55 public String FileSavePath { set; private get; } 56 57 /// <summary> 58 /// 回調時間 59 /// </summary> 60 public Action<String, String> CallBackAction; 61 62 63 /// <summary> 64 /// 異步請求 65 /// </summary> 66 /// <param name="url">請求地址</param> 67 /// <param name="tryTimes">錯誤重試次數</param> 68 public void AsynRequest(String url, int tryTimes = 3) 69 { 70 Trace.TraceInformation(String.Concat("開始異步請求:", url)); 71 urlTryList.TryAdd(url, tryTimes); 72 var request = WebRequest.Create(url) as HttpWebRequest; 73 if (request == null) return; 74 request.Headers.Add("Accept-Encoding", "gzip,deflate,sdch"); 75 request.Headers.Add("Accept-Language", "zh-CN,zh;q=0.8"); 76 request.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate | 77 DecompressionMethods.None; 78 request.Credentials = CredentialCache.DefaultNetworkCredentials; 79 request.UseDefaultCredentials = false; 80 request.KeepAlive = false; 81 request.PreAuthenticate = false; 82 request.ProtocolVersion = HttpVersion.Version10; 83 request.UserAgent = 84 "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.116 Safari/537.36"; 85 request.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"; 86 //request.CachePolicy = new RequestCachePolicy(RequestCacheLevel.NoCacheNoStore); 87 request.Timeout = 1000*60*3; 88 request.CookieContainer = CookieContainer; 89 //request.AllowAutoRedirect = false; 90 91 if (!String.IsNullOrEmpty(PostData)) 92 { 93 request.ContentType = "application/x-www-form-urlencoded"; 94 request.Method = "POST"; 95 request.BeginGetRequestStream(GetRequestStreamCallback, request); 96 } 97 else 98 { 99 //request.AllowReadStreamBuffering = false; 100 request.AllowWriteStreamBuffering = false; 101 request.BeginGetResponse(GetResponseCallback, request); 102 } 103 } 104 105 /// <summary> 106 /// 開始對用來寫入數據的 Stream 對象的異步請求。 107 /// </summary> 108 /// <param name="ar"></param> 109 private void GetRequestStreamCallback(IAsyncResult ar) 110 { 111 var request = ar.AsyncState as HttpWebRequest; 112 if (request == null) return; 113 var postStream = request.EndGetRequestStream(ar); 114 var byteArray = Encoding.GetBytes(PostData); 115 postStream.Write(byteArray, 0, PostData.Length); 116 postStream.Close(); 117 request.BeginGetResponse(GetResponseCallback, request); 118 } 119 120 /// <summary> 121 /// 開始對 Internet 資源的異步請求。 122 /// </summary> 123 /// <param name="ar"></param> 124 private void GetResponseCallback(IAsyncResult ar) 125 { 126 var request = ar.AsyncState as HttpWebRequest; 127 if (request == null) return; 128 try 129 { 130 using (var response = request.EndGetResponse(ar) as HttpWebResponse) 131 { 132 if (response != null) 133 { 134 //if (response.StatusCode == HttpStatusCode.Found) 135 //{ 136 // string redirect = response.Headers["Location"]; 137 // if (!String.IsNullOrEmpty(redirect)) AsynRequest(redirect); 138 // return; 139 //} 140 141 if (response.StatusCode != HttpStatusCode.OK) 142 { 143 Trace.TraceError(String.Concat("請求地址:", request.RequestUri, " 失敗,HttpStatusCode", 144 response.StatusCode)); 145 return; 146 } 147 148 using (var streamResponse = response.GetResponseStream()) 149 { 150 if (streamResponse != null) 151 { 152 if (!IsText(response.ContentType)) 153 { 154 var contentEncodingStr = response.ContentEncoding; 155 var contentEncoding = Encoding; 156 if (!String.IsNullOrEmpty(contentEncodingStr)) 157 contentEncoding = Encoding.GetEncoding(contentEncodingStr); 158 using (var streamRead = new StreamReader(streamResponse, contentEncoding)) 159 { 160 var str = streamRead.ReadToEnd(); 161 if (CallBackAction != null && !String.IsNullOrEmpty(str)) 162 CallBackAction.BeginInvoke(str, request.RequestUri.ToString(), (s) => { }, 163 null); 164 } 165 } 166 else 167 { 168 var fileName = String.Concat(DateTime.Now.ToString("yyyyMMdd"), "/", 169 DateTime.Now.ToString("yyyyMMddHHmmssffff"), 170 //Extensions.String_.Extensions.GetRnd(6, true, false, false, false, String.Empty), 171 ".jpg"); 172 var fileDirectory = Path.Combine(FileSavePath, DateTime.Now.ToString("yyyyMMdd")); 173 CodePath = Path.Combine(FileSavePath, fileName); 174 if (!Directory.Exists(fileDirectory)) 175 Directory.CreateDirectory(fileDirectory); 176 177 //下載文件 178 using ( 179 var fileStream = new FileStream(Path.Combine(FileSavePath, fileName), 180 FileMode.Create)) 181 { 182 var buffer = new byte[2048]; 183 int readLength; 184 do 185 { 186 readLength = streamResponse.Read(buffer, 0, buffer.Length); 187 fileStream.Write(buffer, 0, readLength); 188 } while (readLength != 0); 189 } 190 if (CallBackAction != null && !String.IsNullOrEmpty(fileName)) 191 CallBackAction.BeginInvoke(fileName, request.RequestUri.ToString(), (s) => { }, 192 null); 193 } 194 } 195 } 196 response.Close(); 197 } 198 } 199 } 200 catch (WebException ex) 201 { 202 Trace.TraceError(String.Concat("請求地址:", request.RequestUri, " 失敗信息:", ex.Message)); 203 var toUrl = request.RequestUri.ToString(); 204 int tryTimes; 205 if (urlTryList.TryGetValue(toUrl, out tryTimes)) 206 { 207 urlTryList.TryUpdate(toUrl, tryTimes, tryTimes - 1); 208 if (tryTimes - 1 <= 0) 209 { 210 urlTryList.TryRemove(toUrl, out tryTimes); 211 return; 212 } 213 AsynRequest(toUrl); 214 } 215 } 216 finally 217 { 218 request.Abort(); 219 } 220 } 221 222 223 /// <summary> 224 /// 同步請求 225 /// </summary> 226 /// <param name="url">請求地址</param> 227 /// <param name="tryTimes">錯誤重試次數</param> 228 public String SyncRequest(String url, int tryTimes = 3) 229 { 230 Trace.TraceInformation(String.Concat("開始同步請求:", url)); 231 urlTryList.TryAdd(url, tryTimes); 232 var request = WebRequest.Create(url) as HttpWebRequest; 233 if (request == null) return String.Empty; 234 request.Headers.Add("Accept-Encoding", "gzip,deflate,sdch"); 235 request.Headers.Add("Accept-Language", "zh-CN,zh;q=0.8"); 236 request.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate | 237 DecompressionMethods.None; 238 request.Credentials = CredentialCache.DefaultNetworkCredentials; 239 request.UseDefaultCredentials = false; 240 request.KeepAlive = false; 241 request.PreAuthenticate = false; 242 request.ProtocolVersion = HttpVersion.Version10; 243 request.UserAgent = 244 "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.116 Safari/537.36"; 245 request.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"; 246 request.CachePolicy = new RequestCachePolicy(RequestCacheLevel.NoCacheNoStore); 247 request.Timeout = 1000*60*3; 248 request.CookieContainer = CookieContainer; 249 request.AllowAutoRedirect = true; 250 251 if (!String.IsNullOrEmpty(PostData)) 252 { 253 request.ContentType = "application/x-www-form-urlencoded"; 254 request.Method = "POST"; 255 using (var postStream = request.GetRequestStream()) 256 { 257 var byteArray = Encoding.GetBytes(PostData); 258 postStream.Write(byteArray, 0, PostData.Length); 259 postStream.Close(); 260 } 261 } 262 else 263 { 264 //request.AllowReadStreamBuffering = false; 265 request.AllowWriteStreamBuffering = false; 266 } 267 try 268 { 269 using (var response = request.GetResponse() as HttpWebResponse) 270 { 271 if (response != null) 272 { 273 if (response.StatusCode != HttpStatusCode.OK) 274 { 275 Trace.TraceError(String.Concat("請求地址:", request.RequestUri, " 失敗,HttpStatusCode", 276 response.StatusCode)); 277 return String.Empty; 278 } 279 using (var streamResponse = response.GetResponseStream()) 280 { 281 if (streamResponse != null) 282 { 283 if (!IsText(response.ContentType)) 284 { 285 var contentEncodingStr = response.ContentEncoding; 286 var contentEncoding = Encoding; 287 if (!String.IsNullOrEmpty(contentEncodingStr)) 288 contentEncoding = Encoding.GetEncoding(contentEncodingStr); 289 var streamRead = new StreamReader(streamResponse, contentEncoding); 290 var str = streamRead.ReadToEnd(); 291 if (CallBackAction != null && !String.IsNullOrEmpty(str)) 292 CallBackAction.BeginInvoke(str, request.RequestUri.ToString(), (s) => { }, null); 293 return str; 294 } 295 296 var fileName = String.Concat(DateTime.Now.ToString("yyyyMMdd"), "/", 297 DateTime.Now.ToString("yyyyMMddHHmmssffff"), 298 //Extensions.String_.Extensions.GetRnd(6, true, false, false, false, String.Empty), 299 Path.GetExtension(request.RequestUri.AbsoluteUri)); 300 var fileDirectory = Path.Combine(FileSavePath, DateTime.Now.ToString("yyyyMMdd")); 301 if (!Directory.Exists(fileDirectory)) 302 Directory.CreateDirectory(fileDirectory); 303 304 //下載文件 305 using ( 306 var fileStream = new FileStream(Path.Combine(FileSavePath, fileName), 307 FileMode.Create)) 308 { 309 var buffer = new byte[2048]; 310 int readLength; 311 do 312 { 313 readLength = streamResponse.Read(buffer, 0, buffer.Length); 314 fileStream.Write(buffer, 0, readLength); 315 } while (readLength != 0); 316 } 317 if (CallBackAction != null && !String.IsNullOrEmpty(fileName)) 318 CallBackAction.BeginInvoke(fileName, request.RequestUri.ToString(), (s) => { }, null); 319 return fileName; 320 } 321 } 322 response.Close(); 323 } 324 } 325 } 326 catch (WebException ex) 327 { 328 Trace.TraceError(String.Concat("請求地址:", request.RequestUri, " 失敗信息:", ex.Message)); 329 var toUrl = request.RequestUri.ToString(); 330 if (urlTryList.TryGetValue(toUrl, out tryTimes)) 331 { 332 urlTryList.TryUpdate(toUrl, tryTimes, tryTimes - 1); 333 if (tryTimes - 1 <= 0) 334 { 335 urlTryList.TryRemove(toUrl, out tryTimes); 336 Trace.TraceError(String.Concat("請求地址重試失敗:", request.RequestUri)); 337 return String.Empty; 338 } 339 SyncRequest(toUrl); 340 } 341 } 342 finally 343 { 344 request.Abort(); 345 } 346 return String.Empty; 347 } 348 349 /// <summary> 350 /// 驗證碼獲取 351 /// </summary> 352 /// <param name="url">請求地址</param> 353 /// <param name="tryTimes">錯誤重試次數</param> 354 public Bitmap GetCheckCode(String url, int tryTimes = 3) 355 { 356 Trace.TraceInformation(String.Concat("開始同步請求:", url)); 357 urlTryList.TryAdd(url, tryTimes); 358 var request = WebRequest.Create(url) as HttpWebRequest; 359 if (request == null) return null; 360 request.Headers.Add("Accept-Encoding", "gzip,deflate,sdch"); 361 request.Headers.Add("Accept-Language", "zh-CN,zh;q=0.8"); 362 request.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate | 363 DecompressionMethods.None; 364 request.Credentials = CredentialCache.DefaultNetworkCredentials; 365 request.UseDefaultCredentials = false; 366 request.KeepAlive = false; 367 request.PreAuthenticate = false; 368 request.ProtocolVersion = HttpVersion.Version10; 369 request.UserAgent = 370 "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.116 Safari/537.36"; 371 request.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"; 372 request.CachePolicy = new RequestCachePolicy(RequestCacheLevel.NoCacheNoStore); 373 request.Timeout = 1000*60*3; 374 request.CookieContainer = CookieContainer; 375 request.AllowAutoRedirect = true; 376 377 if (!String.IsNullOrEmpty(PostData)) 378 { 379 request.ContentType = "application/x-www-form-urlencoded"; 380 request.Method = "POST"; 381 using (var postStream = request.GetRequestStream()) 382 { 383 var byteArray = Encoding.GetBytes(PostData); 384 postStream.Write(byteArray, 0, PostData.Length); 385 postStream.Close(); 386 } 387 } 388 else 389 { 390 //request.AllowReadStreamBuffering = false; 391 request.AllowWriteStreamBuffering = false; 392 } 393 try 394 { 395 using (var response = request.GetResponse() as HttpWebResponse) 396 { 397 if (response != null) 398 { 399 if (response.StatusCode != HttpStatusCode.OK) 400 { 401 Trace.TraceError(String.Concat("請求地址:", request.RequestUri, " 失敗,HttpStatusCode", 402 response.StatusCode)); 403 return null; 404 } 405 using (var streamResponse = response.GetResponseStream()) 406 { 407 if (streamResponse != null) 408 { 409 return (Bitmap) Bitmap.FromStream(streamResponse); 410 } 411 } 412 response.Close(); 413 } 414 } 415 } 416 catch (WebException ex) 417 { 418 Trace.TraceError(String.Concat("請求地址:", request.RequestUri, " 失敗信息:", ex.Message)); 419 var toUrl = request.RequestUri.ToString(); 420 if (urlTryList.TryGetValue(toUrl, out tryTimes)) 421 { 422 urlTryList.TryUpdate(toUrl, tryTimes, tryTimes - 1); 423 if (tryTimes - 1 <= 0) 424 { 425 urlTryList.TryRemove(toUrl, out tryTimes); 426 Trace.TraceError(String.Concat("請求地址重試失敗:", request.RequestUri)); 427 return null; 428 } 429 GetCheckCode(toUrl); 430 } 431 } 432 finally 433 { 434 request.Abort(); 435 } 436 return null; 437 } 438 439 /// <summary> 440 /// 判斷文件是否為文本類型 441 /// </summary> 442 /// <param name="contentType">內容類型</param> 443 /// <returns></returns> 444 private static bool IsText(String contentType) 445 { 446 var fileContentType = new List<string> 447 { 448 "image/Bmp", 449 "image/gif", 450 "image/jpeg", 451 "image/png", 452 "image/tiff", 453 "application/octet-stream" 454 }; 455 return fileContentType.Contains(contentType); 456 } 457 } 458 } HttpHepler 1 using System; 2 using System.Drawing; 3 using System.Drawing.Drawing2D; 4 using System.Drawing.Imaging; 5 using System.Runtime.InteropServices; 6 7 namespace Tesseract.Test.Tools 8 { 9 public class UnCodebase 10 { 11 public Bitmap bmpobj; 12 13 public UnCodebase(Bitmap pic) 14 { 15 bmpobj = new Bitmap(pic); //轉換為Format32bppRgb 16 } 17 18 /// <summary> 19 /// 根據RGB,計算灰度值 20 /// </summary> 21 /// <param name="posClr">Color值</param> 22 /// <returns>灰度值,整型</returns> 23 private int GetGrayNumColor(Color posClr) 24 { 25 return (posClr.R*19595 + posClr.G*38469 + posClr.B*7472) >> 16; 26 } 27 28 /// <summary> 29 /// 灰度轉換,逐點方式 30 /// </summary> 31 public Bitmap GrayByPixels() 32 { 33 for (int i = 0; i < bmpobj.Height; i++) 34 { 35 for (int j = 0; j < bmpobj.Width; j++) 36 { 37 int tmpValue = GetGrayNumColor(bmpobj.GetPixel(j, i)); 38 bmpobj.SetPixel(j, i, Color.FromArgb(tmpValue, tmpValue, tmpValue)); 39 } 40 } 41 return bmpobj; 42 } 43 44 /// <summary> 45 /// 去圖形邊框 46 /// </summary> 47 /// <param name="borderWidth"></param> 48 public Bitmap ClearPicBorder(int borderWidth) 49 { 50 for (int i = 0; i < bmpobj.Height; i++) 51 { 52 for (int j = 0; j < bmpobj.Width; j++) 53 { 54 if (i < borderWidth || j < borderWidth || j > bmpobj.Width - 1 - borderWidth || 55 i > bmpobj.Height - 1 - borderWidth) 56 bmpobj.SetPixel(j, i, Color.FromArgb(255, 255, 255)); 57 } 58 } 59 return bmpobj; 60 } 61 62 /// <summary> 63 /// 灰度轉換,逐行方式 64 /// </summary> 65 public Bitmap GrayByLine() 66 { 67 Rectangle rec = new Rectangle(0, 0, bmpobj.Width, bmpobj.Height); 68 BitmapData bmpData = bmpobj.LockBits(rec, ImageLockMode.ReadWrite, bmpobj.PixelFormat); 69 // PixelFormat.Format32bppPArgb); 70 // bmpData.PixelFormat = PixelFormat.Format24bppRgb; 71 IntPtr scan0 = bmpData.Scan0; 72 int len = bmpobj.Width*bmpobj.Height; 73 int[] pixels = new int[len]; 74 Marshal.Copy(scan0, pixels, 0, len); 75 76 //對圖片進行處理 77 int GrayValue = 0; 78 for (int i = 0; i < len; i++) 79 { 80 GrayValue = GetGrayNumColor(Color.FromArgb(pixels[i])); 81 pixels[i] = (byte) (Color.FromArgb(GrayValue, GrayValue, GrayValue)).ToArgb(); //Color轉byte 82 } 83 84 bmpobj.UnlockBits(bmpData); 85 return bmpobj; 86 } 87 88 /// <summary> 89 /// 得到有效圖形並調整為可平均分割的大小 90 /// </summary> 91 /// <param name="dgGrayValue">灰度背景分界值</param> 92 /// <param name="CharsCount">有效字符數</param> 93 /// <returns></returns> 94 public void GetPicValidByValue(int dgGrayValue, int CharsCount) 95 { 96 int posx1 = bmpobj.Width; 97 int posy1 = bmpobj.Height; 98 int posx2 = 0; 99 int posy2 = 0; 100 for (int i = 0; i < bmpobj.Height; i++) //找有效區 101 { 102 for (int j = 0; j < bmpobj.Width; j++) 103 { 104 int pixelValue = bmpobj.GetPixel(j, i).R; 105 if (pixelValue < dgGrayValue) //根據灰度值 106 { 107 if (posx1 > j) posx1 = j; 108 if (posy1 > i) posy1 = i; 109 110 if (posx2 < j) posx2 = j; 111 if (posy2 < i) posy2 = i; 112 } 113 ; 114 } 115 ; 116 } 117 ; 118 // 確保能整除 119 int Span = CharsCount - (posx2 - posx1 + 1)%CharsCount; //可整除的差額數 120 if (Span < CharsCount) 121 { 122 int leftSpan = Span/2; //分配到左邊的空列 ,如span為單數,則右邊比左邊大1 123 if (posx1 > leftSpan) 124 posx1 = posx1 - leftSpan; 125 if (posx2 + Span - leftSpan < bmpobj.Width) 126 posx2 = posx2 + Span - leftSpan; 127 } 128 //復制新圖 129 Rectangle cloneRect = new Rectangle(posx1, posy1, posx2 - posx1 + 1, posy2 - posy1 + 1); 130 bmpobj = bmpobj.Clone(cloneRect, bmpobj.PixelFormat); 131 } 132 133 /// <summary> 134 /// 得到有效圖形,圖形為類變量 135 /// </summary> 136 /// <param name="dgGrayValue">灰度背景分界值</param> 137 /// <param name="CharsCount">有效字符數</param> 138 /// <returns></returns> 139 public void GetPicValidByValue(int dgGrayValue) 140 { 141 int posx1 = bmpobj.Width; 142 int posy1 = bmpobj.Height; 143 int posx2 = 0; 144 int posy2 = 0; 145 for (int i = 0; i < bmpobj.Height; i++) //找有效區 146 { 147 for (int j = 0; j < bmpobj.Width; j++) 148 { 149 int pixelValue = bmpobj.GetPixel(j, i).R; 150 if (pixelValue < dgGrayValue) //根據灰度值 151 { 152 if (posx1 > j) posx1 = j; 153 if (posy1 > i) posy1 = i; 154 155 if (posx2 < j) posx2 = j; 156 if (posy2 < i) posy2 = i; 157 } 158 ; 159 } 160 ; 161 } 162 ; 163 //復制新圖 164 Rectangle cloneRect = new Rectangle(posx1, posy1, posx2 - posx1 + 1, posy2 - posy1 + 1); 165 bmpobj = bmpobj.Clone(cloneRect, bmpobj.PixelFormat); 166 } 167 168 /// <summary> 169 /// 得到有效圖形,圖形由外面傳入 170 /// </summary> 171 /// <param name="dgGrayValue">灰度背景分界值</param> 172 /// <param name="CharsCount">有效字符數</param> 173 /// <returns></returns> 174 public Bitmap GetPicValidByValue(Bitmap singlepic, int dgGrayValue) 175 { 176 int posx1 = singlepic.Width; 177 int posy1 = singlepic.Height; 178 int posx2 = 0; 179 int posy2 = 0; 180 for (int i = 0; i < singlepic.Height; i++) //找有效區 181 { 182 for (int j = 0; j < singlepic.Width; j++) 183 { 184 int pixelValue = singlepic.GetPixel(j, i).R; 185 if (pixelValue < dgGrayValue) //根據灰度值 186 { 187 if (posx1 > j) posx1 = j; 188 if (posy1 > i) posy1 = i; 189 190 if (posx2 < j) posx2 = j; 191 if (posy2 < i) posy2 = i; 192 } 193 ; 194 } 195 ; 196 } 197 ; 198 //復制新圖 199 Rectangle cloneRect = new Rectangle(posx1, posy1, posx2 - posx1 + 1, posy2 - posy1 + 1); 200 return singlepic.Clone(cloneRect, singlepic.PixelFormat); 201 } 202 203 /// <summary> 204 /// 平均分割圖片 205 /// </summary> 206 /// <param name="RowNum">水平上分割數</param> 207 /// <param name="ColNum">垂直上分割數</param> 208 /// <returns>分割好的圖片數組</returns> 209 public Bitmap[] GetSplitPics(int RowNum, int ColNum) 210 { 211 if (RowNum == 0 || ColNum == 0) 212 return null; 213 int singW = bmpobj.Width/RowNum; 214 int singH = bmpobj.Height/ColNum; 215 Bitmap[] PicArray = new Bitmap[RowNum*ColNum]; 216 217 Rectangle cloneRect; 218 for (int i = 0; i < ColNum; i++) //找有效區 219 { 220 for (int j = 0; j < RowNum; j++) 221 { 222 cloneRect = new Rectangle(j*singW, i*singH, singW, singH); 223 PicArray[i*RowNum + j] = bmpobj.Clone(cloneRect, bmpobj.PixelFormat); //復制小塊圖 224 } 225 } 226 return PicArray; 227 } 228 229 /// <summary> 230 /// 返回灰度圖片的點陣描述字串,1表示灰點,0表示背景 231 /// </summary> 232 /// <param name="singlepic">灰度圖</param> 233 /// <param name="dgGrayValue">背前景灰色界限</param> 234 /// <returns></returns> 235 public string GetSingleBmpCode(Bitmap singlepic, int dgGrayValue) 236 { 237 Color piexl; 238 string code = ""; 239 for (int posy = 0; posy < singlepic.Height; posy++) 240 for (int posx = 0; posx < singlepic.Width; posx++) 241 { 242 piexl = singlepic.GetPixel(posx, posy); 243 if (piexl.R < dgGrayValue) // Color.Black ) 244 code = code + "1"; 245 else 246 code = code + "0"; 247 } 248 return code; 249 } 250 251 /// <summary> 252 /// 去掉噪點 253 /// </summary> 254 /// <param name="dgGrayValue"></param> 255 /// <param name="MaxNearPoints"></param> 256 public Bitmap ClearNoise(int dgGrayValue, int MaxNearPoints) 257 { 258 Color piexl; 259 int nearDots = 0; 260 int XSpan, YSpan, tmpX, tmpY; 261 //逐點判斷 262 for (int i = 0; i < bmpobj.Width; i++) 263 for (int j = 0; j < bmpobj.Height; j++) 264 { 265 piexl = bmpobj.GetPixel(i, j); 266 if (piexl.R < dgGrayValue) 267 { 268 nearDots = 0; 269 //判斷周圍8個點是否全為空 270 if (i == 0 || i == bmpobj.Width - 1 || j == 0 || j == bmpobj.Height - 1) //邊框全去掉 271 { 272 bmpobj.SetPixel(i, j, Color.FromArgb(255, 255, 255)); 273 } 274 else 275 { 276 if (bmpobj.GetPixel(i - 1, j - 1).R < dgGrayValue) nearDots++; 277 if (bmpobj.GetPixel(i, j - 1).R < dgGrayValue) nearDots++; 278 if (bmpobj.GetPixel(i + 1, j - 1).R < dgGrayValue) nearDots++; 279 if (bmpobj.GetPixel(i - 1, j).R < dgGrayValue) nearDots++; 280 if (bmpobj.GetPixel(i + 1, j).R < dgGrayValue) nearDots++; 281 if (bmpobj.GetPixel(i - 1, j + 1).R < dgGrayValue) nearDots++; 282 if (bmpobj.GetPixel(i, j + 1).R < dgGrayValue) nearDots++; 283 if (bmpobj.GetPixel(i + 1, j + 1).R < dgGrayValue) nearDots++; 284 } 285 286 if (nearDots < MaxNearPoints) 287 bmpobj.SetPixel(i, j, Color.FromArgb(255, 255, 255)); //去掉單點 && 粗細小3鄰邊點 288 } 289 else //背景 290 bmpobj.SetPixel(i, j, Color.FromArgb(255, 255, 255)); 291 } 292 return bmpobj; 293 } 294 295 /// <summary> 296 /// 扭曲圖片校正 297 /// </summary> 298 public Bitmap ReSetBitMap() 299 { 300 Graphics g = Graphics.FromImage(bmpobj); 301 Matrix X = new Matrix(); 302 // X.Rotate(30); 303 X.Shear((float) 0.16666666667, 0); // 2/12 304 g.Transform = X; 305 // Draw image 306 //Rectangle cloneRect = GetPicValidByValue(128); //Get Valid Pic Rectangle 307 Rectangle cloneRect = new Rectangle(0, 0, bmpobj.Width, bmpobj.Height); 308 Bitmap tmpBmp = bmpobj.Clone(cloneRect, bmpobj.PixelFormat); 309 g.DrawImage(tmpBmp, 310 new Rectangle(0, 0, bmpobj.Width, bmpobj.Height), 311 0, 0, tmpBmp.Width, 312 tmpBmp.Height, 313 GraphicsUnit.Pixel); 314 315 return tmpBmp; 316 } 317 } 318 } UnCodebase 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using Tesseract.Test.Tools; 7 8 namespace Tesseract.Test 9 { 10 /// <summary> 11 /// 網站用戶 12 /// </summary> 13 public class WebUser 14 { 15 /// <summary> 16 /// 用戶名 17 /// </summary> 18 private string _userName = string.Empty; 19 20 /// <summary> 21 /// 密碼 22 /// </summary> 23 private string _password = string.Empty; 24 25 /// <summary> 26 /// 驗證碼 27 /// </summary> 28 private string _checkCode = string.Empty; 29 30 private HttpHelper _httpHelper = new HttpHelper(); 31 32 /// <summary> 33 /// 構造函數 34 /// </summary> 35 public WebUser() 36 { 37 _userName = "admin"; //初始化用戶 38 _password = "password"; //初始化密碼 39 Login(); 40 } 41 42 43 /// <summary> 44 /// 登錄 45 /// </summary> 46 private void Login() 47 { 48 //登錄頁 49 _httpHelper.SyncRequest("Login.html"); 50 51 //獲取驗證碼圖片 52 var bitmap = _httpHelper.GetCheckCode("CheckCode.html"); 53 54 //解析驗證碼 55 using (var engine = new TesseractEngine(@"./tessdata", "eng", EngineMode.Default)) 56 { 57 using (var entity = engine.Process(bitmap)) 58 { 59 _checkCode = entity.GetText(); 60 } 61 } 62 63 //登錄驗證 64 _httpHelper.PostData = string.Format("userName={0}&password={1}&checkcode={2}", 65 _userName, _password, _checkCode); 66 _httpHelper.SyncRequest("CheckLogin.html"); 67 } 68 } 69 } WebUser四、代碼解釋
1、模擬登錄的過程基本都是訪問登錄頁面獲取相關的cookie。
2、訪問驗證碼頁面這樣就不用將驗證碼下載到本地再解析。直接在程序內解析。減少驗證碼本地圖片文件增加。
3、使用tesseract來解析驗證碼。(需要看驗證碼的干擾是否嚴重,使用UnCodebase對驗證碼圖片適當的處理可以增加識別正確率)
4、訪問登錄驗證頁面將用戶名密碼驗證碼POST過去。
5、登錄成功。