Python使用Splinter(Selenium)进行浏览器模拟测试
原文地址:Python使用Splinter(Selenium)进行浏览器模拟测试 | 豪翔天下
每次看到selenium都觉得很牛,但是苦于文档(包括英文)太少,我到今天才真正完整地安装使用了一把。我不喜欢来一个项目就在自己电脑上搭一个运行环境,而是喜欢在docker或者虚拟机里进行操作,问题是docker或者虚拟机里并没有任何的可视化的浏览器,而Selenium又依赖于这些浏览器驱动,我是最讨厌安装驱动的,因为驱动这个东西电脑不同差距特别大,总是会出现各种问题。而在服务器上如何安装selenium或者splinter,这个过程在网上基本是找不到的,所以这里记录下自己的安装方法。
注:这里之所以要使用splinter
,而不只使用selenium
是因为splinter
在selenium
之上又封装了一层,使得接口更为简单。另外,即使是用的splinter
,也可以通过browser.driver
直接获取到selenium
的驱动的。
Linux install Splinter(Selenium)
首先,需要安装必要的python包pip3 install splinter selenium xvfbwrapper
需要注意的是,splinter只有在使用浏览器的时候才需要安装selenium,如果仅仅是在flask或者django中进行测试是不需要的。
安装chromedriver
ChromeDriver首页-WebDriver for Chrome,下载对应操作系统的最新的chromedriver
1 2 3 4 5 | wget http://chromedriver.storage.googleapis.com/2.23/chromedriver_linux64.zip unzip chromedriver_linux64.zip mv chromedriver /usr/bin # 添加到PATH即可 chromedriver # 运行命令进行测试,没抛错则表示正确了 |
Docker版本Selenium
GitHub - SeleniumHQ/docker-selenium: Docker images for the Selenium Grid Server
handless browser
使用 Headless Chrome 进行页面渲染 - 知乎
Linux Server(Raspberry Pi)安装浏览器
上面的方式是直接打开浏览器的方式,但是在Server上面没有界面,也就没有浏览器,这种情况就得安装单独的真对server的浏览器了。最先我想使用ChromeDriver,但是无论怎么折腾也安装不上,于是就用了Firefox,发现一篇很好的教程。它这个版本被称作Selenium headless firefox
。安装步骤如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | # 添加repository,并安装firefox sudo add-apt-repository ppa:mozillateam/firefox-stable sudo apt-get update sudo apt-get install firefox # 安装Xvfb: 是用来虚拟X服务程序,实现X11显示的协议 sudo apt-get install xvfb sudo Xvfb :10 -ac # 10表示编号 # 设置环境变量 export DISPLAY=:10 # 就可以使用了 firefox |
开始Splinter(Selenium)
无桌面环境
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | from splinter import Browser from xvfbwrapper import Xvfb from selenium.webdriver.chrome.options import Options # 由于是在server上运行chrome,所以必须用一些模拟器 vdisplay = Xvfb() vdisplay.start() # 这些设置都是必要的 chrome_options = Options() chrome_options.add_argument("--no-sandbox") chrome_options.add_argument("--disable-setuid-sandbox") chrome_options.add_argument("--ignore-certificate-errors") // 忽略网页的证书错误 # 这里才是正式的使用了 browser = Browser('chrome', options=chrome_options, executable_path='/root/bin/chromedriver') browser.visit('https://haofly.net') print(browser.title) browser.quit() vdisplay.stop() |
桌面环境
如果直接在本地有桌面环境的情况下进行测试那么,直接这样子:
1 2 3 4 5 6 7 8 | from splinter import Browser from selenium.webdriver.chrome.options import Options chrome_options = Options() browser = Browser('chrome', executable_path='/Users/haofly/share/chromedriver', user_agent='User-Agent设置', options=chrome_options) browser.driver.set_window_size(1500, 900) # 设置浏览器的size browser.visit('https://wiki.haofly.net') print(browser.title) |
Options选项
通过chrome_options.add_argument('')
可以设置非常多的浏览器的参数
1 2 3 | disable-infobars // 禁用网页上部的提示栏,比如2.28的webdriver开始会提示你Chrome正受到自动测试软件的控制,这个特性应该是chrome为了安全给加的 option.add_argument('--user-data-dir=/path') # 指定个人资料路径,指向自己的浏览器,这样,chromedriver的配置就能和自己的一样了 |
获取所有网络请求
很多时候访问一个页面,在该页面可能会同时访问其他的资源,例如js,css,甚至其他一些关键信息。这时候就要求我们能够获取中间的所有的请求,但是selenium
是不带这个功能的,只能使用一些代理,例如:browsermob-proxy。其不需要安装,只需要release
下载bin
包,然后pip install browsermob-proxy
,然后在使用的时候指定路径即可。例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | from browsermobproxy import Server server = Server("~/browsermob-proxy-2.1.4/bin/browsermob-proxy") server.start() proxy = server.create_proxy() chrome_options = Options() chrome_options.add_argument('--proxy-server={host}:{port}'.format(host='localhost', port=proxy.port)) browser = Browser('chrome', executable_path='~/share/chromedriver2.28', options=chrome_options) browser.driver.set_window_size(1500, 900) # 设置浏览器的size browser.driver.set_page_load_timeout(1) # 设置页面加载超时时间,防止visit一直卡住 proxy.new_har() browser.visit('https://haofly.net') print(proxy.har) # 以json的形式打印出中间所有的网络请求 |
浏览器操作
1 2 3 4 5 6 7 8 9 10 | browser.windows # 所有打开了的窗口 browser.windows[0] # 第一个窗口 browser.windows.current # 当前窗口 browser.windows.current = browser.windows[2] # 切换窗口 browser.window[0].close() # 关闭窗口 browser.window[0].close_others() # 关闭其他窗口 browser.window[0].is_current # 序号为零的窗口是否是当前的窗口 browser.window[0].next # 下一个窗口 browser.window[0].prev # 上一个窗口 browser.quit() # 退出浏览器 |
页面操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | browser.visit(url) # 访问URL browser.reload() # 重新加载当前页 browser.back() # 回退 browser.forward() # 向前 browser.find_by_tag('h1').mouse_over() # 鼠标移动到某个元素上 browser.find_by_tag('h1').mouse_out() # 鼠标移开 browser.find_by_tag('h1').click() # 鼠标点击事件 browser.find_ty_tag('h1').double_click()# 鼠标双击事件 browser.find_by_tag('h1').right_click() # 鼠标右键点击 draggable = browser.find_by_tag('h1') # 鼠标拖曳事件 target = browser.find_by_css('.container') draggable.drag_and_drop(target) # 点击链接 browser.click_link_by_href('http://www.the_site.com/my_link') browser.click_link_by_partial_href('my_link') browser.click_link_by_text("submit") browser.click_link_by_partial_text('part of link text') browser.click_link_by_id('link_id') # 点击按钮 browser.find_by_name('send').first.click() browser.find_link_by_text('my link').first.click() # 表单填写 browser.fill('query', 'my name') # 直接通过name属性的元素输入 browser.find_by_tag('input').first.fill('my name') browser.attach_file('file', '/path/to/file/somefile.jpg') browser.choose('some-radio', 'radio-value') browser.check('check-name') # checkbox browser.choose('name', 'value') # radio browser.uncheck('some-check') browser.select('uf', 'rj') # 元素基本操作方法 ele.click() # 点击元素 ele.fill('value') # 输入字符 ele.has_class('name') # 是否包含某个类 ele.mouse_out() ele.mouse_over() ele.screenshot() ele.select('option', slowly=False) # 下拉选择option ele.text # 元素内部的文字内容 ele.type('value', slowly=False) ele.uncheck() ele.value # 跟.text差不多 ele.clear() # 清空元素内容 # selenium发送按键 browser.driver.find_element_by_id('title').send_keys(Keys.BACKSPACE) |
数据获取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | browser.title # 获取网页title browser.html # 获取网页内容 browser.url # 获取网页url browser.find_by_css('h1').first.value # 获取元素值 browser.is_text_present('', wait_time=None) # html里面是否存在某个字符串 # 查找css元素 browser.find_by_css('h1') browser.find_by_xpath('//h1') browser.find_by_tag('h1') browser.find_by_name('name') browser.find_by_text('Hello World!')[1] browser.find_by_id('firstheader').last browser.find_by_value('query').first browser.driver.find_element_by_name('A').find_element_by_xpath('..') # 利用selenium原生来实现寻找parent父级元素 # 寻找网页链接 browser.find_link_by_text() browser.find_link_by_partial_text() browser.find_link_by_href() browser.find_link_by_partial_href() # 可以连续用的 browser.find_by_tag('div').first.find_by_name('name') # 获取Alert/Prompt alert = browser.get_alert(wait_time=None) # 获取alert,官方文档说获取不到返回为None,但是实际会报超时错误 |
TroubleShooting
-
Chrome driver crashes when opens a new tab
原因可能是服务器内存太低了,需要加大虚拟内存
-
selenium.common.exceptions.WebDriverException: Message: session not created exception,将webdriver更新到最新版基本上能解决问题
-
session not created: This version of ChromeDriver only supports Chrome version 77: 这是因为下载的
chromedriver
版本和你当前系统已经安装的chrome
版本不一致造成的,需要对其中某一个进行升级或降级 -
Unable to connect host: 如果浏览器能正常访问,那么可能是
chromedriver
打开网页有问题,导致splinter
未去访问目标网页,并不是网络连接问题 -
browsermob-proxy 地址已在使用中: 可以指定代理服务器的端口:
1 2 3 4 5
from browsermobproxy import Server dict={'port':8090} server = Server(path="./browsermob-proxy",options=dict) server.start() proxy = server.create_proxy()