盘点selenium phantomJS使用的坑

说到python爬虫,刚开始主要用urllib库,虽然接口比较繁琐,但也能实现基本功能。等见识了requests库的威力后,便放弃urllib库,并且也不打算回去了。但对一些动态加载的网站,经常要先分析请求,再用requests模拟,比较麻烦。直到遇到了selenium库,才发现爬动态网页也可以这么简单,果断入坑!

selenium是python的一个第三方自动化测试库,虽然是测试库,却也非常适合用来写爬虫,而phantomJS是其子包webdriver下面的一个浏览器。phantomJS本身是一个无头浏览器(headless browser),也称无界面浏览器。可以在通过官网下载运行phantomjs.exe,简单几行代码也能访问网页,爬取数据。但本文主要讨论通过python的selenium库使用phantomJS。除了phantomJS浏览器,webdriver还整合了Chrome、Firefox、IE等浏览器,并提供了操作这些浏览器的接口。

由于phantomJS是无界面浏览器,不需要界面的同时占用的内存也相对较小,更适用于大规模多进程爬数据(试想,如果开几十个Chrome进程爬数据,那真是内存噩梦!)。本文主要讨论使用selenium phantomJS过程中遇到的bug,而不是selenium phantomJS使用教程,有需要了解selenium基本用法的同学,请移步官方文档

个人用phantomJS爬数据有一段时间了,爬虫程序也大致完工了,过程中遇到了很多坑,统一总结如下。

1. 查看phantomJS文档

前面提到,phantomJS是selenium子包webdriver下面多个浏览器中的一个,而selenium包对不同的浏览器都提供了统一的接口,所以直接查看selenium的官方文档即可,也有对应的中文文档。文档内容不多,但很全面。遇到不懂的问题,先看文档肯定没错。

这里需要注意的是,百度搜索phantomJS得到的结果只是phantomJS的官方文档,而phantomJS是一个独立的无界面浏览器,也称JS模拟器,本来就独立于python。我们需要的是phantomJS的python接口,也就是通过python调用phantomJS,所以只需查看selenium的webdriver文档。

当然,官方文档很全面,但也相对繁杂。python有个查看文档的小技巧,直接使用help()就能查看某个对象的帮助文档。比如help(driver)即可直接查看driver这个对象的文档,包括其内部函数、变量的说明。如果driver是一个phantomJS对象,那么会显示phantomJS浏览器对象的函数和变量的文档,具体内容和官方文档一样。对所有的python对象都可以这样干,非常便捷。似乎现在各种IDE也有这个功能:当鼠标悬停在某个对象上,就显示该对象的帮助文档。不过多掌握个方法总归没错。

2. phantomJS的配置问题

selenium官方文档中,phantomJS对象的帮助文档很详细,但当涉及phantomJS浏览器的配置,比如user-agent伪装、代理、超时返回等选项时,有用的信息就非常少了。结合网上的资料和自己遇到的各种坑,我总结了常用的phantomJS配置选项,对普通的爬虫来说,应该够用了。

from selenium import webdriver
# 引入配置对象DesiredCapabilities
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
dcap = dict(DesiredCapabilities.PHANTOMJS)
#从USER_AGENTS列表中随机选一个浏览器头,伪装浏览器
dcap["phantomjs.page.settings.userAgent"] = (random.choice(USER_AGENTS))
# 不载入图片,爬页面速度会快很多
dcap["phantomjs.page.settings.loadImages"] = False
# 设置代理
service_args = [‘--proxy=127.0.0.1:9999‘,‘--proxy-type=socks5‘]
#打开带配置信息的phantomJS浏览器
driver = webdriver.PhantomJS(phantomjs_driver_path, desired_capabilities=dcap,service_args=service_args)
# 隐式等待5秒,可以自己调节
driver.implicitly_wait(5)
# 设置10秒页面超时返回,类似于requests.get()的timeout选项,driver.get()没有timeout选项
# 以前遇到过driver.get(url)一直不返回,但也不报错的问题,这时程序会卡住,设置超时选项能解决这个问题。
driver.set_page_load_timeout(10)
# 设置10秒脚本超时时间
driver.set_script_timeout(10)

3. phantomJS的并发问题

phantomJS爬数据比较慢,并发编程几乎是必选项。最初,我考虑采用多线程/协程的方式,毕竟对于这种IO密集型的程序,多线程/协程比较合适。但多次测试下来,程序却遇到各种问题,有时能成功运行,有时却不能。尝试将phantomJS改成Chrome,程序居然能正常运行,这基本确定是phantomJS的锅了。所以,如果需要并发编程提高效率,用Chrome比较好,虽然内存占用相对较多,况且经下面简友提醒,在没界面的主机上也可以跑Chrome,那自然更好了。

在网上仔细查找了相关资料(这玩意的中文资料极少,只能去国外技术论坛潜水),原来phantomJS本身在多线程方面还有很多bug,建议使用多进程,具体什么原因有时间再去了解。

