动态网页爬取例子(WebCollector+selenium+phantomjs)

 

目标:动态网页爬取
javascript

说明:这里的动态网页指几种可能:1)须要用户交互,如常见的登陆操做;2)网页经过JS / AJAX动态生成,如一个html里有<div id="test"></div>,经过JS生成<div id="test"><span>aaa</span></div>。css

这里用了WebCollector 2进行爬虫,这东东也方便,不过要支持动态关键仍是要靠另一个API -- selenium 2(集成htmlunit 和 phantomjs).html


1)须要登陆后的爬取,如新浪微博
java

 

[java] view plain copy
 
  1. import java.util.Set;  
  2.   
  3. import cn.edu.hfut.dmic.webcollector.crawler.DeepCrawler;  
  4. import cn.edu.hfut.dmic.webcollector.model.Links;  
  5. import cn.edu.hfut.dmic.webcollector.model.Page;  
  6. import cn.edu.hfut.dmic.webcollector.net.HttpRequesterImpl;  
  7.   
  8. import org.openqa.selenium.Cookie;  
  9. import org.openqa.selenium.WebElement;  
  10. import org.openqa.selenium.htmlunit.HtmlUnitDriver;  
  11. import org.jsoup.nodes.Element;  
  12. import org.jsoup.select.Elements;  
  13.   
  14. /* 
  15.  * 登陆后爬取 
  16.  * Refer: http://nutcher.org/topics/33 
  17.  * https://github.com/CrawlScript/WebCollector/blob/master/README.zh-cn.md 
  18.  * Lib required: webcollector-2.07-bin, selenium-java-2.44.0 & its lib 
  19.  */  
  20. public class WebCollector1 extends DeepCrawler {  
  21.       
  22.     public WebCollector1(String crawlPath) {  
  23.         super(crawlPath);  
  24.         /*获取新浪微博的cookie,帐号密码以明文形式传输,请使用小号*/    
  25.         try {  
  26.             String cookie=WebCollector1.WeiboCN.getSinaCookie("yourAccount", "yourPwd");  
  27.             HttpRequesterImpl myRequester=(HttpRequesterImpl) this.getHttpRequester();  
  28.             myRequester.setCookie(cookie);  
  29.         } catch (Exception e) {  
  30.             e.printStackTrace();  
  31.         }  
  32.     }  
  33.       
  34.     @Override  
  35.     public Links visitAndGetNextLinks(Page page) {  
  36.         /*抽取微博*/  
  37.         Elements weibos=page.getDoc().select("div.c");  
  38.         for(Element weibo:weibos){  
  39.             System.out.println(weibo.text());  
  40.         }  
  41.         /*若是要爬取评论,这里能够抽取评论页面的URL,返回*/  
  42.         return null;  
  43.     }  
  44.       
  45.     public static void main(String[] args) {  
  46.         WebCollector1 crawler=new WebCollector1("/home/hu/data/weibo");  
  47.         crawler.setThreads(3);  
  48.         /*对某人微博前5页进行爬取*/  
  49.         for(int i=0;i<5;i++){  
  50.             crawler.addSeed("http://weibo.cn/zhouhongyi?vt=4&page="+i);  
  51.         }  
  52.         try {  
  53.             crawler.start(1);  
  54.         } catch (Exception e) {  
  55.             e.printStackTrace();  
  56.         }  
  57.     }  
  58.   
  59.     public static class WeiboCN {  
  60.   
  61.         /** 
  62.          * 获取新浪微博的cookie,这个方法针对weibo.cn有效,对weibo.com无效 
  63.          * weibo.cn以明文形式传输数据,请使用小号 
  64.          * @param username 新浪微博用户名 
  65.          * @param password 新浪微博密码 
  66.          * @return 
  67.          * @throws Exception  
  68.          */  
  69.         public static String getSinaCookie(String username, String password) throws Exception{  
  70.             StringBuilder sb = new StringBuilder();  
  71.             HtmlUnitDriver driver = new HtmlUnitDriver();  
  72.             driver.setJavascriptEnabled(true);  
  73.             driver.get("http://login.weibo.cn/login/");  
  74.   
  75.             WebElement mobile = driver.findElementByCssSelector("input[name=mobile]");  
  76.             mobile.sendKeys(username);  
  77.             WebElement pass = driver.findElementByCssSelector("input[name^=password]");  
  78.             pass.sendKeys(password);  
  79.             WebElement rem = driver.findElementByCssSelector("input[name=remember]");  
  80.             rem.click();  
  81.             WebElement submit = driver.findElementByCssSelector("input[name=submit]");  
  82.             submit.click();  
  83.   
  84.             Set<Cookie> cookieSet = driver.manage().getCookies();  
  85.             driver.close();  
  86.             for (Cookie cookie : cookieSet) {  
  87.                 sb.append(cookie.getName()+"="+cookie.getValue()+";");  
  88.             }  
  89.             String result=sb.toString();  
  90.             if(result.contains("gsid_CTandWM")){  
  91.                 return result;  
  92.             }else{  
  93.                 throw new Exception("weibo login failed");  
  94.             }  
  95.         }  
  96.     }  
  97.   
  98. }  

 

