目标:动态网页爬取
说明:这里的动态网页指几种可能:1)需要用户交互,如常见的登录操作;2)网页通过JS /
AJAX动态生成,如一个html里有<div id="test"></div>,通过JS生成<div
id="test"><span>aaa</span></div>。
这里用了WebCollector 2进行爬虫,这东东也方便,不过要支持动态关键还是要靠另外一个API -- selenium 2(集成htmlunit 和 phantomjs).
1)需要登录后的爬取,如新浪微博
- import java.util.Set;
-
- import cn.edu.hfut.dmic.webcollector.crawler.DeepCrawler;
- import cn.edu.hfut.dmic.webcollector.model.Links;
- import cn.edu.hfut.dmic.webcollector.model.Page;
- import cn.edu.hfut.dmic.webcollector.net.HttpRequesterImpl;
-
- import org.openqa.selenium.Cookie;
- import org.openqa.selenium.WebElement;
- import org.openqa.selenium.htmlunit.HtmlUnitDriver;
- import org.jsoup.nodes.Element;
- import org.jsoup.select.Elements;
-
- public class WebCollector1 extends DeepCrawler {
-
- public WebCollector1(String crawlPath) {
- super(crawlPath);
-
- try {
- String cookie=WebCollector1.WeiboCN.getSinaCookie("yourAccount", "yourPwd");
- HttpRequesterImpl myRequester=(HttpRequesterImpl) this.getHttpRequester();
- myRequester.setCookie(cookie);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- @Override
- public Links visitAndGetNextLinks(Page page) {
-
- Elements weibos=page.getDoc().select("div.c");
- for(Element weibo:weibos){
- System.out.println(weibo.text());
- }
-
- return null;
- }
-
- public static void main(String[] args) {
- WebCollector1 crawler=new WebCollector1("/home/hu/data/weibo");
- crawler.setThreads(3);
-
- for(int i=0;i<5;i++){
- crawler.addSeed("http://weibo.cn/zhouhongyi?vt=4&page="+i);
- }
- try {
- crawler.start(1);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- public static class WeiboCN {
-
-
- public static String getSinaCookie(String username, String password) throws Exception{
- StringBuilder sb = new StringBuilder();
- HtmlUnitDriver driver = new HtmlUnitDriver();
- driver.setJavascriptEnabled(true);
- driver.get("http://login.weibo.cn/login/");
-
- WebElement mobile = driver.findElementByCssSelector("input[name=mobile]");
- mobile.sendKeys(username);
- WebElement pass = driver.findElementByCssSelector("input[name^=password]");
- pass.sendKeys(password);
- WebElement rem = driver.findElementByCssSelector("input[name=remember]");
- rem.click();
- WebElement submit = driver.findElementByCssSelector("input[name=submit]");
- submit.click();
-
- Set<Cookie> cookieSet = driver.manage().getCookies();
- driver.close();
- for (Cookie cookie : cookieSet) {
- sb.append(cookie.getName()+"="+cookie.getValue()+";");
- }
- String result=sb.toString();
- if(result.contains("gsid_CTandWM")){
- return result;
- }else{
- throw new Exception("weibo login failed");
- }
- }
- }
-
- }
* 这里有个自定义路径/home/hu/data/weibo(WebCollector1 crawler=new WebCollector1("/home/hu/data/weibo");),是用来保存到嵌入式数据库Berkeley DB。
* 总体上来自Webcollector 作者的sample。
2)JS动态生成HTML元素的爬取
- import java.util.List;
-
- import org.openqa.selenium.By;
- import org.openqa.selenium.WebDriver;
- import org.openqa.selenium.WebElement;
-
- import cn.edu.hfut.dmic.webcollector.crawler.DeepCrawler;
- import cn.edu.hfut.dmic.webcollector.model.Links;
- import cn.edu.hfut.dmic.webcollector.model.Page;
-
- public class WebCollector3 extends DeepCrawler {
-
- public WebCollector3(String crawlPath) {
- super(crawlPath);
-
- }
-
- @Override
- public Links visitAndGetNextLinks(Page page) {
-
- WebDriver driver = PageUtils.getWebDriver(page);
- List<WebElement> divInfos=driver.findElements(By.cssSelector("#feed_content span"));
- for(WebElement divInfo:divInfos){
- System.out.println("Text是:" + divInfo.getText());
- }
- return null;
- }
-
- public static void main(String[] args) {
- WebCollector3 crawler=new WebCollector3("/home/hu/data/wb");
- for(int page=1;page<=5;page++)
- crawler.addSeed("http://cq.qq.com/baoliao/detail.htm?294064");
- try {
- crawler.start(1);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- }
PageUtils.java
- import java.io.BufferedReader;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.InputStreamReader;
-
- import org.openqa.selenium.JavascriptExecutor;
- import org.openqa.selenium.WebDriver;
- import org.openqa.selenium.chrome.ChromeDriver;
- import org.openqa.selenium.htmlunit.HtmlUnitDriver;
- import org.openqa.selenium.ie.InternetExplorerDriver;
- import org.openqa.selenium.phantomjs.PhantomJSDriver;
-
- import com.gargoylesoftware.htmlunit.BrowserVersion;
-
- import cn.edu.hfut.dmic.webcollector.model.Page;
-
- public class PageUtils {
- public static HtmlUnitDriver getDriver(Page page) {
- HtmlUnitDriver driver = new HtmlUnitDriver();
- driver.setJavascriptEnabled(true);
- driver.get(page.getUrl());
- return driver;
- }
-
- public static HtmlUnitDriver getDriver(Page page, BrowserVersion browserVersion) {
- HtmlUnitDriver driver = new HtmlUnitDriver(browserVersion);
- driver.setJavascriptEnabled(true);
- driver.get(page.getUrl());
- return driver;
- }
-
- public static WebDriver getWebDriver(Page page) {
-
-
- System.setProperty("phantomjs.binary.path", "D:\\Installs\\Develop\\crawling\\phantomjs-2.0.0-windows\\bin\\phantomjs.exe");
- WebDriver driver = new PhantomJSDriver();
- driver.get(page.getUrl());
-
- return driver;
- }
-
- public static String getPhantomJSDriver(Page page) {
- Runtime rt = Runtime.getRuntime();
- Process process = null;
- try {
- process = rt.exec("D:\\Installs\\Develop\\crawling\\phantomjs-2.0.0-windows\\bin\\phantomjs.exe " +
- "D:\\workspace\\crawlTest1\\src\\crawlTest1\\parser.js " +
- page.getUrl().trim());
- InputStream in = process.getInputStream();
- InputStreamReader reader = new InputStreamReader(
- in, "UTF-8");
- BufferedReader br = new BufferedReader(reader);
- StringBuffer sbf = new StringBuffer();
- String tmp = "";
- while((tmp = br.readLine())!=null){
- sbf.append(tmp);
- }
- return sbf.toString();
- } catch (IOException e) {
- e.printStackTrace();
- }
-
- return null;
- }
- }
2.1)HtmlUnitDriver getDriver是selenium 1.x的作法,已经outdate了,现在用WebDriver getWebDriver
2.2)这里用了几种方法:HtmlUnitDriver, ChromeDriver, PhantomJSDriver,
PhantomJS,参考 http://blog.csdn.net/five3/article/details/19085303,各自之间的优缺
点如下:
driver类型 |
优点 |
缺点 |
应用 |
真实浏览器driver |
真实模拟用户行为 |
效率、稳定性低 |
兼容性测试 |
HtmlUnit |
速度快 |
js引擎不是主流的浏览器支持的 |
包含少量js的页面测试 |
PhantomJS |
速度中等、模拟行为接近真实 |
不能模拟不同/特定浏览器的行为 |
非GUI的功能性测试 |
* 真实浏览器driver 包括 Firefox, Chrome, IE
2.3)用PhantomJSDriver的时候,遇上错
误:ClassNotFoundException: org.openqa.selenium.browserlaunchers.Proxies,原
因竟然是selenium 2.44 的bug,后来通过maven找到phantomjsdriver-1.2.1.jar 才解决了。
2.4)另外,我还试了PhantomJS 原生调用(也就是不用selenium,直接调用PhantomJS,见上面的方法),原生要调用JS,这里的parser.js代码如下:
- system = require(‘system‘)
- address = system.args[1];
- var page = require(‘webpage‘).create();
- var url = address;
- page.open(url, function (status) {
-
- if (status !== ‘success‘) {
- console.log(‘Unable to post!‘);
- } else {
-
- console.log(page.content);
- }
- phantom.exit();
- });
3)后话
3.1)HtmlUnitDriver + PhantomJSDriver是当前最可靠的动态抓取方案。
3.2)这过程中用到很多包、exe,遇到很多的墙~,有需要的朋友可以找我要。
Reference
http://www.ibm.com/developerworks/cn/web/1309_fengyq_seleniumvswebdriver/
http://blog.csdn.net/smilings/article/details/7395509
http://phantomjs.org/download.html
http://blog.csdn.net/five3/article/details/19085303
http://phantomjs.org/quick-start.html