使用Selenium来抓取动态加载的页面

通常的爬虫都是直接使用http协议,下载指定url的html内容,并对内容进行分析和抽取。在我写的爬虫框架webmagic里也使用了HttpClient来完成这样的任务。html

可是有些页面是经过js以及ajax动态加载的,例如:花瓣网。这时若是咱们直接分析原始页面的html,是得不到有效的信息的。固然,由于不管怎样动态加载,基础信息总归是包含在初始页面中得,因此咱们能够用爬虫代码来模拟js代码,js读取页面元素值,咱们也读取页面元素值;js发送ajax,咱们就拼凑参数、发送ajax并解析返回的json。这样总归是能作的,可是比较麻烦,有没有比较省力的方法呢?比较好的方法大概是内嵌一个浏览器了。java

Selenium是一个模拟浏览器,进行自动化测试的工具,它提供一组API能够与真实的浏览器内核交互。Selenium是跨语言的,有Java、C#、python等版本,而且支持多种浏览器,chrome、firefox以及IE都支持。python

在Java项目中使用Selenium,须要作两件事:git

  • 在项目中引入Selenium的Java模块,以Maven为例:github

    <dependency>
          <groupId>org.seleniumhq.selenium</groupId>
          <artifactId>selenium-java</artifactId>
          <version>2.33.0</version>
      </dependency>
  • 下载对应的driver,以chrome为例:http://code.google.com/p/chromedriver/downloads/listweb

    下载后,须要将driver的位置写到Java的环境变量里,例如我在mac下将其下载到了/Users/yihua/Downloads/chromedriver,则须要在程序里添加如下代码(固然在JVM参数里写-Dxxx=xxx也是能够的):ajax

    <!-- lang: java --> System.getProperties().setProperty("webdriver.chrome.driver","/Users/yihua/Downloads/chromedriver");chrome

Selenium的API挺简单的,核心是WebDriver,下面是动态渲染页面,并获取最终html的代码:json

<!-- lang: java -->
	 @Test
    public void testSelenium() {
        System.getProperties().setProperty("webdriver.chrome.driver", "/Users/yihua/Downloads/chromedriver");
        WebDriver webDriver = new ChromeDriver();
        webDriver.get("http://huaban.com/");
        WebElement webElement = webDriver.findElement(By.xpath("/html"));
        System.out.println(webElement.getAttribute("outerHTML"));
        webDriver.close();
    }

值得注意的是,每次new ChromeDriver(),Selenium都会创建一个Chrome进程,并使用一个随机端口在Java中与chrome进程进行通讯来交互。因而可知有两个问题:浏览器

  • 所以若是直接关闭Java程序,Chrome进程多是没法关闭的。这里须要显示的调用webDriver.close()来关闭进程。

  • 建立进程的开销仍是比较大的,尽可能对webDriver进行复用会比较好。惋惜根据官方的文档,webDriver不是线程安全的,因此咱们须要创建一个webDriver池来保存它们。不清楚Selenium是否有这样的接口,反正我是本身写了一个WebDriverPool来完成这个任务。

我已经将Selenium整合到了个人爬虫框架webmagic中,目前仍是试用版本,有兴趣的能够一块儿学习交流。

最后说说效率问题。嵌入浏览器以后,不但要多花CPU去渲染页面,还要下载页面附加的资源。彷佛单个webDriver中的静态资源是有缓存的,初始化以后,访问速度会加快。我试用ChromeDriver加载了100次花瓣的首页(http://huaban.com/),共耗时263秒,平均每一个页面2.6秒。

为了测试效果,我写了一个花瓣抽取器,抽取花瓣网的分享图片url,用了咱本身的webmagic框架,集成了Selenium。

<!-- lang: java -->
    /**
 * 花瓣网抽取器。<br>
 * 使用Selenium作页面动态渲染。<br>
 */
public class HuabanProcessor implements PageProcessor {

    private Site site;

    @Override
    public void process(Page page) {
        page.addTargetRequests(page.getHtml().links().regex("http://huaban\\.com/.*").all());
        if (page.getUrl().toString().contains("pins")) {
            page.putField("img", page.getHtml().xpath("//div[@id='pin_img']/img/@src").toString());
        } else {
            page.getResultItems().setSkip(true);
        }
    }

    @Override
    public Site getSite() {
        if (site == null) {
            site = Site.me().setDomain("huaban.com").addStartUrl("http://huaban.com/").setSleepTime(1000);
        }
        return site;
    }

    public static void main(String[] args) {
        Spider.create(new HuabanProcessor()).thread(5)
                .scheduler(new RedisScheduler("localhost"))
                .pipeline(new FilePipeline("/data/webmagic/test/"))
                .downloader(new SeleniumDownloader("/Users/yihua/Downloads/chromedriver"))
                .run();
    }
}

sample地址:HuabanProcessor.java

相关文章
相关标签/搜索