玩玩小爬虫——抓取动态页面

 

       在ajax横行的年代,很多网页的内容都是动态加载的,而我们的小爬虫抓取的仅仅是web服务器返回给我们的html,这其中就

跳过了js加载的部分,也就是说爬虫抓取的网页是残缺的,不完整的,下面可以看下博客园首页

从首页加载中我们看到,在页面呈现后,还会有5个ajax异步请求,在默认的情况下,爬虫是抓取不到这些ajax生成的内容的,

这时候要想获取就必须调用浏览器的内核引擎来下载这些动态页面,目前内核引擎三足鼎立。

Trident: 也就是IE内核,WebBrowser就是基于该内核,但是加载性内比较差。

Gecko: FF的内核,性能相对Trident较好。

WebKit: Safari和Chrome的内核,性能你懂的,在真实场景中还是以它为主。

好了,为了简单方便,这里使用WebBrowser来玩一把,使用WebBrowser我们要注意以下几点:

第一:因为WebBrowser在System.Windows.Forms 中,属于winform控件,所以我们要设置STAThread标记。

第二:winform是事件驱动的,而Console并不会去响事件,所有事件在windows的消息队列中等待执行,为了不让程序假死,

         我们需要调用DoEvents方法转让控制权,让操作系统执行其他的事件。

第三:WebBrowser中的内容,我们需要用DomDocument来查看,而不是DocumentText。

判断一个动态网页是否加载完毕,一般常会有两种方法:

①:设定一个最大值,因为每当异步加载一个js,都会触发一个Navigating和DocumentCompleted事件,所以我们需要在此

       处记录一下count值即可。

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Windows.Forms;
 6 using System.Threading;
 7 using System.IO;
 8 
 9 namespace ConsoleApplication2
10 {
11     public class Program
12     {
13         static int hitCount = 0;
14 
15         [STAThread]
16         static void Main(string[] args)
17         {
18             string url = "http://www.cnblogs.com";
19 
20             WebBrowser browser = new WebBrowser();
21 
22             browser.ScriptErrorsSuppressed = true;
23 
24             browser.Navigating += (sender, e) =>
25             {
26                 hitCount++;
27             };
28 
29             browser.DocumentCompleted += (sender, e) =>
30             {
31                 hitCount++;
32             };
33 
34             browser.Navigate(url);
35 
36             while (browser.ReadyState != WebBrowserReadyState.Complete)
37             {
38                 Application.DoEvents();
39             }
40 
41             while (hitCount < 16)
42                 Application.DoEvents();
43 
44             var htmldocument = (mshtml.HTMLDocument)browser.Document.DomDocument;
45 
46             string gethtml = htmldocument.documentElement.outerHTML;
47 
48             //写入文件
49             using (StreamWriter sw = new StreamWriter(Environment.CurrentDirectory + "//1.html"))
50             {
51                 sw.WriteLine(gethtml);
52             }
53 
54             Console.WriteLine("html 文件 已经生成!");
55 
56             Console.Read();
57         }
58     }
59 }

然后,我们打开生成好的1.html,看看js加载的内容是不是有了。

②: 当然除了通过判断最大值确定是否已经加载完成,我们还可以通过设定一个Timer来判断,比如3s,4s,5s后来查看

      WEBbrowser 是否加载完毕。

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Windows.Forms;
 6 using System.Threading;
 7 using System.IO;
 8 
 9 namespace ConsoleApplication2
10 {
11     public class Program
12     {
13         [STAThread]
14         static void Main(string[] args)
15         {
16             string url = "http://www.cnblogs.com";
17 
18             WebBrowser browser = new WebBrowser();
19 
20             browser.ScriptErrorsSuppressed = true;
21 
22             browser.Navigate(url);
23 
24             //先要等待加载完毕
25             while (browser.ReadyState != WebBrowserReadyState.Complete)
26             {
27                 Application.DoEvents();
28             }
29 
30             System.Timers.Timer timer = new System.Timers.Timer();
31 
32             var isComplete = false;
33 
34             timer.Elapsed += new System.Timers.ElapsedEventHandler((sender, e) =>
35             {
36                 //加载完毕
37                 isComplete = true;
38 
39                 timer.Stop();
40             });
41 
42             timer.Interval = 1000 * 5;
43 
44             timer.Start();
45 
46             //继续等待 5s,等待js加载完
47             while (!isComplete)
48                 Application.DoEvents();
49 
50             var htmldocument = (mshtml.HTMLDocument)browser.Document.DomDocument;
51 
52             string gethtml = htmldocument.documentElement.outerHTML;
53 
54             //写入文件
55             using (StreamWriter sw = new StreamWriter(Environment.CurrentDirectory + "//1.html"))
56             {
57                 sw.WriteLine(gethtml);
58             }
59 
60             Console.WriteLine("html 文件 已经生成!");
61 
62             Console.Read();
63         }
64     }
65 }

当然,效果依旧,就不截图了,从上面的两种写法来看,我们的WebBrowser都是放在主线程中,下面我们来看看如何放在工作线程上,

很简单,只要将该工作线程设定为STA模式即可。

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Windows.Forms;
 6 using System.Threading;
 7 
 8 namespace ConsoleApplication2
 9 {
10     public class Program
11     {
12         static int hitCount = 0;
13 
14         //[STAThread]
15         static void Main(string[] args)
16         {
17             Thread thread = new Thread(new ThreadStart(() =>
18             {
19                 Init();
20                 System.Windows.Forms.Application.Run();
21             }));
22 
23             //将该工作线程设定为STA模式
24             thread.SetApartmentState(ApartmentState.STA);
25 
26             thread.Start();
27 
28             Console.Read();
29         }
30 
31         static void Init()
32         {
33             string url = "http://www.cnblogs.com";
34 
35             WebBrowser browser = new WebBrowser();
36 
37             browser.ScriptErrorsSuppressed = true;
38 
39             browser.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(browser_DocumentCompleted);
40 
41             browser.Navigating += new WebBrowserNavigatingEventHandler(browser_Navigating);
42 
43             browser.Navigate(url);
44 
45             while (browser.ReadyState != WebBrowserReadyState.Complete)
46             {
47                 Application.DoEvents();
48             }
49 
50             while (hitCount < 16)
51                 Application.DoEvents();
52 
53             var htmldocument = (mshtml.HTMLDocument)browser.Document.DomDocument;
54 
55             string gethtml = htmldocument.documentElement.outerHTML;
56 
57             Console.WriteLine(gethtml);
58         }
59 
60         static void browser_Navigating(object sender, WebBrowserNavigatingEventArgs e)
61         {
62             hitCount++;
63         }
64 
65         static void browser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
66         {
67             hitCount++;
68         }
69     }
70 }
posted @ 2012-11-06 00:00  一线码农  阅读(36661)  评论(23编辑  收藏  举报