第六章 selenium自动化测试工具:天下工具为我所用
简介
Selenium是一个Web的自动化测试工具,类型像我们玩游戏用的按键精灵,它支持所有主流的浏览器(包括PhantomJS这些无界面的浏览器)。
Selenium 可以根据我们的指令,让浏览器自动加载页面,获取需要的数据,甚至页面截屏,或者判断网站上某些动作是否发生。
但是被用歪了,现在一般作为破解反爬的一种手段,一般用于解决动态页面或者js加密的爬虫问题。(ps:只要有js参与页面动态生成元素的都叫动态页面)
不然的话用Python 解决这个问题只有两种途径: 1.直接从JavaScript 代码里采集内容(费时费力) 2.用Python 的第三方库运行JavaScript,直接采集你在浏览器里看到的页面。
案例瓜子二手手网站爬取:
import requests
headers = {
"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:66.0) Gecko/20100101 Firefox/66.0",
html = requests.get("https://www.guazi.com/zz/?ca_s=pz_baidu&ca_n=tbmkbturl&scode=10103000312",headers=headers).content.decode()
print(html)
<!DOCTYPE html>
<html lang="en">
<meta charset="utf-8">
<script type="text/javascript">...</script>
</head>
<p>正在打开中,请稍后...<e style='float:right'>2019-05-30 17:36:20</e><p>
</body>
</html>
用今天的技术就可以解决:
from selenium import webdriver
driver = webdriver.Firefox()
driver.get("https://www.guazi.com/zz/?ca_s=pz_baidu&ca_n=tbmkbturl&scode=10103000312")
print(driver.page_source)
结果:很OK
安装
pip install selenium==3.8.0 为了适配phantomjs
如果不适配phantomjs 直接pip install selenium
或者可以使用清华源,安装更快 pip install -i https:// pypi.tuna.tsinghua.edu.cn /simple selenium
浏览器驱动的安装
Selenium 自己不带浏览器,需要自己安装浏览器软件和驱动 一般测试阶段使用带界面的浏览器(谷歌/火狐等):
1.谷歌浏览器下载配置:
打开如下页面: http:// npm.taobao.org/mirrors/ chromedriver/ 把exe文件放置到自己的python环境安装目录
2.火狐浏览器下载配置:
打开如下页面: https:// github.com/mozilla/geck odriver/releases
把exe文件放置到自己的python环境安装目录
3.phantomjs浏览器下载配置:
官网下载安装 http:// phantomjs.org/download. html 1、解压放到:C:\phantomjs-2.1.1-windows 2、需设置环境变量,Path添加C:\phantomjs-2.1.1-windows\bin
问题说明:
真正生成环境下爬取数据阶段使用无界面(PhantomJS) 但是谷歌火狐也可以设置为无头浏览器
最新版本的selenium3.11.0停止对PhantomJS的支持,需要对selenium降级 pip3 install selenium==3.8.0
有时就算这里设置环境变量,后边有可能程序运行的时候,也有可能发生找不到的情况发生 没关系我们可以直接在代码里可以设置指明路径 webdriver.PhantomJS(executable_path="安装路径")
知识
快速入门
Selenium 库里有个叫WebDriver 的API。WebDriver可以加载网站也可以查找页面元素,对页面上的元素进行交互(发送文本、点击等),以及执行其他动作来运行网络爬虫。
from selenium import webdriver
import time
# 打开浏览器驱动
driver = webdriver.Firefox()
# 加载网址
driver.get("http://www.baidu.com")
# 页面源码
print(driver.page_source)
# 获取 cookie
print(driver.get_cookies())
# url
print(driver.current_url)
driver.save_screenshot("./baidu0.png")
driver.find_element_by_id("kw").send_keys("996")
time.sleep(2)
driver.save_screenshot("./baidu1.png")
driver.find_element_by_id("su").click()
time.sleep(2)
driver.save_screenshot("./baidu2.png")
driver.quit()
定位UI元素(WebElements)
各种方式获取元素
单个元素查找 search
# 通过属性为name的值进行过滤
find_element_by_name
find_element_by_id
find_element_by_tag_name
find_element_by_class_name
# 通过链接的文本进行过滤
find_element_by_link_text
# 通过链接的部分文本进行过滤
find_element_by_partial_link_text
# 通过xpath查找
find_element_by_xpath
# 通过css选择器查找
find_element_by_css_selector
多个元素查找 findall
find_elements_by_name
find_elements_by_id
find_elements_by_xpath
find_elements_by_link_text
find_elements_by_partial_link_text
find_elements_by_tag_name
find_elements_by_class_name
find_elements_by_css_selector
通过find_element + by的属性实现获取元素
from selenium.webdriver.common.by import By
setfElement = driver.find_element(By.ID,"setf")
获取元素属性值
元素.get_attribute('属性名')
from selenium import webdriver
# 打开浏览器
browser = webdriver.Chrome()
# 发送请求
url = 'https://www.zhihu.com/explore'
browser.get(url)
# 查找元素
logo =browser.find_element_by_id('zh-top-link-logo')
print(logo)
# 获取元素属性
print(logo.get_attribute('class'))
获取元素标签内部值
元素.text
from selenium import webdriver
# 打开浏览器
browser = webdriver.Chrome()
# 发送请求
url = 'https://www.zhihu.com/explore'
browser.get(url)
# 查找元素
input = browser.find_element_by_class_name('zu-top-add-question')
# 获取元素文本值
print(input.text)
一些琐碎的知识点
执行JavaScript
# 执行js脚本
driver = webdriver.Firefox()
driver.get("https://www.jd.com/")
sleep(2)
# 窗口滑动到底部
driver.execute_script("window.scrollTo(0, document.body.scrollHeight)")
sleep(2)
driver.quit()
截图
使用不同的浏览器即可 PhantomJS是长截图 火狐谷歌是短截图
# 长截图短截图
driver = webdriver.Firefox()
driver.get("http://www.moguproxy.com/http")
sleep(2)
driver.save_screenshot("./短截图.png")
driver.quit()
driver = webdriver.PhantomJS()
driver.get("http://www.moguproxy.com/http")
sleep(2)
driver.save_screenshot("./长截图.png")
driver.quit()
窗口最大化
# 浏览器对象.maximize_window
driver.maximize_window()
元素的位置大小属性(用于截图)
电脑分辨率和缩放比例的改变会影响属性值,用于后边的截图搞验证码
from selenium import webdriver
from time import sleep
driver = webdriver.Firefox()
driver.get("http://www.baidu.com")
# 浏览器窗口最大化
driver.maximize_window()
sleep(2)
driver.save_screenshot("./baiduLogo.png")
logoElement = driver.find_element_by_id("kw")
# 唯一id 指向某一个元素
print(logoElement.id)
# 标签名
print(logoElement.tag_name)
# 元素坐标位置
print(logoElement.location)
# 本身元素大小(受电脑本身屏幕分辨率和文本缩放比例有关系)
print(logoElement.size)
sleep(2)
driver.quit()
动作链
动作链(模拟用户行为)
点击
from selenium import webdriver
from time import sleep
# 比较长 导入动作链类
from selenium.webdriver.common.action_chains import ActionChains
driver = webdriver.Firefox()
driver.get("http://sahitest.com/demo/clicks.htm")
sleep(2)
# 找到需要操作的元素
dbclickElement = driver.find_element_by_xpath("//input[contains(@value,'dbl click me')]")
leftclickElement = driver.find_element_by_xpath("//input[@value='click me']")
rightclickElement = driver.find_element_by_xpath("//input[contains(@value,'right click me')]")
# 链式写法
# 传入浏览器对象构造动作链
actionChains = ActionChains(driver)
# 调用各种各样的动作函数
# double_click左键双击一个元素
# click左键单击一个元素
# context_click右键单击一个元素
actionChains.double_click(dbclickElement).click(leftclickElement).context_click(rightclickElement).perform()
# 或者分开写
# 传入浏览器对象构造动作链
actionChains = ActionChains(driver)
# 左键双击一个元素
actionChains.double_click(dbclickElement)
# 执行 不要忘记
actionChains.perform()
sleep(2)
actionChains = ActionChains(driver)
# 左键单击一个元素
actionChains.click(leftclickElement)
actionChains.perform()
sleep(2)
actionChains = ActionChains(driver)
# 右键单击一个元素
actionChains.context_click(rightclickElement)
actionChains.perform()
sleep(2)
移动
from selenium import webdriver
from time import sleep
from selenium.webdriver.common.action_chains import ActionChains
driver = webdriver.Firefox()
driver.get("http://sahitest.com/demo/mouseover.htm")
sleep(3)
# 找到需要鼠标移动指向的元素
hiElement = driver.find_element_by_xpath("//span[contains(.,'Hi Kamlesh')]")
writeElement = driver.find_element_by_xpath("//input[@value='Write on hover']")
blankElement = driver.find_element_by_xpath("//input[@value='Blank on hover']")
# 直接移动到某一个元素
ActionChains(driver).move_to_element(hiElement).perform()
sleep(2)
# 在当前位置移动x,y的偏移
ActionChains(driver).move_by_offset(10,25).perform()
sleep(2)
# 先移动到某一个元素 再做偏移 是上面两个api的封装
ActionChains(driver).move_to_element_with_offset(writeElement,5,55).perform()
sleep(5)
driver.quit()
拖拽
from selenium import webdriver
from time import sleep
from selenium.webdriver.common.action_chains import ActionChains
driver = webdriver.Firefox()
driver.get("http://sahitest.com/demo/dragDropMooTools.htm")
driver.maximize_window()
sleep(5)
driver.save_screenshot("./dragMe.png")
# 找到相关需要拖拽的元素
dragMe = driver.find_element_by_xpath("//div[@class='drag']")
item1 = driver.find_element_by_xpath("//div[contains(.,'Item 1')]")
item2= driver.find_element_by_xpath("//div[contains(.,'Item 2')]")
item3 = driver.find_element_by_xpath("//div[contains(.,'Item 3')]")
item4 = driver.find_element_by_xpath("//div[contains(.,'Item 4')]")
# drag_and_drop 拖某一个到某一个元素
ActionChains(driver).drag_and_drop(dragMe,item1).perform()
sleep(2)
# drag_and_drop_by_offset 按当前鼠标的位置的偏移 拖拽一个元素
ActionChains(driver).drag_and_drop_by_offset(dragMe,190,250).perform()
sleep(2)
# click_and_hold 鼠标左键持续按住一个元素 move_to_element 移动到某一个元素
# release 释放鼠标
ActionChains(driver).click_and_hold(dragMe).move_to_element(item3).release().perform()
sleep(2)
# click_and_hold 鼠标左键持续按住一个元素
# move_to_element_with_offset 移动到某一个元素再偏移一定像素
# release 释放鼠标
ActionChains(driver).click_and_hold(dragMe).move_to_element_with_offset(item3,150,10).release().perform()
sleep(2)
driver.quit()
按键
from selenium import webdriver
from time import sleep
from selenium.webdriver.common.action_chains import ActionChains
driver = webdriver.Firefox()
driver.get("http://sahitest.com/demo/keypress.htm")
sleep(3)
# 找到相关元素
enter = driver.find_element_by_xpath("//input[@name='t2']")
keyUp = driver.find_element_by_xpath("//label[contains(.,'Key Up')]")
keyDown = driver.find_element_by_xpath("//label[contains(.,'Key Down')]")
keyPress = driver.find_element_by_xpath("//label[contains(.,'Key Press')]")
# 按键周期 按下-按住-抬起 操作逻辑必须符合按键周期
# 按键抬起测试
ActionChains(driver).click(keyUp).click(enter).key_down("a",enter).key_up("a").perform()
sleep(2)
# 按键按下测试
keyDown.click()
enter.click()
ActionChains(driver).key_down("s",enter).key_up("s").perform()
sleep(2)
# 持续按住测试
keyPress.click()
enter.click()
ActionChains(driver).key_down("d",enter).key_up("d").perform()
sleep(2)
driver.quit()
下拉框
from selenium import webdriver
from time import sleep
# 导入一个下拉框类
from selenium.webdriver.support.ui import Select
driver = webdriver.Firefox()
driver.get("http://sahitest.com/demo/selectTest.htm")
sleep(3)
# 下拉框操作 需要提前构造 Select类 参数:select标签元素
select = Select(driver.find_element_by_xpath("//select[@id='s1Id']"))
# 按下标选 0-空 1,2,3-选项顺序
select.select_by_index(1)
# 选择的第一个 元素
print(select.first_selected_option.text)
sleep(2)
# 通过属性value选择
select.select_by_value("o2")
# 所有的选择元素对象
for e in select.all_selected_options:
print(e.text)
sleep(2)
# 按文本显示内容
select.select_by_visible_text("o3")
print(select.first_selected_option.text)
sleep(2)
driver.quit()
各种切换
弹窗处理
switch_to_alert 切换到弹窗
accept 点击【确认】按钮
dismiss 点击【取消】按钮(如有按钮)
send_keys 输入内容(如有输入框)
from selenium import webdriver
from time import sleep
driver = webdriver.Firefox()
driver.get("http://sahitest.com/demo/alertTest.htm")
sleep(3)
clickForAlert = driver.find_element_by_xpath("//input[@name='b1']")
# 触发弹窗
clickForAlert.click()
sleep(1)
# 切换操作对象
alert = driver.switch_to_alert()
sleep(1)
alert.accept()
sleep(1)
driver.quit()
iframe定位
from selenium import webdriver
from time import sleep
driver = webdriver.Firefox()
driver.get("http://sahitest.com/demo/iframesTest.htm")
print(driver.page_source)
print("===================================")
iframe = driver.find_element_by_tag_name("iframe")
# 查找到内嵌网页iframe 切换
driver.switch_to_frame(iframe)
print(driver.page_source)
标签操作
页面切换
from selenium import webdriver
from time import sleep
driver = webdriver.Firefox()
driver.get("http://www.baidu.com")
sleep(3)
# 当前浏览器的标签集合
print(len(driver.window_handles))
print(driver.window_handles)
print("=============================================")
sleep(2)
driver.execute_script("window.open()")
driver.switch_to_window(driver.window_handles[1])
driver.get("http://www.pinduoduo.com")
print(len(driver.window_handles))
print(driver.window_handles)
print("=============================================")
sleep(2)
driver.execute_script("window.open()")
driver.switch_to_window(driver.window_handles[2])
driver.get("http://www.taobao.com")
sleep(2)
driver.switch_to_window(driver.window_handles[1])
sleep(2)
driver.switch_to_window(driver.window_handles[0])
sleep(2)
driver.close()
driver.quit()
页面前进后退
from selenium import webdriver
from time import sleep
# 单个标签的操作前进forward 后退back
driver = webdriver.Firefox()
driver.get("http://www.baidu.com")
sleep(3)
driver.find_element_by_xpath("//a[@name='tj_trnews']").click()
sleep(2)
driver.find_element_by_xpath("//a[@href='http://image.baidu.com/'][contains(.,'图片')]").click()
sleep(2)
driver.back()
sleep(2)
driver.back()
sleep(2)
driver.forward()
sleep(2)
driver.forward()
sleep(2)
driver.close()
页面滚动
# 执行js脚本
driver = webdriver.Firefox()
driver.get("https://www.jd.com/")
sleep(2)
# 窗口滑动到底部
driver.execute_script("window.scrollTo(0, document.body.scrollHeight)")
sleep(2)
driver.quit()
页面刷新与关闭
# 页面刷新
driver.flush()
# 关闭当前页面 如是是最后一个标签 关闭浏览器
driver.close()
# 直接关闭浏览器
driver.quit()
页面等待
强制等待
time.sleep
隐式等待
隐式等待比较简单,就是简单地设置一个等待时间,单位为秒
from selenium import webdriver
driver = webdriver.Chrome()
driver.implicitly_wait(10) # seconds
driver.get("http://www.xxxxx.com/loading")
myDynamicElement= driver.find_element_by_id("myDynamicElement")
当然如果不设置,默认等待时间为0。
显示等待
显式等待指定某个条件,然后设置最长等待时间。如果在这个时间还没有找到元素,那么便会抛出异常了。
from selenium import webdriver
from time import sleep
# expected_conditions 类,负责条件触发
from selenium.webdriver.support import expected_conditions as EC
# WebDriverWait 库,负责循环等待
from selenium.webdriver.support.ui import WebDriverWait
# 查找方式的库
from selenium.webdriver.common.by import By
driver = webdriver.Firefox()
driver.get("http://sahitest.com/demo/linkTest.htm")
WebDriverWait(driver,30).until(EC.presence_of_element_located((By.XPATH,'//div[@class="WB_feed WB_feed_v3 WB_feed_v4"]')))
# 如果不写参数,程序默认会0.5s 调用一次来查看元素是否已经生成,如果本来元素就是存在的,那么会立即返回。下面是一些内置的等待条件,你可以直接调用这些条件,而不用自己写某些等待条件了。
presence_of_element_located元素加载出,传入定位元组,如(By.ID,'p')
element_to_be_clickable元素可点击
visibility_of_element_located元素可见,传入定位元组visibility_of可见,传入元素对象 text_to_be_present_in_element某个元素文本包含某文字 text_to_be_present_in_element_value某个元素值包含某文字 frame_to_be_available_and_switch_to_itframe加载并切换 invisibility_of_element_located元素不可见 staleness_of判断一个元素是否仍在DOM,可判断页面是否已经刷新 element_to_be_selected元素可选择,传元素对象 element_located_to_be_selected元素可选择,传入定位元组 element_selection_state_to_be传入元素对象以及状态,相等返回True,否则返回False element_located_selection_state_to_be传入定位元组以及状态,相等返回True,否则返回False alert_is_present是否出现Alert
selenium配置
提高效率
无界面浏览器
- 火狐
# 创建火狐参数对象
opt = webdriver.FirefoxOptions()
# 把火狐设置成无头模式,不论windows还是linux都可以,自动适配对应参数
opt.set_headless()
# 不制定options选项则是普通有头浏览器
driver = webdriver.Firefox(firefox_options=opt)
from selenium import webdriver
from time import sleep
opt = webdriver.FirefoxOptions()
opt.set_headless()
driver = webdriver.Firefox(firefox_options=opt)
driver.get("http://www.baidu.com")
print(driver.page_source)
sleep(2)
driver.quit()
- 谷歌
# 创建chrome参数对象
opt = webdriver.ChromeOptions()
# 把chrome设置成无头模式,不论windows还是linux都可以,自动适配对应参数
opt.set_headless()
# 不制定options选项则是普通有头浏览器
driver = webdriver.Chrome(options=opt)
from selenium import webdriver
from time import sleep
opt = webdriver.ChromeOptions()
opt.set_headless()
driver = webdriver.Chrome(options=opt)
driver.get("http://www.baidu.com")
print(driver.page_source)
sleep(2)
driver.quit()
禁止加载图片
- 火狐
opt = webdriver.FirefoxOptions()
opt.set_headless()
f = webdriver.FirefoxProfile()
f.set_preference("permissions.default.stylesheet", 2)
f.set_preference('permissions.default.image', 2)
driver = webdriver.Firefox(firefox_options=opt,firefox_profile=f)
- 谷歌
chrome_options = webdriver.ChromeOptions()
prefs = {"profile.managed_default_content_settings.images": 2}
chrome_options.add_experimental_option("prefs", prefs)
chrome_options.add_argument('--headless')
chrome_options.add_argument('--disable-gpu')
driver = webdriver.Chrome(chrome_options=chrome_options)
中断网页加载
from selenium import webdriver
import time
driver = webdriver.Firefox()
driver.set_page_load_timeout(5)
startTime = time.time()
print(time.time())
driver.get("http://www.yuduxx.com/")
except:
driver.execute_script('window.stop ? window.stop() : document.execCommand("Stop");')
print("-----------------")
print(driver.page_source)
print(time.time() - startTime)
设置UA
- 火狐
from selenium import webdriver
profile = webdriver.FirefoxProfile()
user_agent = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.87 Safari/537.36 OPR/37.0.2178.32"
profile.set_preference("general.useragent.override", user_agent)
profile.update_preferences()
driver = webdriver.Firefox(firefox_profile=profile)
driver.get('http://service.spiritsoft.cn/ua.html')
print(driver.page_source)
- 谷歌
from selenium import webdriver
chromeOptions = webdriver.ChromeOptions()
# 一定要注意,=两边不能有空格
chromeOptions.add_argument('user-agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:66.0) Gecko/20100101 Firefox/66.0"')
browser = webdriver.Chrome(chrome_options = chromeOptions)
# 查看本机UA
# http://www.useragentstring.com/
browser.get("http://service.spiritsoft.cn/ua.html")
print(browser.page_source)
设置代理
- 火狐
from selenium import webdriver
profile = webdriver.FirefoxProfile()
profile.set_preference('network.proxy.type', 1)
profile.set_preference('network.proxy.http', '115.239.240.230')
profile.set_preference('network.proxy.http_port', 808) # int
profile.update_preferences()
#"123.160.224.146, 123.160.224.146"
driver = webdriver.Firefox(firefox_profile=profile)
driver.get('http://httpbin.org/ip')
print(driver.page_source)
- 谷歌
from selenium import webdriver
chromeOptions = webdriver.ChromeOptions()
# 设置代理
chromeOptions.add_argument("--proxy-server=http://115.239.240.230:808")
# 一定要注意,=两边不能有空格,不能是这样--proxy-server = http://115.239.240.230:808
browser = webdriver.Chrome(chrome_options = chromeOptions)