本文总结如何使用python的webdriver插件,应用自动化测试以及爬虫抓取数据。
谷歌版本下载:https://www.iplaysoft.com/tools/chrome/
webdriver版本:http://npm.taobao.org/mirrors/chromedriver/
或https://chromedriver.storage.googleapis.com/index.html
webdriver和谷歌的版本需要对应,比如谷歌的版本是109,就得下载109版本的webdriver
vscode开发工具:https://code.visualstudio.com/
python3.7:https://www.python.org/downloads/
window的cmd下,输入python若出现以下的提示,就是安装完成。
点击左侧栏Extension (或输入 Ctrl + shift + X) 进入插件面板。
该目录下的文件,独立存放python的第三方软件包。比如在D盘新建一个文件夹venv,在vscode终端中运行以下命令。
D: #进入D盘
md venv #新建venv文件夹
python -m venv /venv #使用Python创建venv环境
cd /venv
source bin/activate #激活venv环境,可以看到虚拟环境中的 Package 只有最基础的 pip、setuptools
cd /venv
deactivate #退出venv环境
使用pyvenv.cfg简化venv的开关,这样,vscode在运行相关Python程序的时候,就能自动开关venv环境了。
打开vscode,使用快捷键ctrl+shift+' 或 菜单栏-terminal-new terminal,打开终端
D:
cd /venv
source bin/activate
deactivate
通常,vscode就会在该目录下,自动创建一个pyvenv.cfg文件。
今后只要用vscode打开这个目录,直接运行Python程序,就会自动切换到venv模式下运行。
到这里就可以使用python自带pip工具,安装一个第三方软件selenium
pip3 install selenium
webdriver是selenium的组件,Selenium 是一个 Web 的自动化测试工具,最初是为网站自动化测试而开发的。Selenium 自己不带浏览器,不支持浏览器的功能,它需要与第三方浏览器结合在一起才能使用。它支持所有主流的浏览器(包括 IE、Firefox、Safari、Opera 和 Chrome 等)。
# -*- coding: utf-8 -*-
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
class testlogin:
def __init__(self):
self.driver = webdriver.Chrome()
self.driver.get("www.baidu.com")
if __name__ == "__main__":
test=testlogin()
当然啦,也可以使用无界面形态。Headless Chrome 是 Chrome 浏览器的无界面形态,可以在不打开浏览器的前提下,使用所有 Chrome 支持的特性运行你的程序。相比于现代浏览器,Headless Chrome 更加方便测试 web 应用,获得网站的截图,做爬虫抓取信息等。相比于较早的 PhantomJS 等,Headless Chrome 则更加贴近浏览器环境
# -*- coding: utf-8 -*-
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
class testlogin:
def __init__(self):
#=====改写部分====begin
chrome_options = webdriver.ChromeOptions()
# 增加无界面选项
chrome_options.add_argument('--headless')
# 如果不加这个选项,有时定位会出现问题
chrome_options.add_argument('--disable-gpu')
#=====改写部分====end
self.driver = webdriver.Chrome(options=chrome_options)
self.driver.get("www.baidu.com")
if __name__ == "__main__":
test=testlogin()
Selenium 提供了以下几种定位元素的方法:
描述 |
查找一个元素 |
查找多个元素 |
通过 ID 定位元素 |
find_element_by_id() |
find_elements_by_id() |
通过 Name 定位元素 |
find_element_by_name() |
find_elements_by_name() |
通过 XPath 定位元素 |
find_element_by_xpath() |
find_elements_by_xpath() |
通过完整链接文本定位超链接 |
find_element_by_link_text() |
find_elements_by_link_text() |
通过部分链接文本定位超链接 |
find_element_by_partial_link_text() |
find_elements_by_partial_link_text() |
通过标签名定位元素 |
find_element_by_tag_name() |
find_elements_by_tag_name() |
通过类名定位元素 |
find_element_by_class_name() |
find_elements_by_class_name() |
通过CSS选择器定位元素 |
find_element_by_css_selector() |
find_elements_by_css_selector() |
也可以用find_element统一函数写法,再在参数里声明通过什么方式定位元素,比如
find_element(By.ID,'元素ID') 对应 find_element_by_id('元素ID')
比如登录百度账号,点击登录按钮,在浏览器上按下F12,按下图依次点击。
最后找到id=s-top-loginbtn,就是需要找的id元素,于是脚本就可以做以下的修改。
# -*- coding: utf-8 -*-
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
class testlogin:
def __init__(self):
chrome_options = webdriver.ChromeOptions()
# 增加无界面选项
chrome_options.add_argument('--headless')
# 如果不加这个选项,有时定位会出现问题
chrome_options.add_argument('--disable-gpu')
self.driver = webdriver.Chrome(options=chrome_options)
self.driver.get("www.baidu.com")
#=====新增部分====begin
def loginClick(self):
loginbtn = self.driver.find_element(By.ID,'s-top-loginbtn')
loginbtn.click()
#=====新增部分====end
if __name__ == "__main__":
test=testlogin()
test.loginClick()
需要准确跳转至页面,才能取到元素。
第一种情况:同一页面里有多个frame的话,就得找到对应的frame才行。
# 转到页面
def switchIframe(self,src):
#返回到主窗体
self.driver.switch_to.default_content()
iframes = self.driver.find_elements(By.TAG_NAME,"iframe")
iframe = None
for frm in iframes:
if re.search(src,frm.get_attribute("src"))!=None:
iframe=frm
self.driver.switch_to.frame(iframe)
第二种情况,同时找开了多个页面
def switchWindow(self,url):
windows = self.driver.window_handles
for w in windows:
self.driver.switch_to.window(w)
if self.driver.current_url.startswith(url):
# print(self.driver.title)
break
#有时只需要找开最后一个页面。
self.driver.switch_to.window(self.driver.window_handles[-1])
self.driver.maximize_window()
其实xpath用法比较复杂,一般只会用到第1,2,3点。第4,5,6点需要的时候再查找的。
//tagname[@attribute=‘value’]
其中,tagname表示标签名称,@后面跟随属性名称,等号后面是属性值。如果需要查找多重下级,则在父级的xpath路径后继续追加定位,例如://div[@id='car_genre_id']/div/div,表示 id是car_genre_id的div元素的子节点div的子节点div。
1)单斜线‘/’代表绝对路径,表示该符号后面的元素是上一级节点的子节点的一个,只能一级级按序向下查询,不能跳级;
2)双斜线'//'代表相对路径,表示下级任何子节点或者任何嵌套子节点中的一个,可以跳级;
举个栗子,在路径//div[@id='root']/input中,表示越过所有父节点,直接在整个html页面中查找id是'root'的div元素,因为div标签前是代表相对定位的双斜线'//';而对于input标签来说,它必须是id是'root'的div的一级子节点,它和div中间不能越过其他的层级,因为input标签前是代表绝对路径的单斜线'/' 。
大多浏览器的开发者模式中都具备了输出元素xpath路径的功能。以chrome为例,点击F12后,在DOM树中选中元素,右键> Copy > CopyXpath后,在记事本上黏贴,即可得到该元素的xpath路径。
contains表示查找属性包含该属性值value的页面标签元素,注意是包含,并不是完全等于,类似于sql查找语句中like的用法。
语法为: //tagname[contains(attribute1,'value1') and contains(attribute2,'value2') and ... ]
举个例子,路径 //div[@id="root"]//div[contains(text(),'共'] ,表示在id是'root'的div中,越过层级关系,查找文本信息包含"共"的div元素;
路径//div[@id='root']//div[contains(className,'ant-Btn')], 表示在id是'root'的div中,越过层级关系,查找className属性中包含'ant-Btn'属性的元素;
2.5.1父节点定位子节点:
# 1.串联寻找
print driver.find_element_by_id('B').find_element_by_tag_name('div').text
# 2.xpath父子关系寻找
print driver.find_element_by_xpath("//div[@id='B']/div").text
# 3.css selector父子关系寻找
print driver.find_element_by_css_selector('div#B>div').text
# 4.css selector nth-child
print driver.find_element_by_css_selector('div#B div:nth-child(1)').text
# 5.css selector nth-of-type
print driver.find_element_by_css_selector('div#B div:nth-of-type(1)').text
# 6.xpath轴 child
print driver.find_element_by_xpath("//div[@id='B']/child::div").text driver.quit()
2.5.2由子节点定位父节点:
这里我们有两种办法,第1种是 .. 的形式,就像我们知道的,. 表示当前节点,.. 表示父节点;第2种办法跟上面一样,是xpath轴中的一个:parent,取当前节点的父节点。这里也是css selector的一个痛点,因为css的设计不允许有能够获取父节点的办法(至少目前没有)
# 1.xpath: `.`代表当前节点; '..'代表父节点
print driver.find_element_by_xpath("//div[@id='C']/../..").text
# 2.xpath轴 parent
print driver.find_element_by_xpath("//div[@id='C']/parent::*/parent::div").text
2.5.3由弟弟节点定位哥哥节点
# 1.xpath,通过父节点获取其哥哥节点
print driver.find_element_by_xpath("//div[@id='D']/../div[1]").text
# 2.xpath轴 preceding-sibling
print driver.find_element_by_xpath("//div[@id='D']/preceding-sibling::div[1]").text
2.5.4由哥哥节点定位弟弟节点
# 1.xpath,通过父节点获取其弟弟节点
print driver.find_element_by_xpath("//div[@id='D']/../div[3]").text
# 2.xpath轴 following-sibling
print driver.find_element_by_xpath("//div[@id='D']/following-sibling::div[1]").text
# 3.xpath轴 following
print driver.find_element_by_xpath("//div[@id='D']/following::*").text
# 4.css selector +
print driver.find_element_by_css_selector('div#D + div').text
# 5.css selector ~
print driver.find_element_by_css_selector('div#D ~ div').text
1)保证xpath路径的唯一性。
一定要保证xpath路径在页面中是唯一的。如果不太确定,可以在chrome的开发者工具打开的情况下,点击ctrol+F,在弹出的输入框中输入xpath
的路径,观察当前页面下,以该路径查找到的元素是不是只有一个;
2)尽量不要使用通配符*
*表示可以是当前页面中的任意标签,这样在查找时会增大负载,尽量准确地写出标签名称
3)建议使用相对路径定位
如果前面页面的元素结构发生调整,那么之前的绝对定位就会作废。而使用相对路径,可以减少定位对页面HTML元素结构的依赖性,且相对路径
往往长度较短,更加准确美观。在优化定位方式时,可以将此原则作为参考。
有时直接用selenium接口操作元素失效,不妨尝试直接执行js脚本。
def executeJs(self):
jsStr = "document.getElementById('append').getElementsByTagName('li')[0].click()"
self.driver.execute_script(jsStr)
有时某个元素需等待一段时间才会出现,如果用time.sleep(20000),等待20秒,再执行点击操作,也可以,只是有点low;有时网络没有延迟或页面响应较快,不用等20秒,而是等1秒就出现呢,为了保证元素出现,程序每次都会等待20秒,这样等待时长也是挺难受的。
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions
# 设置等待20秒超时时间,20秒内,如果出现id='append',就直接执行点击操作
def waitForShow(self):
append = WebDriverWait(self.driver, 20).until(expected_conditions.presence_of_element_located((By.ID, "append")))
append.click()