原文:http://my.oschina.net/flashsword/blog/147334?p=1

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

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

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

在Java项目中使用Selenium,需要做两件事:

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

    <dependency>
    <groupId>org.seleniumhq.selenium</groupId>
    <artifactId>selenium-java</artifactId>
    <version>2.33.0</version>
    </dependency>
  • 下载对应的driver,以chrome为例:http://www.cnblogs.com/printN/p/7210633.html

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

    System.getProperties().setProperty("webdriver.chrome.driver","/Users/yihua/Downloads/chromedriver");

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

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

     /**
* 花瓣网抽取器。<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

使用Selenium来抓取动态加载的页面的更多相关文章

  1. Selenium来抓取动态加载的页面

    一般的爬虫都是直接使用http协议,下载指定url的html内容,并对内容进行分析和抽取.在我写的爬虫框架webmagic里也使用了HttpClient来完成这样的任务. 但是有些页面是通过js以及a ...

  2. Scrapy 框架 使用 selenium 爬取动态加载内容

    使用 selenium 爬取动态加载内容 开启中间件 DOWNLOADER_MIDDLEWARES = { 'wangyiPro.middlewares.WangyiproDownloaderMidd ...

  3. Python+Selenium爬取动态加载页面(2)

    注: 上一篇<Python+Selenium爬取动态加载页面(1)>讲了基本地如何获取动态页面的数据,这里再讲一个稍微复杂一点的数据获取全国水雨情网.数据的获取过程跟人手动获取过程类似,所 ...

  4. Python+Selenium爬取动态加载页面(1)

    注: 最近有一小任务,需要收集水质和水雨信息,找了两个网站:国家地表水水质自动监测实时数据发布系统和全国水雨情网.由于这两个网站的数据都是动态加载出来的,所以我用了Selenium来完成我的数据获取. ...

  5. scrapy和selenium结合抓取动态网页

    1.安装python (我用的是2.7版本的) 2.安装scrapy:   详情请参考 http://blog.csdn.net/wukaibo1986/article/details/8167590 ...

  6. 微信小程序web-view之动态加载html页面

    官方推出的web-view方便了很多开发人员. 我们在做的时候,经常会想到写一个小程序的page然后通过动态加载web-view的形式来完成其他功能页面的开发. 之前研究web-view的时候发现网上 ...

  7. 在aspx页动态加载ascx页面内容,给GridView控件绑定数据

    在aspx页动态加载ascx页面内容 //加载ascx页面内容Control c1 = this.Page.LoadControl("WebUserControl1.ascx"); ...

  8. 爬虫再探实战(三)———爬取动态加载页面——selenium

    自学python爬虫也快半年了,在目前看来,我面临着三个待解决的爬虫技术方面的问题:动态加载,多线程并发抓取,模拟登陆.目前正在不断学习相关知识.下面简单写一下用selenium处理动态加载页面相关的 ...

  9. 爬虫再探实战(四)———爬取动态加载页面——请求json

    还是上次的那个网站,就是它.现在尝试用另一种办法——直接请求json文件,来获取要抓取的信息. 第一步,检查元素,看图如下: 过滤出JS文件,并找出包含要抓取信息的js文件,之后就是构造request ...

随机推荐

  1. 【小白的CFD之旅】19 来自计算网格的困惑

    经过一年的忙碌,终于又到了寒假时间,小白又满状态复活了. 这一年小白学了很多的课程,但是一年下来,小白却感觉脑袋里没留下什么东西,貌似什么东西都在考完试的那一刹那全还回给老师了.这一年学习之余,小白仍 ...

  2. CCardSlip

    该类已经把tableview封装好,可以把它当做一个精灵来用,这样做的好处是,当一个界面同时需要多个tableview的时候就可以很好的解决这个问题,而且模块也更清晰. //------------- ...

  3. C++11 强枚举类型

    在标准C++11之前的枚举是继承C的,枚举类型不是类型安全的.枚举类型被视为整数,这使得两种不同的枚举类型之间可以进行比较. 一.C中enum类型的局限语法: enum type1{a, b, c}; ...

  4. 【Acm】八皇后问题

    八皇后问题,是一个古老而著名的问题,是回溯算法的典型例题. 其解决办法和我以前发过的[算法之美—Fire Net:www.cnblogs.com/lcw/p/3159414.html]类似 题目:在8 ...

  5. Redhat6.5安装DB2 Express-C版本

    Linux Redhat6.5安装DB2 Express-C版本: 创建相关用户和组 创建用户组: groupdel db2iadm1 groupadd -g 999 db2iadm1 groupad ...

  6. Android开发(四)——Android中的颜色

    Android开发中关于资源文件的存储操作.对于Android资源也是非常重要的,主要包括文本字符串(strings).颜色(colors).数组(arrays).动画(anim).布局(layout ...

  7. [Linux实用工具]Ubuntu环境下SSH的安装及使用

    SSH分为客户端和服务端. 服务端是一个守护进程,一般是sshd进程,在后台运行并响应来自客户端的请求.提供了对远程请求的处理,一般包括公共密钥认证.密钥交换.对称密钥加密和非安全连接. 客户端一般是 ...

  8. java泛型中<? super String>和<? extends String> 的区别

    (1)<? super String> is any class which is a superclass of String (including String itself). (I ...

  9. select 操作选中添加、删除操作Javascript

    //添加选中项 function addItem() { var myMember = document.getElementById("myMember"); var other ...

  10. 简单防范SYN_RECV攻击

    netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}' 这条语句返回结果如下 TIME_WAIT FIN_WAIT1 ...