好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

Google 图片下载工具

Google 图片下载工具

 毕设做实验需要从网上下几万张图片,以前用师兄做的Flickr下载器,用Flickr的API完成的。但是Flickr上的图片是用户分享居多,通过指定的关键词去搜索,很多时候无法得到满意的图片。在Google、Bing上虽然能得到比较好的搜索结果,但是Google早早地停用了搜索的SDK,CodeProject上的例子是N年前的,试过都不能用了;Bing虽然现在还有SDK,但是看官方的通告,大约是8月份也要停用了,而且现在提供的下载限制每天一张,木有办法,只能自己想招了。

       在查看Google图片搜索页面的源码时,发现在<a>的href属性里面包含了图片原始的url,所以就想到解析搜索结果页面的办法,将原始图片的url切出来,然后从url现在原始的图片。url的切割,可以使用正则表达式来完成。

       还有一个问题就是搜索结果页面的获取。

       看了下搜索结果的url,发现在url里面包含了搜索关键字,以及其他的一些信息,于是想:是不是可以拼接字符串,然后向google发送web请求,返回结果可能就是搜索结果页面吧。

       这个url是直接复制浏览器搜索结果的url,然后替换掉q和oq两个参数,其值为搜索关键词。举个例子,关键词是answer,发送请求,保存返回的结果,得到如下页面:

       总过有21张图片,可见Google对这种形式的web请求的返回结果做了一些限制,不可以像使用浏览器那样,一次返回很多页,只能一次返回21张。

       然后,点了下分页的“2”,看第二页,发现url有了些变化:

       关键在于这个start属性!start=21,那应该是说,利用这个属性来做的分页,但是了,又不知道哪个属性指定end,略郁闷。。。但还好了,至少可以说,在拼url的时候,可以通过指定start的值,发送多次请求,得到多个搜索结果分页,虽然笨一点,但是目的还是达到了。

       Nice!

       最后使用的url如下:

       q指定搜索关键词,start索引各个分页的起始位置。

       以上就是编程在Google上搜索关键词并下载图片的基本思路。

       有几个问题还要记录下:

       1、搜索返回页面的问题。

       直接向Google发送请求,然后从搜索结果里面用RE分割url,耗费内存资源太多。实验发现,一个搜索返回页面200k,同时下载关键词100个,每个关键词下载200张,也就是10个页面,如果都在内存里完成的话,不算光是存这些东西,就需要大约200M,还要用RE来切割url,实际跑的时候看了下,内存爆了都,我电脑是4G的内存。而且,由于网络的问题,有时候下载过程会中断,想了下,还是先把搜索结果存到本地,然后再切割url。

        2、RE切割图片的问题。

        RE学的不够好,自己写了几个总是有问题,就在网上找了下面的一个:

        这个只能切除url,还需要加一些后缀过滤,才能得到图片的url。还在琢磨怎么写一个高级的RE,直接把图片的url切出来。

         3、多线程下载的问题。

         因为同时下载100多个关键词的图片,就想多线程来做,撇开网络带宽的问题,希望这样能下载的快一点。初始的时候,每个关键词开启一个线程。但是这样还是慢,每个关键词下载额定200张的图片,需要1-2个小时,当然,是在多个关键词同时下的情况。72个关键词,实际下到8000多张图片,共计下了一晚上,6、7个小时吧。忍不了。。

        后来,想着在每个关键词下载过程中,给每个下载链接起一个线程。结果,由于同时起的线程过多,导致线程资源短缺,频繁的有线程挂掉,虽然在一定的阶段里,同等网络条件下,确实下的快了,但是整体上却是慢了。也尝试了ThreadPool,情况类似。

        我想的理想情况是,开200个线程,维护一个线程池,有需求就请求线程作业,作业完毕则释放资源,如果当前没有资源,则请求端等待,直到有可用线程。暂时没有实现,最近要再搞一下。

        此外,在下载中,还做了log,并对图片重新编号,还有图片名与url的映射字典。

        以下附程序代码:

       

Program.cs 1   using  System;
 2   using  System.Collections.Generic;
 3   using  System.IO;
 4   using  System.Linq;
 5   using  System.Threading;
 6   using  System.Windows.Forms;
 7  
 8   namespace  GoogleImageDownload
 9  {
10       static   class  Program
11      {
12           ///   <summary>
13            ///  应用程序的主入口点。
14            ///   </summary>
15           [STAThread]
16           static   void  Main()
17          {
18              StreamReader sr =  new  StreamReader( " keywords.txt " );
19               string  keywordPool = sr.ReadToEnd();
20              sr.Close();
21               string [] keywords = keywordPool.Split( new   char [] {  ' , '  });
22               foreach  ( string  keyword  in  keywords)
23              {
24                  GoogleImages bi =  new  GoogleImages();
25                  Thread download =  new  Thread( new  ParameterizedThreadStart(bi.DownLoadImages));
26                  download.Name =  " Thread_ "  + keyword;
27                  download.Start(keyword);
28                   // WaitCallback wc = new WaitCallback(bi.DownLoadImages);
29                    // ThreadPool.QueueUserWorkItem(wc, keyword);
30               }
31          }
32      }
33  }

