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


     

    目标:动态网页爬取

    说明:这里的动态网页指几种可能: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)需要登录后的爬取,如新浪微博

    [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。

    * 总体上来自Webcollector 作者的sample。



    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,各自之间的优缺 点如下:

    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代码如下:

    [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

  • 相关阅读:
    (转)Epoll模型详解
    (转)彻底学会使用epoll(一)——ET模式实现分析
    (转)Linux内核 TCP/IP、Socket参数调优
    Nodejs RESTFul架构实践之api篇(转)
    创业笔记-Node.js入门之阻塞与非阻塞
    创业笔记-Node.js入门之基于事件驱动的回调
    创业笔记-Node.js入门之一个完整的基于Node.js的web应用
    创业笔记-Node.js入门之JavaScript与Node.js
    在CentOS6.5下配置安装LVS
    zend studio 添加xdebug调试php代码
  • 原文地址:https://www.cnblogs.com/timssd/p/5429164.html
Copyright © 2020-2023  润新知