很多網頁的內容包括圖片是用JS或Jquery動態加載的,用Webbrowser直接獲得的源碼是沒有參考價值的,而JS加載後含所有element的代碼很難獲取的,起碼筆者搜索了一圈下來看到的方法都幾乎沒有使用價值。
筆者這裏分享一種解決方案,隨便找個網頁會動態加載內容的,不難發現需要加載的內容需要你滾動頁面,視野範圍內的內容就會動態加載,於是乎,辦法就來了。
首先,你還必須先研究一下你需要抓取的網站的內容構造,比如筆者需要抓取的網頁共有100張圖片,並且需要頁面滾動到圖片位置這些圖片纔會加載。於是先用普通瀏覽器等加載完右擊頁面選檢查,分析得到這些圖片的class裏面包含title,下面就可以根據這個來操作了。在webBrowser1_DocumentCompleted也就是加載完之後(注意並不是js也加載完),獲取當前頁面所有元素並存儲在docall當中,然後開啓Timer1。
private void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
currentEleIndex = 0;
docAll = webBrowser1.Document.All;
this.timer1.Enabled = true;
}
在Timer1裏面,根據上面的分析我們遍歷所有元素,找到class包含title的圖片元素並滾動到此元素,這時候js就會執行了,這裏有一句scrollcount++ % 5 == 0來控制並不對每個圖片都執行這個操作,因爲頁面圖片太多,每個都執行一邊沒必要,這裏設置每5個滾動一次,因爲窗口可是範圍一般都可以顯示大於2行。這裏有個要注意的,在頁面滾動的時候會不斷觸發webBrowser1_DocumentCompleted事件。docAll和docAll.Count即元素總數也會不斷改變。最後,在頁面不再刷新的時候就可以執行後面的工作了,也就是這裏 startprocess();所轉向的函數。
//webbrowser加載完後,要往下滾動來讓js加載所有項目及圖片
//timer1隔一段時間滾動一下
private void timer1_Tick(object sender, EventArgs e)
{
while (currentEleIndex < docAll.Count)
{
string s = docAll[currentEleIndex].GetAttribute("classname");
if (s != null && s.Contains("title"))
{
if (scrollcount++ % 5 == 0)//每行5個圖片
{
docAll[currentEleIndex].ScrollIntoView(true);
currentEleIndex++;
return;
}
}
currentEleIndex++;
}
if (currentEleIndex >= docAll.Count)
{
if (webBrowser1.Document.Body==null || !webBrowser1.Document.Body.InnerHtml.Contains("pagination"))
{
currentEleIndex = 0;
docAll = webBrowser1.Document.All;
}
else
{
this.timer1.Enabled = false;
this.textBox3.Text += "browsercompleted\n";
startprocess();
}
}
}
以上是滾動到指定元素,如果只是簡單的頁面滾動,可以更簡單
int nowheight = 0;
int stepnum = 10;
int scrollstep = webBrowser1.Document.Body.ScrollRectangle.Height/stepnum;
for (int i = 0; i < stepnum; i++)
{
nowheight += scrollstep;
webBrowser1.Document.Window.ScrollTo(0, nowheight);
System.Threading.Thread.Sleep(500);
}
另外值得一提的是,此時已經可以獲得js執行後的完整網頁源代碼,訪問webBrowser1.Document.Body.InnerHtml而不是別的,切記!
void startprocess()
{
savepictures();
//滾動到底部並保存完圖片,肯定加載完了,不需要timer對比源代碼是否有變化,直接開始處理了
oldDocText = webBrowser1.Document.Body.InnerHtml;
switch (state)
{
case 1: processPartsFirstPage(); break;
case 2: processPartsPages(); break;
}
}
然後就是怎麼把webbrowser裏面的圖片保存到本地了,也是網上搜索一圈也不好解決的,其實你大可以把源代碼裏用正則摳出來的圖片鏈接用代碼再下載一次,不過如果頻繁訪問的話會不會被網站封掉ip就難說了,而且webbrowser裏面都有了如果能直接保存不更好嗎,這裏涉及到電腦剪貼板的訪問,也就是說在程序運行當中,你就不能用電腦同時做一些編輯工作了,否則會因爲剪貼板出錯,因爲你做編輯工作的時候肯定會不小心的用到複製粘貼操作。最後要說一下,這個要添加2個引用,1個是框架裏的Microsoft.CSharp,1個是COM裏面的Microsoft HTML Object Library。好了,上代碼。
void savepictures()
{
string path = Application.StartupPath + "\\imgs\\front\\";
foreach (HtmlElement he in docAll)
{
if (he.GetAttribute("classname") != null && he.GetAttribute("classname").Contains("fill_img"))
{
string filename = he.GetAttribute("src");
int startpos = filename.LastIndexOf('/') + 1;
filename = filename.Substring(startpos, filename.Length - startpos);
if (File.Exists(path + filename)) continue;
HTMLDocument doc = (HTMLDocument)webBrowser1.Document.DomDocument;
HTMLBody body = (HTMLBody)doc.body;
IHTMLControlRange rang = (IHTMLControlRange)body.createControlRange();
IHTMLControlElement Img = (IHTMLControlElement)he.DomElement; //圖片地址
rang.add(Img);
rang.execCommand("Copy", false, null); //拷貝到內存
Image pic= Clipboard.GetImage();
pic.Save(path + filename);
System.Threading.Thread.Sleep(50);
}
}
}