关于多进程,推荐使用multiprocessing库,简洁、高效!下面几行代码便实现了多进程并发。

from multiprocessing import Pool
pool = Pool(8)
data_list = pool.map(get, url_list)
pool.close()
pool.join()

4. phantomJS进程不自动退出问题

话说,一开始我写好程序后,先在本地测试了一段时间,确认程序各方面都没问题后,直接扔阿里云主机上跑了。过了一段时间,查了下程序运行日志,很好,一切如常。于是我就高高兴兴地摸鱼去了。

第二天准备登录主机验收程序时,却发现居然无法登录!啥,无法登录?难不成这个小爬虫程序还能把主机搞崩?我先在阿里云后台查看了主机的运行日志,发现主机的内存使用越来越高,应该是内存耗尽后,强制关机了。似乎是程序没有回收内存,导致占用的内存越来越大。明确大致原因后,就是痛苦的查bug过程了。

由于bug涉及内存的使用,我自然地想到了用top命令查看进程的内存使用情况。先运行程序,然后运行top命令,实时检测程序的内存使用情况。一开始程序占用内存在正常范围,只有一个phantomJS进程在运行,似乎没有什么不对。但随着时间的增长,内存中居然同时有好几个phantomJS进程在运行,内存所剩空间越来越小!但根据程序的逻辑,任何时候都只有一个phantomJS进程在爬数据。我意识到可能是由于phantomJS进程没有正常关闭,所以在内存中驻留的phantomJS进程越来越多,最终吃光了内存。

带着这个问题,我重新检查了一次代码,尤其在程序异常退出的地方。最终找到了类似下面的代码:

try:
    self.driver.get(url)
    self.wait_()
    return True
except Exception as e:
    return False

程序的逻辑是:如果在打开url的过程中报错,那么就返回False,反之返回True。

似乎直接return False的处理太粗心了。我尝试着在return False前加上一行self.driver.quit()。再次运行程序,并用top查看内存使用情况,发现程序的内存使用一直都在正常范围内,并没有出现多个phantomJS进程的情况,问题搞定!后面在网上找到的资料也证实了我的猜想:主程序退出后,selenium不保证phantomJS也成功退出,最好手动关闭phantomJS进程。

5. 其他问题

5.1 不同frame间的转换

有时,phantomJS获得的页面源码的确存在某元素,但通过find_element_by_xpath()等定位函数却无法获得该元素对象,总是提示“元素不存在”的错误。遇到这种情况,除了检查元素节点路径是否正确外,还应该分析页面源码,检查元素是否被包裹在一个特定的frame中,如果是后者,那么在使用查找函数前,需要额外的处理。

比如网页源码中有如下代码:

<iframe id="topmenuFrame" width="100%" scrolling="no" height="100%" src="topmenu.aspx?>
<div id="haha">text</div>
</iframe>

假如你想要获取id="haha"的div标签,直接通过driver.find_element_by_id(‘haha‘)就会提示“元素不存在“的错误。

这时需要使用driver.switch_to_frame(driver.find_element_by_id("topmenuFrame")),即先进入id为topmenuFrame的frame,然后再执行driver.find_element_by_id("haha"),就能正确获得该元素了。

需要注意的是,切换到这个frame之后,只能访问当前frame的内容,如果想要回到默认的内容范围,相当于默认的frame,还需要使用driver.switch_to_default_content()

页面中有多个frame时,要注意frame之间的切换。

5.2 implicit_wait、WebDriverWait不一定靠谱

宿舍哥们用phantomJS爬数据时,遇到了一个匪夷所思的bug。起初,他写了个很简单的程序,从个方面来看都没问题,但实际运行却提示各种错误,让人十分费解。折腾大半天之后,他直接注释掉自己不太熟悉的implicit_wait(),改用time.sleep()作延时,程序居然就能正确运行了!原来implicit_wait()有bug。同样的,对于WebDriverWait,大家使用时也要特别注意。

看来python的selenium库不是很成熟,还存在一些问题,一些函数的实际运行情况并不是预期的那样,在查bug时,要留意这些问题。

6. 总结

总的来说,selenium库简单,容易上手,是爬动态网页的杀手级武器,但对phantomJS浏览器的支持还不是特别完善。当然,了解存在的问题,并找到对应的解决方法,就能发挥phantomJS的威力了。

以上就是我个人这段时间的phantomJS使用小结,虽然不是很全面,也不确保完全准确,算是对我这段学习历程的总结吧,希望对大家有用。

时间: 2024-10-06 01:08:06

盘点selenium phantomJS使用的坑的相关文章

C#项目中使用 Selenium + PhantomJS 模拟登录博客园

