在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 }