DocumentWorker.cs
上传用户:yxdanqu
上传日期:2010-01-07
资源大小:84k
文件大小:8k
源码类别:

搜索引擎

开发平台:

C#

  1. using System;
  2. using System.Net;
  3. using System.IO;
  4. using System.Threading;
  5. namespace Spider
  6. {
  7. /// <summary>
  8. /// Perform all of the work of a single thread for the spider.
  9. /// This involves waiting for a URL to becomve available, download
  10. /// and then processing the page.
  11. /// 
  12. /// </summary>
  13. // 完成必须由单个工作线程执行的操作,包括
  14. // 等待可用的URL,下载和处理页面
  15. public class DocumentWorker
  16. {
  17. /// <summary>
  18. /// The base URI that is to be spidered.
  19. /// </summary>
  20. // 要扫描的基础URI
  21. private Uri m_uri;
  22. /// <summary>
  23. /// The spider that this thread "works for"
  24. /// </summary>
  25. // 
  26. private Spider m_spider;
  27. /// <summary>
  28. /// The thread that is being used.
  29. /// </summary>
  30. private Thread m_thread;
  31. /// <summary>
  32. /// The thread number, used to identify this worker.
  33. /// </summary>
  34. // 线程编号,用来标识当前的工作线程
  35. private int m_number;
  36. /// <summary>
  37. /// The name for default documents.
  38. /// </summary>
  39. // 缺省文档的名字
  40. public const string IndexFile = "index.html";
  41. /// <summary>
  42. /// Constructor.
  43. /// </summary>
  44. /// <param name="spider">The spider that owns this worker.</param>
  45. // 构造函数,参数表示拥有当前工作线程的蜘蛛程序
  46. public DocumentWorker(Spider spider)
  47. {
  48. m_spider = spider;
  49. }
  50. /// <summary>
  51. /// This method will take a URI name, such ash /images/blank.gif
  52. /// and convert it into the name of a file for local storage.
  53. /// If the directory structure to hold this file does not exist, it
  54. /// will be created by this method.
  55. /// </summary>
  56. /// <param name="uri">The URI of the file about to be stored</param>
  57. /// <returns></returns>
  58. // 输入参数是一个URI名称,例如/images/blank.gif.
  59. // 把它转换成本地文件名称。如果尚未创建相应的目录
  60. // 结构,则创建之
  61. private string convertFilename(Uri uri)
  62. {
  63. string result = m_spider.OutputPath;
  64. int index1;
  65. int index2;
  66. // add ending slash if needed
  67. if( result[result.Length-1]!='\' )
  68. result = result+"\";
  69. // strip the query if needed
  70. String path = uri.PathAndQuery;
  71. int queryIndex = path.IndexOf("?");
  72. if( queryIndex!=-1 )
  73. path = path.Substring(0,queryIndex);
  74. // see if an ending / is missing from a directory only
  75. int lastSlash = path.LastIndexOf('/');
  76. int lastDot = path.LastIndexOf('.');
  77. if( path[path.Length-1]!='/' )
  78. {
  79. if(lastSlash>lastDot)
  80. path+="/"+IndexFile;
  81. }
  82. // determine actual filename
  83. lastSlash = path.LastIndexOf('/');
  84. string filename = "";
  85. if(lastSlash!=-1)
  86. {
  87. filename=path.Substring(1+lastSlash);
  88. path = path.Substring(0,1+lastSlash);
  89. if(filename.Equals("") )
  90. filename=IndexFile;
  91. }
  92. // 必要时创建目录结构
  93. index1 = 1;
  94. do
  95. {
  96. index2 = path.IndexOf('/',index1);
  97. if(index2!=-1)
  98. {
  99. String dirpart = path.Substring(index1,index2-index1);
  100. result+=dirpart;
  101. result+="\";
  102. Directory.CreateDirectory(result);
  103. index1 = index2+1;
  104. }
  105. } while(index2!=-1);
  106. // attach name
  107. result+=filename;
  108. return result;
  109. }
  110. /// <summary>
  111. /// Save a binary file to disk.
  112. /// </summary>
  113. /// <param name="response">The response used to save the file</param>
  114. // 将二进制文件保存到磁盘
  115. private void SaveBinaryFile(WebResponse response)
  116. {
  117. byte []buffer = new byte[1024];
  118. if( m_spider.OutputPath==null )
  119. return;
  120. string filename = convertFilename( response.ResponseUri );
  121. Stream outStream = File.Create( filename );
  122. Stream inStream = response.GetResponseStream();
  123. int l;
  124. do
  125. {
  126. l = inStream.Read(buffer,0,buffer.Length);
  127. if(l>0)
  128. outStream.Write(buffer,0,l);
  129. }
  130. while(l>0);
  131. outStream.Close();
  132. inStream.Close();
  133. }
  134. /// <summary>
  135. /// Save a text file.
  136. /// </summary>
  137. /// <param name="buffer">The text to save</param>
  138. // 保存文本文件
  139. private void SaveTextFile(string buffer)
  140. {
  141. if( m_spider.OutputPath==null )
  142. return;
  143. string filename = convertFilename( m_uri );
  144. StreamWriter outStream = new StreamWriter( filename );
  145. outStream.Write(buffer);
  146. outStream.Close();
  147. }
  148. /// <summary>
  149. /// Download a page
  150. /// </summary>
  151. /// <returns>The data downloaded from the page</returns>
  152. // 下载一个页面
  153. private string GetPage()
  154. {
  155. WebResponse response = null;
  156. Stream stream = null;
  157. StreamReader reader = null;
  158. try
  159. {
  160. HttpWebRequest request = (HttpWebRequest)WebRequest.Create(m_uri);
  161. response = request.GetResponse();
  162. stream = response.GetResponseStream();
  163. if( !response.ContentType.ToLower().StartsWith("text/") )
  164. {
  165. SaveBinaryFile(response);
  166. return null;
  167. }
  168. string buffer = "",line;
  169. reader = new StreamReader(stream);
  170. while( (line = reader.ReadLine())!=null )
  171. {
  172. buffer+=line+"rn";
  173. }
  174. SaveTextFile(buffer);
  175. return buffer;
  176. }
  177. catch(WebException e)
  178. {
  179. System.Console.WriteLine("下载失败,错误:" + e);
  180. return null;
  181. }
  182. catch(IOException e)
  183. {
  184. System.Console.WriteLine("下载失败,错误:" + e);
  185. return null;
  186. }
  187. finally
  188. {
  189. if( reader!=null ) reader.Close();
  190. if( stream!=null ) stream.Close();
  191. if( response!=null ) response.Close();
  192. }
  193. }
  194. /// <summary>
  195. /// Process each link encountered. The link will be recorded
  196. /// for later spidering if it is an http or https docuent, 
  197. /// has not been visited before(determined by spider class),
  198. /// and is in the same host as the original base URL.
  199. /// </summary>
  200. /// <param name="link">The URL to process</param>
  201. private void ProcessLink(string link)
  202. {
  203. Uri url;
  204. // fully expand this URL if it was a relative link
  205. try
  206. {
  207. url = new Uri(m_uri,link,false);
  208. }
  209. catch(UriFormatException e)
  210. {
  211. System.Console.WriteLine( "Invalid URI:" + link +" Error:" + e.Message);
  212. return;
  213. }
  214. if(!url.Scheme.ToLower().Equals("http") &&
  215. !url.Scheme.ToLower().Equals("https") )
  216. return;
  217. // comment out this line if you would like to spider
  218. // the whole Internet (yeah right, but it will try)
  219. if( !url.Host.ToLower().Equals( m_uri.Host.ToLower() ) )
  220. return;
  221. //System.Console.WriteLine( "Queue:"+url );
  222. m_spider.addURI( url );
  223. }
  224. /// <summary>
  225. /// Process a URL
  226. /// </summary>
  227. /// <param name="page">the URL to process</param>
  228. private void ProcessPage(string page)
  229. {
  230. ParseHTML parse = new ParseHTML();
  231. parse.Source = page;
  232. while(!parse.Eof())
  233. {
  234. char ch = parse.Parse();
  235. if(ch==0)
  236. {
  237. Attribute a = parse.GetTag()["HREF"];
  238. if( a!=null )
  239. ProcessLink(a.Value);
  240. a = parse.GetTag()["SRC"];
  241. if( a!=null )
  242. ProcessLink(a.Value);
  243. }
  244. }
  245. }
  246. /// <summary>
  247. /// This method is the main loop for the spider threads.
  248. /// This method will wait for URL's to become available, 
  249. /// and then process them. 
  250. /// </summary>
  251. public void Process()
  252. {
  253. while(!m_spider.Quit )
  254. {
  255. m_uri = m_spider.ObtainWork();
  256. m_spider.SpiderDone.WorkerBegin();
  257. System.Console.WriteLine("Download("+this.Number+"):"+m_uri);
  258. string page = GetPage();
  259. if(page!=null)
  260. ProcessPage(page);
  261. m_spider.SpiderDone.WorkerEnd();
  262. }
  263. }
  264. /// <summary>
  265. /// Start the thread.
  266. /// </summary>
  267. public void start()
  268. {
  269. ThreadStart ts = new ThreadStart( this.Process );
  270. m_thread = new Thread(ts);
  271. m_thread.Start();
  272. }
  273. /// <summary>
  274. /// The thread number. Used only to identify this thread.
  275. /// </summary>
  276. public int Number 
  277. {
  278. get
  279. {
  280. return m_number;
  281. }
  282. set
  283. {
  284. m_number = value;
  285. }
  286. }
  287. }
  288. }