Selenium selenium 是一个web的自动化测试工具,不少学习功能自动化的同学开始首选selenium PhantomJS PhantomJS是一个基于webkit的javascript API.它使用QtWebKit作为它核心浏览器的功能,使用webkit来编译解释执行JavaScript代码.任何你可以在基于webkit浏览器做的事情,它都能做到.它不仅是个隐形的浏览器,提供了诸如CSS选择器.支持Web标准.DOM操作.JSON.html5.Canvas.SVG等,同时也提供了

selenium phantomjs java无界面浏览器环境搭建

java selenium搭建无界面浏览器 1.http://phantomjs.org/ 下载windows版phantomjs 2.解压后bin目录下会有exe文件 3.测试代码: package se; import org.openqa.selenium.WebDriver; import org.openqa.selenium.firefox.FirefoxDriver; import org.openqa.selenium.phantomjs.PhantomJSDriver; pub

数据抓取的艺术(一):Selenium+Phantomjs数据抓取环境配置

数据抓取的艺术(一):Selenium+Phantomjs数据抓取环境配置 2013-05-15 15:08:14 分类: Python/Ruby 数据抓取是一门艺术,和其他软件不同,世界上不存在完美的.一致的.通用的抓取工具.为了不同的目的,需要定制不同的代码.不过,我们不必Start from Scratch,已经有许多的基本工具.基本方法和基础框架可供使用.不同的工具.不同的方法.不同的框架的特点也不同.了解这些工具.方法和框架是首要任务,接下来就需要明白它们的差异都在哪里.什么情境该用什

[Python爬虫] 之九:Selenium +phantomjs抓取活动行中会议活动(单线程抓取)

思路是这样的,给一系列关键字:互联网电视:智能电视:数字:影音:家庭娱乐:节目:视听:版权:数据等.在活动行网站搜索页(http://www.huodongxing.com/search?city=%E5%85%A8%E5%9B%BD&pi=1)的文本输入框中分别输入每个关键字,在搜索结果中抓取需要的数据. 首先通过Selenium+IE驱动得到每个关键字搜索结果的url(首页,因为以后各个页的url就是索引不一样)和总页数,保存的列表里面.然后再循环列表,用Selenium +phantomj

Selenium+PhantomJS使用初体验

抓取使用Ajax技术完成的网页内容时可以使用Selenium+PhantomJS技术 1.pip install selenium 2.下载Phantomjs不需要用pip 武汉科技大学首页有一块使用js异步加载的网页内容,如图 抓取这一块内容的思路是:判断这一块是否加载完毕:selenium抓取 在判断加载完毕这一步可以判断是否有'校企合作'出现 (ps:其实合理的做法是找异步内容里面的某个最后加载出来元素,但是这个例子里面元素没有多余的特征供选择了) 1 #coding:utf-8 2 fr

[Python爬虫] 之十五:Selenium +phantomjs根据微信公众号抓取微信文章

借助搜索微信搜索引擎进行抓取 抓取过程 1.首先在搜狗的微信搜索页面测试一下,这样能够让我们的思路更加清晰 在搜索引擎上使用微信公众号英文名进行“搜公众号”操作(因为公众号英文名是公众号唯一的,而中文名可能会有重复,同时公众号名字一定要完全正确,不然可能搜到很多东西,这样我们可以减少数据的筛选工作, 只要找到这个唯一英文名对应的那条数据即可),即发送请求到'http://weixin.sogou.com/weixin?type=1&query=%s&ie=utf8&_sug_=n&

C#使用Selenium+PhantomJS抓取数据

手头项目需要抓取一个用js渲染出来的网站中的数据.使用常用的httpclient抓回来的页面是没有数据.上网百度了一下,大家推荐的方案是使用PhantomJS.PhantomJS是一个没有界面的webkit浏览器,能够和浏览器效果一致的使用js渲染页面.Selenium是一个web测试框架.使用Selenium来操作PhantomJS绝配.但是网上的例子多是Python的.无奈,下载了python按照教程搞了一下,卡在了Selenium的导入问题上.遂放弃,还是用自己惯用的c#吧,就不信c#上没

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

目标:动态网页爬取 说明:这里的动态网页指几种可能:1)需要用户交互,如常见的登录操作:2)网页通过JS / AJAX动态生成,如一个html里有<div id="test"></div>,通过JS生成<div id="test"><span>aaa</span></div>. 这里用了WebCollector 2进行爬虫,这东东也方便,不过要支持动态关键还是要靠另外一个API -- selen

python+selenium自动化软件测试(第6章):selenium phantomjs页面解析使用

我们都知道Selenium是一个Web的自动化测试工具,可以在多平台下操作多种浏览器进行各种动作,比如运行浏览器,访问页面,点击按钮,提交表单,浏览器窗口调整,鼠标右键和拖放动作,下拉框和对话框处理等,我们抓取时选用它,主要是Selenium可以渲染页面,运行页面中的JS,以及其点击按钮,提交表单等操作. from selenium import webdriver driver = webdriver.PhantomJS() driver.get("http://www.xxxxxx.com&