前言
不得不说,selenium是1个很不错的Web自动化工具,提供了多种语言的支持,其中包括Python、Ruby、Java、Nodejs等。但不得不说,随着对该工具使用的越来越多,就会发现它存在的一些不足甚至有待改进的地方。
而对该工具的使用,还得追溯到几年前信息采集的1个项目,当时是赚点人品学习了一下,并没有太深入的使用。而这次,为了项目的1个需求,深入的挖掘并进行总结,希望给后来的人抛砖引玉,减少走弯路。
适应人群
这篇文章写给有志于从事如下岗位工作的人群:
- Web自动化测试
- 信息安全开发
- 爬虫工程师
- 打算找份Python工作
说完了题外话,下面我们还是讲讲实际的操作。这里以Python为例进行说明。
找不到浏览器执行文件
在selenium高版本中,比如3.141中,即使你不指定浏览器的位置,selenium也是可以找到其位置,这是让自己觉得神奇的地方。当然,为了保险起见,还是把对应的浏览器可执行文件及相关的驱动放在环境变量PATH目录下吧。
如果实在找不到浏览器可执行文件,那么可以通过下面的方式进行指定,以火狐为例:
from selenium.webdriver.firefox.firefox_binary import FirefoxBinary binary = FirefoxBinary("E:/Program Files/Mozilla Firefox/firefox.exe") browser = webdriver.Firefox(firefox_binary=binary)
我们需要导入FirefoxBinary类,然后对其进行实例化,传入的参数是你本地系统上安装的绝对路径,最后将其作为 firefox_binary
参数传递给Firefox构造器。
浏览器无任何反应
如果上面的找不到浏览器执行文件是个小坑的话,那么这里的浏览器无任何响应可以说就是个隐形的陷阱。你会神奇的发现,当你把selenium、浏览器及对应驱动,如chromedriver、geckodriver都安装了,但是当浏览器执行到这一行:
browser = webdriver.Firefox() browser.get("https://www.baidu.com")
结果等了10秒,浏览器还是空白的一片。正常不是应该访问百度的首页吗?怎么不跳转呢,然后什么异常或者提示都没有。
当你遇到这样的情况,那么你就要静下心来先想一下,你当前的浏览器版本是多少?浏览器的驱动是多少?还有你的selenium库的版本是多少?
自己遇到1个问题就是,公司PC上安装的上述版本如下:
selenium = 2.53
Firefox = 56.0
geckodriver = 0.19
按照常理,它是可以正常运行的,但是结果就是大半天都没响应。实际上,上述的版本中火狐版本56.0与selenium的版本是不兼容的,必须升级selenium库。
你需要记住的是,selenium的版本2.53最多只能支持到版本54即可,而geckodriver版本0.19是支持火狐55以上的。
访问的URL的请求获取
解决完或者逃过了上述2个问题后,对于业务需要获取访问的URL地址的请求的问题,可以说还是有难度的。但是,还是有解决方案的。
对于Python3来说,真的很简单。直接pip安装selenium-wire就好了,该库要求Python版本大于3.4,这完全可以说就不是事情。而很不幸,公司的项目采用的是Python2.7开发的,对于这样的问题,实际上有多种解决方案,这里简单的说下通过代理的方式。
最开始,公司方面使用的是proxy2这个库来获取其请求,其实际上通过Python标准库中的BaseHTTPServer和ThreadMixin来实现1个简易的代理。但是后来在实践中发现,该库存在3个不足的地方:
- 会出现偶尔获取不了HTTPS请求包
- HTTPS包即使能获取到但是很容易出现异常
- 并发情况下直接请求很明显就是不对的
然后又选择了1个使用Tornado的toproxy的库,然而该库对于HTTPS的支持还是存在问题,而且要与项目集成也不容易。
最后,选择了1个Java编写的代理browsermob-proxy。对于该库,需要说的是,在pip安装时包的名称是 browsermob-proxy
而不是 browsermobproxy
,否则你会发现与网上的教程根本不是同一个事情。
代理竟然没有效果
选择好代理后,接下来就应该为浏览器设置代理了,不然是没有办法截取到HTTP请求信息的。然而,对于Chrome浏览器而言,常用的如下方式是完全无效的:
option = ChromeOptions() option.add_argument("--proxy-server=localhost:8080")
这是网上99%教程的写法,然而对于公司PC上版本只有60的Chrome浏览器而言,基本无视。
而如果将上述代码修改为如下:
proxy = Proxy({ "httpProxy":"localhost:8080", "sslProxy":"localhost:8080" })
那么这种方式,就可以正常的获取到对应的URL请求的信息。
HTTPS证书不可用
好不容易把浏览器代理给设置好了,接着可以心想总算可以放心获取请求的信息了。结果对于HTTPS的站点页面,那么浏览器弹出当前站点不可信,浏览器拒绝连接。要么,获取到的信息是请求CA证书站点的。
对于浏览器弹出当前站点不可信的问题,可以直接让浏览器不验证证书的安全性。而对于获取到请求CA证书站点信息的问题,对于火狐浏览器而言,需要手动创建1个Profile,可以在关闭浏览器的情况下,重要的话要重复3遍。
在浏览器关闭的情况下,运行下面的命令:
firefox.exe -p
此时弹出如下的页面:
然后创建1个配置文件,这里假设为xxx,并在创建浏览器的时候进行指定:
profile = webdriver.FireforxProfile("xxx") browser = webdriver.Firefox(firefox_profile=profile)
节点遍历时的坑
总算解决了上面琐碎的问题后,总可以安心编码,赶紧弄完,回家躺着。每天熬夜,被项目经理催着是种煎熬。然而,当自己在代码中有如下代码时:
href = browser.find_elements_by_tag("a") for href in href: href.click()
然后就直接出来个 StaleElementReferenceException
的异常,直接翻译的话就是对应元素的引用不新鲜了。另外,还有2个常见的异常就是NoSuchElementException和ElementNoVisbleException,通俗的说就是元素找不到和看不到了。
对于这些问题,要根据业务进行取舍,对于后两者,如果业务不是很重要,可以直接跳过当前节点的点击。 而对于StaleElementReferenceException这个异常,只能在每次点击之前重新获取该节点,换句话说,需要这样进行操作:
href = browser.find_elements_by_tag("a") l = len(href) for i in range(l): href = browser.find_elements_by_tag("a")[i] href.click()
这样就可以点击所有的a标签的链接了。
浏览器的Headless与Linux的code=1退出
总算写完基本的逻辑,部署完就可以回家休息了。而在Linux上直接运行不到2分钟就直接code=1的异常退出,异常类似如下:
... can not kill an existed process
对于这样的异常,90%是没有开启浏览器的Headless模式,导致selenium异常退出。
此时,可以通过如下2种方法来开启Headless模式:
option = FirefoxOption() option.add_argument("-headless") #方法一 option.headless = True #方法二
这样上述的问题就可以完美的解决了。
结语
经过1个星期加班加点的工作,总算把selenium的工作可以告一段落了。不得不说,selenium代码接口更新迭代很快,网上很多教程说的也是云里雾里,没少走弯路。
而官方文档的描述更是寥寥几字,很多情况下还是查看其实现的源码可知道来龙去脉,才可以适当的进行修改。
但是,selenium真的是个不错的工具,至少减轻了些琐碎的操作。
对于Python的相关API可以参考:
https://seleniumhq.github.io/selenium/docs/api/py/api.htmlhttps://selenium-python.readthedocs.io/
参考文章:
https://stackoverflow.com/questions/45949274/setting-proxy-in-selenium-in-python-for-firefox-geckodriverhttps://stackoverflow.com/questions/52534658/webdriverexception-message-invalid-argument-cant-kill-an-exited-process-withhttps://www.cnblogs.com/baihuitestsoftware/articles/7753583.html
本文作者:风中纸鹞,1个多年滚打于Web开发的研发工程师。熟悉PHP、Java、C++等编程语言,以编程作为乐趣。
声明:本文为 脚本之家专栏作者 投稿,未经允许请勿转载。