* 这里有个自定义路径/home/hu/data/weibo(WebCollector1 crawler=new WebCollector1("/home/hu/data/weibo");),是用来保存到嵌入式数据库Berkeley DB。node

* 整体上来自Webcollector 做者的sample。git



2)JS动态生成HTML元素的爬取

 

[java] view plain copy
 
  1. import java.util.List;  
  2.   
  3. import org.openqa.selenium.By;  
  4. import org.openqa.selenium.WebDriver;  
  5. import org.openqa.selenium.WebElement;  
  6.   
  7. import cn.edu.hfut.dmic.webcollector.crawler.DeepCrawler;  
  8. import cn.edu.hfut.dmic.webcollector.model.Links;  
  9. import cn.edu.hfut.dmic.webcollector.model.Page;  
  10.   
  11. /* 
  12.  * JS爬取 
  13.  * Refer: http://blog.csdn.net/smilings/article/details/7395509 
  14.  */  
  15. public class WebCollector3 extends DeepCrawler {  
  16.   
  17.     public WebCollector3(String crawlPath) {  
  18.         super(crawlPath);  
  19.         // TODO Auto-generated constructor stub  
  20.     }  
  21.   
  22.     @Override  
  23.     public Links visitAndGetNextLinks(Page page) {  
  24.         /*HtmlUnitDriver能够抽取JS生成的数据*/  
  25. //      HtmlUnitDriver driver=PageUtils.getDriver(page,BrowserVersion.CHROME);  
  26. //      String content = PageUtils.getPhantomJSDriver(page);  
  27.         WebDriver driver = PageUtils.getWebDriver(page);  
  28. //        List<WebElement> divInfos=driver.findElementsByCssSelector("#feed_content");  
  29.         List<WebElement> divInfos=driver.findElements(By.cssSelector("#feed_content span"));  
  30.         for(WebElement divInfo:divInfos){  
  31.             System.out.println("Text是:" + divInfo.getText());  
  32.         }  
  33.         return null;  
  34.     }  
  35.       
  36.     public static void main(String[] args) {  
  37.         WebCollector3 crawler=new WebCollector3("/home/hu/data/wb");  
  38.         for(int page=1;page<=5;page++)  
  39. //        crawler.addSeed("http://www.sogou.com/web?query="+URLEncoder.encode("编程")+"&page="+page);  
  40.         crawler.addSeed("http://cq.qq.com/baoliao/detail.htm?294064");  
  41.         try {  
  42.             crawler.start(1);  
  43.         } catch (Exception e) {  
  44.             e.printStackTrace();  
  45.         }  
  46.     }  
  47.   
  48. }  

PageUtils.java

 

 

