很多网页的内容包括图片是用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);
}
}
}
本文介绍如何使用C#的WebBrowser控件获取JS动态加载后的完整页面源码,并滚动页面使图片加载,然后保存图片到本地。通过分析网页结构,找到图片元素并滚动到其可视区域,避免频繁请求导致IP被封。利用剪贴板API实现图片的保存,需要注意剪贴板可能与其他应用冲突。该方法适用于处理大量图片加载的网页。
8356

被折叠的 条评论
为什么被折叠?