GoogleImages

 1   using  System;
  2   using  System.Collections.Generic;
  3   using  System.Net;
  4   using  System.Xml.Linq;
  5   using  System.IO;
  6   using  System.Web;
  7   using  System.Text.RegularExpressions;
  8   using  System.Threading;
  9   using  System.Drawing;
 10   using  System.Text;
 11  
 12   namespace  GoogleImageDownload
 13  {
 14       public   class  GoogleImages
 15      {
 16           ///   <summary>
 17            ///  通过拼url的方式,向google发出请求
 18            ///  参数0:查询关键字
 19            ///  参数1:从那一条搜索记录开始,每页默认21个,通过设置为0、21、42、63等,获取多张图片
 20            ///   </summary>
 21            private   const   string  IMG_URL =  " http://www.google.com.hk/search?q={0}&hl=zh-CN&newwindow=1&safe=strict&biw=1280& "  +
 22               " bih=699&gbv=2&ie=UTF-8&tbm=isch&ei=2HblT4vrCISwiQeavLhZ&start={1}&sa=N " ;
 23           ///   <summary>
 24            ///  默认POST 10 页
 25            ///   </summary>
 26            private   const   int  PAGES =  10 ;
 27           ///   <summary>
 28            ///  提供四种出错信息
 29            ///   </summary>
 30            public   static   string [] ERRORS = {  " GetDownloadInfo " ,  " CreateImageDownloadLink " ,  " SaveToLocal " ,  " RenameImage "  };
 31           private   string  logFile =  "" ;
 32           private   string  downloadFolder =  "" ;
 33           private   string  downloadObj =  "" ;
 34  
 35           ///   <summary>
 36            ///  Download images from Google
 37            ///   </summary>
 38            public   void  DownLoadImages( object  Obj)
 39          {
 40              DateTime tStart = DateTime.Now;
 41               this .downloadObj = ( string )Obj;
 42  
 43               ///  Images:每个关键字为单独的一个文件夹,该文件夹下保存图片
 44                ///  DownloadInfos:POST得到的Google搜索结果的页面,以及页面中图片的url
 45                ///  Log:下载图片中出现的异常信息
 46                #region  创建保存下载信息的文件夹
 47  
 48               ///  创建图片文件夹
 49                if  (!Directory.Exists(String.Format( " .\\Images\\{0} " , downloadObj)))
 50              {
 51                  Directory.CreateDirectory(String.Format( " .\\Images\\{0} " , downloadObj));
 52              }
 53               this .downloadFolder = String.Format( " .\\Images\\{0} " , downloadObj);
 54  
 55               ///  创建下载信息文件夹
 56                if  (!Directory.Exists(String.Format( " .\\DownloadInfos " , downloadObj)))
 57              {
 58                  Directory.CreateDirectory(String.Format( " .\\DownloadInfos " , downloadObj));
 59              }
 60  
 61               string  resHtmlFile =  " .\\DownloadInfos\\ "  + downloadObj +  " _res.txt " ;
 62               if  (!File.Exists(resHtmlFile))
 63              {
 64                  File.Create(resHtmlFile);
 65              }
 66  
 67               string  resImageList =  " .\\DownloadInfos\\ "  + downloadObj +  " _img.txt " ;
 68               if  (!File.Exists(resImageList))
 69              {
 70                  File.Create(resImageList);
 71              }
 72  
 73               ///  创建日志文件夹
 74                if  (!Directory.Exists(String.Format( " .\\Log " , downloadObj)))
 75              {
 76                  Directory.CreateDirectory(String.Format( " .\\Log " , downloadObj));
 77              }
 78  
 79               this .logFile =  " .\\Log\\ "  + ( string )Obj +  " .log " ;
 80               if  (!File.Exists(logFile))
 81              {
 82                  File.Create(logFile);
 83              }
 84  
 85               #endregion
 86  
 87               ///  确定下载几页,模拟了在搜索结果中手动翻页
 88                ///  默认10页,每页大约21张图片
 89  
 90               #region  POST 10 个请求,返回10个Google搜索结果页面,所有内容存在一个文本文件里面
 91  
 92               for  ( int  i =  0 ; i < PAGES; i++)
 93              {
 94                   string  url =  string .Format(IMG_URL, downloadObj,  21  * i);
 95                   try
 96                  {
 97                      System.Net.HttpWebRequest r = (System.Net.HttpWebRequest)System.Net.HttpWebRequest.Create(url);
 98                      r.AllowAutoRedirect =  true ;
 99                      System.Net.CookieContainer c =  new  System.Net.CookieContainer();
100                      r.CookieContainer = c;
101                      System.Net.HttpWebResponse res = r.GetResponse()  as  System.Net.HttpWebResponse;
102                       if  (res.StatusCode == HttpStatusCode.OK)
103                      {
104                          System.IO.StreamReader s =  new  System.IO.StreamReader(res.GetResponseStream(), System.Text.Encoding.GetEncoding( " GB2312 " ));
105                           // Response.Write(s.ReadToEnd());
106                           Console.WriteLine( " start " );
107                          StreamWriter sw =  new  StreamWriter(resHtmlFile,  true );
108                          sw.Write(s.ReadToEnd());
109                          Console.WriteLine(downloadObj +  "   "  + i);
110                          sw.Close();
111                          s.Close();
112                          res.Close();
113                      }
114                  }
115                   catch  (Exception ex)
116                  {
117                      PrintException( 0 , downloadObj, ex.ToString());
118                  }
119  
120                  Console.WriteLine( " end " );
121              }
122  
123               #endregion
124  
125               #region  从文本文件中用RE切出图片的url,并下载图片
126  
127              StreamReader sr =  new  StreamReader(resHtmlFile);
128               string  result = sr.ReadToEnd();
129              sr.Close();
130  
131               ///  一般网址url的RE
132                string  strRegex =  " (http[s]{0,1}|ftp)://[a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,4})(:\\d+)?(/[a-zA-Z0-9\\.\\-~!@#$%^&*+?:_/=<>]*)? " ; 
133              Regex re =  new  Regex(strRegex);
134              MatchCollection mactes = re.Matches(result);
135  
136               int  count =  0 ;
137               foreach  (Match img  in  mactes)
138              {
139                   string  tmp = img.Value;
140                   ///  割掉RE得到的多余的内容
141                    if  (tmp.Contains( " &amp " ))
142                      tmp = tmp.Substring( 0 , tmp.Length -  4 );
143                   ///  过滤url,专找图片的url
144                    if  (tmp.Contains( " .jpg " ) || tmp.Contains( " .png " ) || tmp.Contains( " .jpeg " ) || tmp.Contains( " .gif " ))
145                  {
146                       string  newFileName =  "" ;
147                       string [] split = tmp.Split( new   char []{ ' / ' });
148                       ///  给图片分配一个新的名字
149                        try
150                      {
151                          FileInfo fi =  new  FileInfo(split[split.Length -  1 ]);
152                          newFileName = String.Format( " {0}_{1}{2} " ,  this .downloadObj, count.ToString( " 000 " ), fi.Extension);
153                          count++;
154                           ///  输出“newFileName    ImageUrl”到DownloadInfos
155                           StreamWriter sw2 =  new  StreamWriter(resImageList,  true );
156                          sw2.WriteLine(String.Format( " {0}\t{1} " , newFileName, tmp));
157                          sw2.Flush();
158                          sw2.Close();
159                      }
160                       catch  (Exception ex)
161                      {
162                          PrintException( 0 , split[split.Length -  1 ], ex.ToString());
163                      }
164  
165                      Console.WriteLine(split[split.Length- 1 ]+ "  is downloading " );
166                       /////////////////////
167                        //  Download Images  //
168                        /////////////////////
169                       SavePhotoFromUrl(newFileName, tmp);
170                       // ThreadPool.QueueUserWorkItem(new WaitCallback(this.SavePhotoFromUrl), new string[] { newFileName, tmp });
171                        // Thread save = new Thread(new ParameterizedThreadStart(this.SavePhotoFromUrl));
172                        // save.Name = "Thread_" + newFileName;
173                        // save.Start(new string[] { newFileName, tmp });
174                       Console.WriteLine(split[split.Length- 1 ]+ "  has been downloaded " );
175                  }
176              }
177              
178               #endregion
179              
180               ///  输出下载用时
181               DateTime tEnd = DateTime.Now;
182              TimeSpan cost = tEnd - tStart;
183              PrintTime(cost.ToString()); 
184          }
185  
186           ///   <summary>
187            ///  通过url将图片保存到本地,指定文件名为FileName
188            ///   </summary>
189            public   bool  SavePhotoFromUrl( string  FileName,  string  Url)
190           // public void SavePhotoFromUrl(Object paras)
191           {
192               // string[] Para = (string[])paras;
193                // string FileName = Para[0];
194                // string Url = Para[1];
195                bool  Value =  false ;
196              WebResponse response =  null ;
197              Stream stream =  null ;
198  
199               try
200              {
201                  HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Url);
202                  response = request.GetResponse();
203                  stream = response.GetResponseStream();
204                  Value = SaveBinaryFile(response,  this .downloadFolder +  " \\ "  + FileName);
205              }
206               catch  (Exception ex)
207              {
208                  PrintException( 1 , Url, ex.ToString());
209              }
210               return  Value;
211          }
212          ///   <summary>
213           ///  保存图片到本地
214           ///   </summary>
215           ///   <param name="response"> 用来保存图片的Response </param>
216            private   bool  SaveBinaryFile(WebResponse response,  string  FileName)
217          {
218               bool  Value =  true ;
219               byte [] buffer =  new   byte [ 1024 ];
220  
221               try
222              {
223                   if  (File.Exists(FileName))
224                  {
225                       return   true ;
226                  }
227                  Stream outStream = System.IO.File.Create(FileName);
228                  Stream inStream = response.GetResponseStream();
229  
230                   int  l;
231                   do
232                  {
233                      l = inStream.Read(buffer,  0 , buffer.Length);
234                       if  (l >  0 )
235                          outStream.Write(buffer,  0 , l);
236                  }
237                   while  (l >  0 );
238  
239                  outStream.Close();
240                  inStream.Close();
241              }
242               catch  (Exception ex)
243              {
244                  PrintException( 2 , FileName, ex.ToString());
245                  Value =  false ;
246              }
247               return  Value;
248          }
249  
250           ///   <summary>
251            ///  在下载过程中打印出错信息
252            ///  三种出错信息:
253            ///  0:GetDownloadInfo           在向Google请求下载信息的时候出错
254            ///  1:CreateImageDownloadLink   在获取图片url后建立连接过程中出错
255            ///  2:SaveToLocal               在建立下载连接后保存到本地过程中出错
256            ///  3: RenameImage               按照标准命名方式重命名文件过程中出错
257            ///   </summary>
258            ///   <param name="type"> 出错类型 </param>
259            ///   <param name="obj"> 出错对象 </param>
260            ///   <param name="exceptionInfo"> 出错信息 </param>
261            private   void  PrintException( int  type,  string  obj,  string  exceptionInfo)
262          {
263               try
264              {
265                  StreamWriter sPrint =  new  StreamWriter( this .logFile,  true );
266                  sPrint.WriteLine(String.Format( " TYPE:{0}\tOBJECT:[{1,-30}]\nERROR:{2}\n " , GoogleImages.ERRORS[type], obj, exceptionInfo));
267                  sPrint.Close();
268              }
269               catch  (Exception ex)
270              {
271                  ;
272              }
273          }
274  
275           ///   <summary>
276            ///  输出下载所用时间
277            ///   </summary>
278            ///   <param name="time"> 下载用时 </param>
279            private   void  PrintTime( string  time)
280          {
281               try
282              {
283                  StreamWriter sPrint =  new  StreamWriter( this .logFile,  true );
284                  sPrint.WriteLine(String.Format( " Download Cost:{0} " ,time));
285                  sPrint.Close();
286              }
287               catch  (Exception ex)
288              {
289                  ;
290              }
291          }
292      }
293  }  

         附:Google url中各个参数的含义(

http://www.4ucode.com/Study/Topic/1060948 ):

         hl(Interface Language):Google搜索的界面语言

         q(Query):查询的关键词

         start:显示搜索结果的起始端,如果start=1,则从第2个搜索结果开始显示;如果你想直接看第搜索结果第21页,让start=200即可,由于Google只显示1000条搜索结果记录,start理论取值范围在0–999之间

         lr(Language Restrict):搜索内容的语言限定限定只搜索某种语言的网页。如果lr参数为空,则为搜索所有网页

         ie(Input Encoding):查询关键词的编码,缺省设置为utf-8,也就是说请求Google搜索时参数q的值是一段utf-8编码的文字

         oe(Output Encoding):搜索结果页面的网页编码,缺省设置oe=utf-8 
         num(Number):搜索结果显示条数,取值范围在10–100条之间,缺省设置num=10 
         newwindow:是否开启新窗口以显示查询结果,缺省设置newwindow=1,在新窗口打开搜索结果而面 
         aq(Ascending Query):判断搜索用户是否是第一次查询,如果用户第一次进行查询,则aq=f(First);如若进行过多次查询,则aq=-1,这个的主要作用应该是统计和放置作-弊 
         as_q(Ascending Search Query):上一次查询关键词

        

         欢迎指教&讨论~

 

分类:  C#

标签:  Google ,  RE ,  图片下载

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于Google 图片下载工具的详细内容...

  阅读:72次

上一篇: 再造巴别塔登天

下一篇:重读<算法导论>