[java] view plain copy
 
  1. import java.io.BufferedReader;  
  2. import java.io.IOException;  
  3. import java.io.InputStream;  
  4. import java.io.InputStreamReader;  
  5.   
  6. import org.openqa.selenium.JavascriptExecutor;  
  7. import org.openqa.selenium.WebDriver;  
  8. import org.openqa.selenium.chrome.ChromeDriver;  
  9. import org.openqa.selenium.htmlunit.HtmlUnitDriver;  
  10. import org.openqa.selenium.ie.InternetExplorerDriver;  
  11. import org.openqa.selenium.phantomjs.PhantomJSDriver;  
  12.   
  13. import com.gargoylesoftware.htmlunit.BrowserVersion;  
  14.   
  15. import cn.edu.hfut.dmic.webcollector.model.Page;  
  16.   
  17. public class PageUtils {  
  18.     public static HtmlUnitDriver getDriver(Page page) {  
  19.         HtmlUnitDriver driver = new HtmlUnitDriver();  
  20.         driver.setJavascriptEnabled(true);  
  21.         driver.get(page.getUrl());  
  22.         return driver;  
  23.     }  
  24.   
  25.     public static HtmlUnitDriver getDriver(Page page, BrowserVersion browserVersion) {  
  26.         HtmlUnitDriver driver = new HtmlUnitDriver(browserVersion);  
  27.         driver.setJavascriptEnabled(true);  
  28.         driver.get(page.getUrl());  
  29.         return driver;  
  30.     }  
  31.       
  32.     public static WebDriver getWebDriver(Page page) {  
  33. //      WebDriver driver = new HtmlUnitDriver(true);  
  34.           
  35. //      System.setProperty("webdriver.chrome.driver", "D:\\Installs\\Develop\\crawling\\chromedriver.exe");  
  36. //      WebDriver driver = new ChromeDriver();  
  37.           
  38.         System.setProperty("phantomjs.binary.path", "D:\\Installs\\Develop\\crawling\\phantomjs-2.0.0-windows\\bin\\phantomjs.exe");  
  39.         WebDriver driver = new PhantomJSDriver();  
  40.         driver.get(page.getUrl());  
  41.           
  42. //      JavascriptExecutor js = (JavascriptExecutor) driver;  
  43. //      js.executeScript("function(){}");  
  44.         return driver;  
  45.     }  
  46.       
  47.     public static String getPhantomJSDriver(Page page) {  
  48.         Runtime rt = Runtime.getRuntime();  
  49.         Process process = null;  
  50.         try {  
  51.             process = rt.exec("D:\\Installs\\Develop\\crawling\\phantomjs-2.0.0-windows\\bin\\phantomjs.exe " +   
  52.             "D:\\workspace\\crawlTest1\\src\\crawlTest1\\parser.js " +  
  53.             page.getUrl().trim());  
  54.             InputStream in = process.getInputStream();  
  55.             InputStreamReader reader = new InputStreamReader(  
  56.                     in, "UTF-8");  
  57.             BufferedReader br = new BufferedReader(reader);  
  58.             StringBuffer sbf = new StringBuffer();  
  59.             String tmp = "";  
  60.             while((tmp = br.readLine())!=null){      
  61.                 sbf.append(tmp);      
  62.             }  
  63.             return sbf.toString();  
  64.         } catch (IOException e) {  
  65.             e.printStackTrace();  
  66.         }  
  67.           
  68.         return null;  
  69.     }  
  70. }  

 


2.1)HtmlUnitDriver getDriver是selenium 1.x的做法,已经outdate了,如今用WebDriver getWebDriver

2.2)这里用了几种方法:HtmlUnitDriver, ChromeDriver, PhantomJSDriver, PhantomJS,参考 http://blog.csdn.net/five3/article/details/19085303,各自之间的优缺 点以下:github

 

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 才解决了。
web


2.4)另外,我还试了PhantomJS 原生调用(也就是不用selenium,直接调用PhantomJS,见上面的方法),原生要调用JS,这里的parser.js代码以下:chrome

 

[javascript] view plain copy
 
  1. system = require('system')     
  2. address = system.args[1];//得到命令行第二个参数 接下来会用到     
  3. //console.log('Loading a web page');     
  4. var page = require('webpage').create();     
  5. var url = address;     
  6. //console.log(url);     
  7. page.open(url, function (status) {     
  8.     //Page is loaded!     
  9.     if (status !== 'success') {     
  10.         console.log('Unable to post!');     
  11.     } else {      
  12.     //此处的打印,是将结果一流的形式output到java中,java经过InputStream能够获取该输出内容  
  13.         console.log(page.content);     
  14.     }        
  15.     phantom.exit();     
  16. });  

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