#author("2023-11-22T10:54:32+08:00","default:Admin","Admin")
#author("2023-11-22T10:55:44+08:00","default:Admin","Admin")
[[Python]]

&color(red){※This article is based on Python 3.7.3};

#contents

* 环境 [#dd3c02c3]

** webdriver [#tc23454f]

*** Microsoft Edge浏览器 [#le6d78da]

推荐使用Edge浏览器,因为Chrome的版本问题,webdrive总是没有最新的

浏览器下载地址:
 https://www.microsoft.com/zh-cn/edge
驱动器下载地址:
 https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/

*** Firefox(火狐)浏览器 [#ha861d6f]
浏览器下载地址:
 http://www.firefox.com.cn
驱动器下载地址:
 https://github.com/mozilla/geckodriver/releases

*** Chrome(google)浏览器 [#if7ca860]
浏览器下载地址:
 https://www.google.cn/chrome
驱动器下载地址:
 http://chromedriver.storage.googleapis.com/index.html

下载路径
 http://npm.taobao.org/mirrors/chromedriver/

要和本地的Chrome的版本一致,否则报错

#codeprettify{{
selenium.common.exceptions.WebDriverException: Message: unknown error: cannot connect to chrome at 127.0.0.1:9527
from session not created: This version of ChromeDriver only supports Chrome version 99
Current browser version is 116.0.5845.142
Stacktrace:
Backtrace:
        Ordinal0 [0x007B9943+2595139]
        Ordinal0 [0x0074C9F1+2148849]
        Ordinal0 [0x00644528+1066280]
        Ordinal0 [0x006642C3+1196739]
        Ordinal0 [0x0065D83B+1169467]
        Ordinal0 [0x0065D606+1168902]
        Ordinal0 [0x00690530+1377584]
}}

* 等待 [#x1696d98]

** 显示等待 [#jead09ae]

 WebDriverWait(driver,timeout=10).until(ec.presence_of_element_located(locator))

超时时间内定位到locator后执行下一步,否则超时异常

** 隐式等待 [#zba3cd2f]

 driver.implicitly_wait(timeout=10)

等待页面完全加载好才能执行下一步,只需要获取某个元素时,有点浪费时间,配一个限制一下避免部分页面加载慢

** 强制等待 [#pf4bb74a]
 time.sleep(10)

不要用这种,不稳定

* 打开页面 [#sfb72490]

 driver.set_page_load_timeout(timeout=20)

执行get(url)方法,页面等待超时时间

** 新窗口 [#lc8f2f64]

在新窗口打开页面

#codeprettify{{
from selenium import webdriver
from time import sleep

driver = webdriver.Chrome()
driver.get("https://www.baidu.com")
sleep(2)

newWin = "window.open('https://www.baidu.com')"
driver.execute_script(newWin)

# 句柄切换到新窗口
handles = driver.window_handles
# driver.switch_to.window(handles[-1])# -1为最新打开的窗口
driver.switch_to_window(handles[0])# 0为第一个打开的窗口
driver.find_element_by_id("kw").send_keys("python")
}}

* 退出 [#s053b007]

退出有两种方式
+ close 用于关闭当前窗口,当打开的窗口较多时,就可以用close关闭部分窗口
+ quit 用于结束进程,关闭所有的窗口,即关闭当前浏览器进程

最后结束测试,要用quit。quit可以回收c盘的临时文件。
* 元素定位 [#pbbb88e1]

Html Code
 <input type="text" class="s_ipt" name="wd" id="kw" />

|定位方式|By|h
|id|By.ID|
|name|By.NAME|
|class_name|By.CLASS_NAME|
|tag_name|By.TAG_NAME|
|link_text|By.LINK_TEXT|
|partial_link_text|By.PARTIAL_LINK_TEXT|
|css_selector|By.CSS_SELECTOR|
|xpath|By.XPATH|

** ID定位 [#k9a98108]

find_element_by_id(id) #id参数表示的是id的属性值;(定位的元素必须有ID属性)

#codeprettify{{
driver.find_element_by_id("kw")
}}

** NAME定位 [#w6688dec]
find_element_by_name(name) #name参数表示的是name的属性值;(定位元素必须有NAME属性)

 driver.find_element_by_name("wd")


** CLASS_NAME定位 [#sfdf6aa0]

find_element_by_class_name(class_name) #class_name参数表示的是class的属性值;(定位元素必须有class属性)

 driver.find_element_by_class_name("s_ipt")
 #新的用法
 driver.find_element(by=By.CLASS_NAME, value="s_ipt")
 #列举多个
 driver.find_elements(by=By.CLASS_NAME, value="s_ipt")



注意:class属性值有多个时(用空格隔开),仅需要其中一个属性值:(但若是使用Xpath属性定位时,需要用到全部属性值)

** TAG_NAME定位 [#we1e80e2]

find_element_by_tag_name(tag_name) #tag_name参数表示的是元素的标签名;(定位元素必须有标签名)如果有重复的元素定位到的元素默认都是第一个;

 driver.find_element_by_tag_name("input")

** XPATH定位 [#y0ad1e43]

#codeprettify{{
<?xml version="1.0"?>
<root>
	<child attr="attr" />
	<child>
	    <a><desc /></a>
	</child>
</root>

{针对上面的XML文档的XPath结果,当前节点为document
/root  {选取root
root {选取root
child {空,因为child不是document的子元素
//child {选取两个child元素,//表示后代
//@attr {选取attr属性节点
/root/child//desc {返回child的后代元素desc
}}
*** 使用class定位 [#z391c356]

 driver.find_element_by_xpath('//input[@class="s_ipt"]')
***路径定位 [#ga85e80e]

定位方法:find_element_by_xpath(xpath) #xpath表达式

绝对路径:表达式以/html开头,元素的层级之间是以/分隔,相同层级的元素可以使用下标,下标从1开始;需要列出元素所经过的所有层级元素,在工作中,一般不使用绝对路径。

 /html/body/div/div/div/div/div

路径定位
定位方法:find_element_by_xpath(xpath) #xpath表达式

相对路径:匹配任意层级的元素,是以//tag_name或者//*开头,也可以使用下标,下标从1 开始。

 //div[10]//button

*** 属性定位 [#da43638d]

#codeprettify{{
<input type="text" class="s_ipt" name="wd" id="kw" />
driver.find_element_by_xpath(//input[@id='kw'])
}}

** 通过父元素查找子元素 [#z5deb3d8]

在Selenium中,我们可以使用find_element_by_xpath()方法来查找子元素。例如,假设我们要查找一个div元素下面的第二个a元素,可以使用以下代码:

#codeprettify{{
div_element = driver.find_element_by_xpath("//div[@class='example']")
a_element = div_element.find_element_by_xpath(".//a[2]")
}}

上面的代码首先使用driver.find_element_by_xpath()方法查找到class属性为'example'的div元素,然后使用div_element.find_element_by_xpath()方法查找div元素下面的第二个a元素。在这里,'.//'表示相对路径,表示在当前元素下查找子元素。


** 通过子元素找到父元素 [#gf0cd54c]

在Selenium中,我们也可以使用parent属性来查找父元素。例如,假设我们要查找一个a元素的父元素,可以使用以下代码:

#codeprettify{{
a_element = driver.find_element_by_xpath("//a[@id='example']")
div_element = a_element.find_element_by_xpath("..")
}}

上面的代码首先使用driver.find_element_by_xpath()方法查找id属性为'example'的a元素,然后使用a_element.find_element_by_xpath("..")方法查找a元素的父元素。在这里,'..'表示父节点。

** 兄弟节点定位 [#q1212fde]

兄弟节点定位是指在HTML中,同一级别的元素之间存在兄弟关系。例如,一个ul元素下面有多个li元素,这些li元素就是ul元素的兄弟节点。在Selenium中,我们可以使用兄弟节点定位来查找特定的元素。

*** 查找前一个兄弟节点 [#xc395122]

在Selenium中,我们可以使用preceding-sibling轴来查找前一个兄弟节点。例如,假设我们要查找一个ul元素下面的第一个li元素的前一个兄弟节点,可以使用以下代码:

#codeprettify{{
ul_element = driver.find_element_by_xpath("//ul[@class='example']")
li_element = ul_element.find_element_by_xpath("./li[1]")
previous_li_element = li_element.find_element_by_xpath("preceding-sibling::li[1]")
}}

上面的代码首先使用driver.find_element_by_xpath()方法查找class属性为'example'的ul元素,然后使用ul_element.find_element_by_xpath()方法查找ul元素下面的第一个li元素,最后使用li_element.find_element_by_xpath("preceding-sibling::li[1]")方法查找li元素的前一个兄弟节点。

*** 查找后一个兄弟节点 [#t9c920b2]

在Selenium中,我们可以使用following-sibling轴来查找后一个兄弟节点。例如,假设我们要查找一个ul元素下面的第一个li元素的后一个兄弟节点,可以使用以下代码:

#codeprettify{{
ul_element = driver.find_element_by_xpath("//ul[@class='example']")
li_element = ul_element.find_element_by_xpath("./li[1]")
next_li_element = li_element.find_element_by_xpath("following-sibling::li[1]")
}}

上面的代码首先使用driver.find_element_by_xpath()方法查找class属性为'example'的ul元素,然后使用ul_element.find_element_by_xpath()方法查找ul元素下面的第一个li元素,最后使用li_element.find_element_by_xpath("following-sibling::li[1]")方法查找li元素的后一个兄弟节点。
* 基本操作 [#t3244061]

** 浏览器相关操作 [#a52aa1bd]

创建浏览器对象
 driver = http://webdriver.xxx()

窗口最大化
 maximize_window()

获取浏览器尺寸
 get_window_size()

设置浏览器尺寸
 set_window_size()

获取浏览器位置
 get_window_position()

设置浏览器位置
 set_window_position(x,y)

关闭当前标签/窗口
 close()

关闭所有标签/窗口
 quit()

** 页面相关操作 [#tf82b85f]

请求某个url
 driver.get(url)

刷新页面操作
 refresh()

回退到之前的页面
 back()

前进到之后的页面
 forward()

获取当前访问页面url
 current_url

获取当前浏览器标题
 title

保存图片
 get_screenshot_as_png()/get_screenshot_as_file(file)

网页源码
 page_source

** 元素的操作 [#r747b5fe]

点击操作
 element.click()

清空输入框
 element.clear()

输入框输入数据 element.send_keys(data)

获取文本内容(既开闭标签之间的内容)
 element.text

获取属性值
#codeprettify{{
element = driver.find_element_by_id('user')
print(element.get_attribute('class'))
element = driver.find_element_by_class_name('input')
print(element.get_attribute('value'))
}}

获取 HTML 内容(整个元素 HTML 或 元素内部的 HTML):
#codeprettify{{
element = driver.find_element_by_id('id')
print(element.get_attribute('outerHTML'))	// 获取整个元素对应的 HTML
print(element.get_attribute('innerHTML'))	// 获取元素内部分HTML
}}
** 鼠标和键盘操作 [#ub04688b]

鼠标操作需要导入类,见第一部分,然后创建对象ActionChains(driver),键盘操作导入类

鼠标右击
el = driver.find_element_by_xxx(value)
context_click(el)

鼠标双击
el = driver.find_element_by_xxx(value)
ActionChains(driver).double_click(el).perform()

鼠标悬停
el = driver.find_element_by_xxx(value)
ActionChains(driver).move_to_element(el).perform()

** 常用键盘操作 [#jc6f76c1]

send_keys(Keys.BACK_SPACE)
 删除键(BackSpace)

send_keys(Keys.SPACE)
 空格键(Space)

send_keys(Keys.TAB)
 制表键(Tab)

send_keys(Keys.ESCAPE)
 回退键(Esc)

send_keys(Keys.ENTER)
 回车键(Enter)

send_keys(Keys.CONTROL,‘a’)
 全选(Ctrl+A)

send_keys(Keys.CONTROL,‘c’)
 复制(Ctrl+C)

send_keys(Keys.CONTROL,‘x’)
 剪切(Ctrl+X)

send_keys(Keys.CONTROL,‘v’)
 粘贴(Ctrl+V)

** 弹出框操作 [#gf59935f]

进入到弹出框中
 driver.switch_to.alert()

接收警告
 accept()

关闭警告
 dismiss()

发送文本到警告框 send_keys(data)

** 下拉框操作 [#y0846917]

将定位到的下拉框元素传入Select类中
 selobj = Select(element)

通过索引选择,index 索引从 0 开始
 select_by_index()

通过值选择(option标签的一个属性值)
 select_by_value()

通过文本选择(下拉框的值)
 select_by_visible_text()

查看所有已选
 all_selected_options

查看第一个已选
 first_selected_option

查看是否是多选
 is_multiple

查看选项元素列表
 options

取消选择 
 deselect_by_index() /deselect_by_value()/ deselect_by_visible_text()

** 滚动条操作 [#zdcad984]

x为水平拖动距离,y为垂直拖动举例
 js = "window.scrollTo(x,y) "
 driver.execute_script(js)
 js= “var q=document.documentElement.scrollTop=n” n为从顶部往下移动滚动举例
 driver.execute_script(js)

** cookies操作 [#ofe7f075]

获取所有cookies
 get_cookies()

获取key对应的值
 get_cookie(key)

设置cookies
 add_cookie(cookie_dict)

删除指定名称的cookie
 delete_cookie(name)

删除所有cookie
 delete_all_cookies()

** 多标签/多窗口、多表单/多框架切换 [#f3f4fedb]

多表单/多框架切换

直接使用id值切换进表单 
 driver.switch_to.frame(value)

定位到表单元素,再切换进入
 el = driver.find_element_by_xxx(value)
 driver.switch_to.frame(el)

跳回最外层的页面
 driver.switch_to.default_content()

跳回上层的页面
 driver.switch_to.parent_frame()

多标签/多窗口之间的切换

获取所有窗口的句柄 
 handles = driver.window_handlers

通过窗口的句柄进入的窗口
 driver.switch_to.window(handles[n])
* 例程 [#gf8f73cd]

#codeprettify{{
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.common.exceptions import TimeoutException, NoSuchElementException
from selenium import webdriver
from selenium.webdriver.edge.service import Service
from selenium.webdriver.common.keys import Keys
import time
 
class SeleniumHelper:
    def __init__(self, driver):
        self.driver = driver
 
    def find_element(self, locator, timeout=10):
        """
        查找单个元素
        :param locator: 元素定位信息,如(By.ID, 'element_id')
        :param timeout: 超时时间,默认为10秒
        :return: WebElement对象或None
        """
        try:
            element = WebDriverWait(self.driver, timeout).until(
                EC.presence_of_element_located(locator)
            )
            return element
        except (TimeoutException, NoSuchElementException):
            return None
 
    def find_elements(self, locator, timeout=10):
        """
        查找多个元素
        :param locator: 元素定位信息,如(By.CLASS_NAME, 'element_class')
        :param timeout: 超时时间,默认为10秒
        :return: WebElement对象列表或空列表
        """
        try:
            elements = WebDriverWait(self.driver, timeout).until(
                EC.presence_of_all_elements_located(locator)
            )
            return elements
        except (TimeoutException, NoSuchElementException):
            return []
 
# Edge浏览器 https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/
s=Service('F:\Temp\edgedriver_win64\msedgedriver.exe')
options = webdriver.EdgeOptions()
options.use_chromium = True
# 屏蔽inforbar
options.add_experimental_option('useAutomationExtension', False)
options.add_experimental_option('excludeSwitches', ['enable-automation', 'enable-logging'])
options.add_argument("start-maximized")
# options.add_argument('--headless')
options.binary_location=r'C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe'
driver = webdriver.Edge(service=s, options=options)
driver.get('https://www.baidu.com')
# 创建SeleniumHelper对象
helper = SeleniumHelper(driver)
search_box = helper.find_element((By.ID, 'kw'))
if search_box:
    search_box.send_keys("aaaaaa")
    search_box.send_keys(Keys.RETURN)
time.sleep(6)
#driver.quit()
}}

* Troubleshooting [#mddadd03]

** pip install win32api [#d7298cab]

 ERROR: Could not find a version that satisfies the requirement win32api (from versions: none)
 ERROR: No matching distribution found for win32api

使用pip install pypiwin32 安装成功
 E:\python>pip install pypiwin32

** button is not clickable at point [#ncc7ae24]

错误现象
#codeprettify{{
selenium.common.exceptions.ElementClickInterceptedException: Message: element click intercepted: Element <button type="button" class="lvc2-grey-btn" elementtiming="element-timing">...</button> is not clickable at point (569, 903). Other element would receive the click: <div class="index-module__goodsFooterContainer___3poOW " elementtiming="element-timing" style="left: 526.156px; width: 621.328px;">...</div>
  (Session info: MicrosoftEdge=115.0.1901.183)
}}

原因有四种可能性
+ 没加载出来就等待元素加载出来,再往下执行。最好还是使用selenium自带WebDriverWait
+ 如果元素在iframe里,在窗口里找是找不到元素的,更是无法点击。所以,要切换到iframe里去找元素。 
+ 不在视窗里,需要拉滚动条
+ 要点击的元素被覆盖
+ 要点击的元素被遮挡


* find_element_by_id 找不到要素的处理 [#k247b620]

#codeprettify{{
import unittest
from selenium import webdriver
from selenium.common.exceptions import NoSuchElementException    #导入NoSuchElementException

class ExceptionTest(unittest.TestCase):
    def setUp(self):
        self.driver = webdriver.Chrome()
        self.driver.get("https://www.baidu.com")

    def test_exception(self):
        driver = self.driver
        try:
            search_text = driver.find_element_by_id("ss")
            self.assertEqual('百度一下', search_text.get_attribute("value"))
        except NoSuchElementException:
}}


#hr();
コメント:
#comment_kcaptcha

トップ   編集 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS