转载自简书

说到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使用小结,虽然不是很全面,也不确保完全准确,算是对我这段学习历程的总结吧,希望对大家有用。

盘点selenium phantomJS使用的坑相关推荐

  1. [Python爬虫] 之二十二:Selenium +phantomjs 利用 pyquery抓取界面网站数据

    一.介绍 本例子用Selenium +phantomjs爬取界面(https://a.jiemian.com/index.php?m=search&a=index&type=news& ...

  2. [Python爬虫] 之三十:Selenium +phantomjs 利用 pyquery抓取栏目

    一.介绍 本例子用Selenium +phantomjs爬取栏目(http://tv.cctv.com/lm/)的信息 二.网站信息 三.数据抓取 首先抓取所有要抓取网页链接,共39页,保存到数据库里 ...

  3. Selenium+PhantomJS使用时报错原因及解决方案

    问题 今天在使用selenium+PhantomJS动态抓取网页时,出现如下报错信息: UserWarning: Selenium support for PhantomJS has been dep ...

  4. 使用selenium+phantomJS实现网页爬取

    有些网站反爬虫技术设计的非常好,很难采用WebClient等技术进行网页信息爬取,这时可以考虑采用selenium+phantomJS模拟浏览器(其实是真实的浏览器)的方式进行信息爬取.之前一直使用的 ...

  5. [Python爬虫] 之二十七:Selenium +phantomjs 利用 pyquery抓取今日头条视频

    一.介绍 本例子用Selenium +phantomjs爬取今天头条视频(http://www.tvhome.com/news/)的信息,输入给定关键字抓取图片信息. 给定关键字:视频:融合:电视 二 ...

  6. [Python爬虫] 之十八:Selenium +phantomjs 利用 pyquery抓取电视之家网数据

    一.介绍 本例子用Selenium +phantomjs爬取电视之家(http://www.tvhome.com/news/)的资讯信息,输入给定关键字抓取资讯信息. 给定关键字:数字:融合:电视 抓 ...

  7. Python+selenium+PhantomJS获取百度搜索结果真实链接地址

    祝愿所有参加高考的孩子们都能超水平发挥,考出好成绩,考上理想的学校!也希望你们考上大学之后仍然保持高考前的学习劲头! ===========正文============= 问题描述:在爬取百度搜索结果 ...

  8. selenium PhantomJS Fiddler使用

    1 通过Python调用浏览器解析页面,处理动态数据加载(AJAX.JavaScript) selenium 可以执行浏览器的平台,并且可以执行浏览器的各种动作事件 PhantomJS 就是一个无界面 ...

  9. selenium+phantomjs 爬虫 获取中国移动,电信,联通通话记录

    主要获取中国电信,中国移动,联通通话记录 提取征信维度,使用selenium+phantomjs完成对网站数据的爬取,由于phantomjs不符合 分布式爬取要求,所以自己动手重构了phantomjs ...

最新文章

  1. 查看器_三款完美替代Windows10自带照片查看器的神器!
  2. 7、Java并发性和多线程-如何创建并运行线程
  3. linux怎么复制手册,程序员的Linux上手手册(2) - 基础文件操作命令
  4. 重磅!百度研究院发布2021年十大科技趋势预测
  5. 小米5安卓使用微信X5 Blink内核调试
  6. go语言html css,html – 在Golang中加载图像和CSS
  7. DevOps平台中的自动化部署框架设计
  8. 算法笔记之——快速幂
  9. 基于Docker搭建Jumpserver堡垒机操作实践
  10. oracle查询不等于1000,解决oracle查询时 in 大于1000的办法
  11. java编写大数据分析模型_如何用Java(DOM分析器)编写XML文件
  12. 深度学习后向算法的直观理解_强化学习的直观介绍
  13. 介绍VMware虚拟化存储原理及数据恢复方法
  14. 乒乓球比赛赛程_2020乒乓球比赛赛程表
  15. 点亮了中行U盾12864屏线 12液晶,附12864详细手册
  16. 脸书AI首席开发布道师:如何贡献PyTorch代码
  17. [Android 4.4.4] Oppo Find7 MIUI 4.8.8 by crazyi
  18. 如何在Win10登录画面默认开启小键盘数字锁定?
  19. 1千6百多甲骨文汉字对应表ACCESS\EXCEL数据库
  20. MATLAB实现多元非线性回归

热门文章

  1. Javascript 面向对象编程(一)
  2. cron计划任务书写格式
  3. Echarts加载地图的三种方式
  4. linux软件包管理(dpkg、rpm)与软件源的由来(apt、yum、Yast)
  5. 太逗了,面试官让我讲线程 WAITING 状态!
  6. 记一次线上商城系统高并发的优化
  7. 树莓派上利用 Tensorflow 实现小车的自动驾驶
  8. 这 30 个常用的 Maven 命令你必须熟悉!
  9. Java 多线程三大核心
  10. ant design pro 页面加载原理及过程,@connect 装饰器