//如果网页源码中有些内容是js渲染过来的,那你通过HttpClient直接取肯定取不到,但是这些数据一般都是通过异步请求传过来的(一般都是通过ajax的get或者post方式)。那么你可以通过火狐浏览器的firebug或者chrome的审查元素,在网络选项中找到这个请求地址,再用HttpClient请求一次就可以拿到你想要的数据,但这些数据可能不是网页源码,一般都是json字符串。
//朋友你好我现在是HtmlUnit运用者,我现在在项目爬去中遇见一个非常棘手的问题,就是分页数据怎样爬取,我现在真的不明白了,朋友可以的话加我QQ452276647 这个问题解决了非常非常的感谢了,
// java模拟浏览器包htmlunit,selenium
//发现一个很不错的模拟浏览器包htmlunit,它可以直接执行访问网站地址,并执行相应的JavaScript脚本;这个功能对于网站爬虫有很大的帮助,一些网站使用了ajax,如果使用简单的http访问只能抓到原始的html源码,但对于页面内执行的ajax却无法获取;使用这个包后,可以将执行ajax后的html源码一并抓取下来。
//网站地址:http://htmlunit.sourceforge.net/
//该站点下边还提到了几个相类似的包:HtmlUnit is used as the underlying "browser" by different Open Source tools like Canoo WebTest, JWebUnit, WebDriver, JSFUnit, Celerity, ...
//canoo WebTest 看了一下,没太明白是怎么用的,没想太深入了解
//jwebunit 是用来做网站测试用的,它整合了JUnit,htmlunit,selenium 包框架;其主要功能是用做白盒测试和压力测试。
//webDriver 后来改名为selenium,它整合了htmlunit,火狐浏览器,IE浏览器,opare浏览器驱动。如果使用htmlunitDriver,则是使用htmlunit包来访问站点;如果使用FirefoxDriver则会直接将Firefox浏览器调出来,然后在浏览器上模拟输入文字和其他鼠标键盘事件。
//htmlunit包访问网站后,获取到html源码后可以对源码进行修改;而jwebunit,selenium则暂时没有发现修改的功能,只是用来做模拟用户操作的功能。
AJAX 网页抓取部分采用调用浏览器 API 的方法,将浏览器虚拟
为一个代理服务器,并将浏览器服务绑定到电脑指定的端口上,通过和这个浏览器
通信并发送网页请求、连接、关闭等指令,实现 AJAX 网页的载入,并将最终载入
完成、重塑之后的网页内容进行保存,并进行后期处理。爬取后的网页将进行去噪、
网页正文抽取和重抓调度等操作。
I am trying to understand testing framework better and been looking into Selenium. I've used HTMLUnit before, mainly when I needed to scrape some information off website or the likes.
In the context of writing test automation, what's the advantage / disadvantages of Selenium vs HTMLUnit? Looks to me Selenium is more complicated to set up than HTMLUnit, although at the same time there's a HTMLUnitDriver for Selenium which I think behave the exact same way as in HTMLUnit itself?
Selenium obviously provides more robust framework, it has the Selenium RC for pararel testing, it also has different browser drivers that can be used - although when you used the browser drivers, the test will actually open/close a browser application rather than headless.
May be I am not understanding Selenium correctly. Some directions and pointers would be great!
On another note - a separate question - I am also looking at doing automated testing on mobile browser, I see that Selenium has an IPhoneDriver for it, but then this is not a headless testing either as it requires actual iOS simulator.
Speaking about parallel testing, it better to use selenium grid. Basic concept of selenium RC and selenium grid.
静态化还有一种思路就要采用HttpClient获取首页生成的html文件保存作为静态页,但是很不幸的,HttpClient无法执行js,所有使用ajax获取的数据都无法获取到,于是这种方式也行不通
最后使用HtmlUnit. HtmlUnit的优势就是可以支持大部分js,对JQuery的支持也不错.大体思路就是先使用HtmlUnit获取执行完js的html, 这个html 生成的样式和html格式会出现一些问题.如果能满足需求则可以直接使用或者略加改动后使用.
很多方案:selenium,phantomjs,casperjs,qtwebkit等。
一般采用的是casperjs。把每个ajax请求完成后保存网页,放入队列,这样的话后面的分析程序就只要分析html就好了。
casperjs这货和nodejs一起使用的时候时不时会有点小问题,如果不想麻烦,npm安装spookyjs,可以把casperjs作为node的模块来使用了。
当然,请求不复杂,无需验证的话,直接观察请求就可以了。
要想做的通用,只能通过浏览器上入手。windows上可以用程序调用IE的浏览器接口来获取页面document,还可以通过chrome或者firefox插件形式来获取要采集的信息后post到服务器上。linux下可以通过phantomjs操纵webit来获取document的内容。phantomjs有好多优点:不依赖X,可以工作在文本模式下,官网里叫做headless;可以抓取页面截图;能监视网络传输;可以禁用图片加载;自定义cookie,自定义头信息等。
使用HtmlUnit. HtmlUnit的优势就是可以支持大部分js,对JQuery的支持也不错.大体思路就是先使用HtmlUnit获取执行完js的html, 这个html 生成的样式和html格式会出现一些问题.如果能满足需求则可以直接使用或者略加改动后使用.
如果这个html不能满足需求,那么就把它当做 为静态数据来使用.这个时候我们先需要一个html模板文件,这个模板文件要保证首页的主题框架,然后从html按照各个div将数据填充进去.下面贴一些实现代码.
//创建一个可执行js,css,ajax的多功能WebClient WebClient multiWebClient = new WebClient(BrowserVersion.INTERNET_EXPLORER_8); multiWebClient.setJavaScriptEnabled(true);//执行JavaScript multiWebClient.setCssEnabled(true);//执行css multiWebClient.setAjaxController(new NicelyResynchronizingAjaxController());//设置ajax代理 //创建一个普通的WebClient WebClient commmonWebClient = new WebClient(BrowserVersion.INTERNET_EXPLORER_8); commmonWebClient.setJavaScriptEnabled(false); commmonWebClient.setCssEnabled(false); //用多功能Client获取动态页面的html并执行完js后的页面 URL dynamicUrl = new URL("http://localhost:30010/WebSite"); HtmlPage dynamicPage = (HtmlPage) multiWebClient.getPage(dynamicUrl); //根据项目需要,使用普通Client加载首页模板(避免执行模板里面的js,这些js都是真正要浏览器查看的时候才会执行) URL constantUrl = new URL("http://localhost:30010/WebSite/wwwroot/indexTemple.html"); HtmlPage htmlpage = (HtmlPage) commmonWebClient.getPage(constantUrl); HtmlElement body = htmlpage.getBody(); /** * 未详细测试的结论:getElementById一个元素只能取一次,取了之后再取就是空元素,其子也无法用getElementById取到 * 开始处理header */ appendChildren(body.getElementById("_static_nav"), dynamicPage.getElementById("_static_nav")); //开始处理_static_leftbox //处理图片滚动KSS_content appendChildren(body.getElementById("KinSlideshow"), dynamicPage.getElementById("KSS_content")); //处理最新电子书 appendChildren(body.getElementById("e_bookDiv"), dynamicPage.getElementById("e_bookDiv")); //取出content HtmlElement content = body.getElementById("content"); //开始处理_static_rightbox content.appendChild(dynamicPage.getElementById("_static_rightbox")); //添加div换行 DomElement clearDiv = htmlpage.createElement("div"); clearDiv.setAttribute("class", "clear"); //一个DomElement貌似只能使用一次 content.appendChild(clearDiv.cloneNode(true)); //开始处理_static_bookshow content.appendChild(dynamicPage.getElementById("_static_bookshow")); content.appendChild(clearDiv.cloneNode(true)); //开始处理_static_assistBox,secrecyRelevancediv,_static_optionBox content.appendChild(dynamicPage.getElementById("_static_assistBox")); content.appendChild(dynamicPage.getElementById("secrecyRelevancediv")); content.appendChild(dynamicPage.getElementById("_static_optionBox")); content.appendChild(clearDiv.cloneNode(true)); //开始处理_static_bookShowA content.appendChild(dynamicPage.getElementById("_static_bookShowA")); content.appendChild(clearDiv.cloneNode(true)); //开始处理_static_serve content.appendChild(dynamicPage.getElementById("_static_serve")); //开始处理footer body.appendChild(clearDiv.cloneNode(true)); body.appendChild(dynamicPage.getElementById("_static_footer")); //处理错误 String finalHtml =htmlpage.asXml().replace("<?xml version=\"1.0\" encoding=\"utf-8\"?>", "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">"); finalHtml = finalHtml.replaceAll("<a class=\"prev browse left\"/>", "<a class=\"prev browse left\" ></a>"); finalHtml = finalHtml.replaceAll("<a class=\"next browse right\"/>", "<a class=\"next browse right\" ></a>"); finalHtml = finalHtml.replaceAll("<b/>", "<b></b>"); finalHtml = finalHtml.replaceAll("scrollable_2", "scrollable"); stringToFile(finalHtml,"E:\\WayOfGlory\\WebSite\\WebContent\\wwwroot\\indexHome.html");
上面一段代码虽然不长,但实际上是个非常漫长的处理过程,从静态数据html中获取到的<div></div>经常会无法直接 使用,这时就需要不断的和浏览器(比如chrome)生成的真正静态页进行比对.虽然过程很漫长,但总比无法实现要强.
下面贴上刚刚使用了的两个小方法.
public DomElement appendChildren(DomElement target,DomElement source){ Iterator it = source.getChildElements().iterator(); while(it.hasNext()){ DomElement ele = (DomElement) it.next(); target.appendChild(ele); } return target; } public void stringToFile(String content,String path){ try { FileWriterWithEncoding fileWriter = new FileWriterWithEncoding(path,"utf-8"); fileWriter.write(content); fileWriter.flush(); fileWriter.close(); } catch (IOException e) { e.printStackTrace(); } }