Learning Selenium WebDriver

本文介绍如何通过注入Sizzle引擎来解决从Selenium 1过渡到Selenium WebDriver时遇到的定位问题,特别是针对CSS标准的变化导致的:contains('text')无法定位网页元素的情况。通过实现SizzleSelector类并使用其findElementBySizzleCss()和findElementsBySizzleCss()方法,可以充分利用Sizzle选择器的强大功能。

Inject the Sizzle CSS selector library

Problem

We might experience some locator issues during the transition from Selenium 1 to Selenium WebDriver. We face these issues because Selenium WebDriver stays strict to the CSS standard. It might that :contains(‘text’) does not locate the web page elements anymore. The solution is to inject the Sizzle engine into the browser under test. We can make a SizzleSelector class and use the power of Sizzleselector where necessary. Shannon Code came up with a very nice class, which can be found here: http://www.prototypic.net/weblog/?p=14

Solution

This is the code of the SizzleSelector class. It injects the Sizzle engine into the browser under test by JavaScript.


import java.util.List;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebDriverException;
import org.openqa.selenium.WebElement;
@SuppressWarnings("unchecked")
public class SizzleSelector {
private JavascriptExecutor driver;
public SizzleSelector(WebDriver webDriver) {
driver = (JavascriptExecutor) webDriver;
}
public WebElement findElementBySizzleCss(String using) {
injectSizzleIfNeeded();
String javascriptExpression = createSizzleSelectorExpression(using);
List<WebElement> elements = (List<WebElement>) driver
.executeScript(javascriptExpression);
if (elements.size() > 0)
return (WebElement) elements.get(0);
return null;
}
public List<WebElement> findElementsBySizzleCss(String using) {
injectSizzleIfNeeded();
String javascriptExpression = createSizzleSelectorExpression(using);
return (List<WebElement>) driver.executeScript(javascriptExpression);
}
private String createSizzleSelectorExpression(String using) {
return "return Sizzle(\"" + using + "\")";
}
private void injectSizzleIfNeeded() {
if (!sizzleLoaded())
injectSizzle();
}
public Boolean sizzleLoaded() {
Boolean loaded;
try {
loaded = (Boolean) driver.executeScript("return Sizzle()!=null");
} catch (WebDriverException e) {
loaded = false;
}
return loaded;
}
public void injectSizzle() {
driver.executeScript(" var headID = document.getElementsByTagName(\"head\")[0];"
+ "var newScript = document.createElement('script');"
+ "newScript.type = 'text/javascript';"
+ "newScript.src = 'https://raw.github.com/jquery/sizzle/master/sizzle.js';"
+ "headID.appendChild(newScript);");
}
}
 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
53
54
55
56
57
import java.util.List;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebDriverException;
import org.openqa.selenium.WebElement;
@SuppressWarnings("unchecked")
public class SizzleSelector {
private JavascriptExecutor driver;
public SizzleSelector(WebDriver webDriver) {
driver = (JavascriptExecutor) webDriver;
}
public WebElement findElementBySizzleCss(String using) {
injectSizzleIfNeeded();
String javascriptExpression = createSizzleSelectorExpression(using);
List<WebElement> elements = (List<WebElement>) driver
.executeScript(javascriptExpression);
if (elements.size() > 0)
return (WebElement) elements.get(0);
return null;
}
public List<WebElement> findElementsBySizzleCss(String using) {
injectSizzleIfNeeded();
String javascriptExpression = createSizzleSelectorExpression(using);
return (List<WebElement>) driver.executeScript(javascriptExpression);
}
private String createSizzleSelectorExpression(String using) {
return "return Sizzle(\"" + using + "\")";
}
private void injectSizzleIfNeeded() {
if (!sizzleLoaded())
injectSizzle();
}
public Boolean sizzleLoaded() {
Boolean loaded;
try {
loaded = (Boolean) driver.executeScript("return Sizzle()!=null");
} catch (WebDriverException e) {
loaded = false;
}
return loaded;
}
public void injectSizzle() {
driver.executeScript(" var headID = document.getElementsByTagName(\"head\")[0];"
+ "var newScript = document.createElement('script');"
+ "newScript.type = 'text/javascript';"
+ "newScript.src = 'https://raw.github.com/jquery/sizzle/master/sizzle.js';"
+ "headID.appendChild(newScript);");
}
}

How to use it

The class described above will inject the Sizzle engine into the browser under test. This allows us to use all the Sizzle selector features, such as :contains(‘text’). Other classes can extended this class and we are able to use the findElementBySizzleCss() method to locate the elements.


import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
public class SizzleExample {
private static WebDriver driver;
SizzleSelector sizzle;
@BeforeClass
public void setUp() {
driver = new FirefoxDriver();
sizzle = new SizzleSelector(driver);
driver.get("http://selenium.polteq.com/prestashop/");
}
@AfterClass
public void tearDown() {
driver.close();
driver.quit();
}
@Test
public void useSizzleSelector() {
sizzle.findElementBySizzleCss("input#search_query_top").sendKeys("ipod nano");
sizzle.findElementBySizzleCss("input[name='submit_search']").click();
String searchHeader =  sizzle.findElementBySizzleCss("H1").getText().toLowerCase();
Assert.assertTrue(searchHeader.contains("ipod nano"));
}
}
 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
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
public class SizzleExample {
private static WebDriver driver;
SizzleSelector sizzle;
@BeforeClass
public void setUp() {
driver = new FirefoxDriver();
sizzle = new SizzleSelector(driver);
driver.get("http://selenium.polteq.com/prestashop/");
}
@AfterClass
public void tearDown() {
driver.close();
driver.quit();
}
@Test
public void useSizzleSelector() {
sizzle.findElementBySizzleCss("input#search_query_top").sendKeys("ipod nano");
sizzle.findElementBySizzleCss("input[name='submit_search']").click();
String searchHeader =  sizzle.findElementBySizzleCss("H1").getText().toLowerCase();
Assert.assertTrue(searchHeader.contains("ipod nano"));
}
}
This entry was posted in Tips & Tricks on February 4, 2012 by Roy de Kleijn.




原文地址:http://selenium.polteq.com/injecting-the-sizzle-css-selector-library/

<think>我们正在解决一个Python Selenium导入错误:ImportError: cannot import name 'webdriver' from partially initialized module 'selenium' (most likely due to a circular import) 根据引用[2]中的错误信息,用户有一个名为`selenium.py`的文件(位于C:\Users\lenovo\PycharmProjects\untitled\selenium.py),这个文件名与selenium包同名,导致Python在导入时首先找到了这个文件(而不是真正的selenium包),从而引发循环导入错误。 同样,引用[3]也展示了一个类似的问题:文件名`ddt.py`与要导入的ddt包同名。 因此,解决这个问题的关键步骤是: 1. 重命名与标准库或第三方库同名的用户脚本文件。 2. 清理可能存在的.pyc缓存文件。 3. 确保正确安装selenium库。 具体步骤: ### 一、重命名文件(关键步骤) 1. **定位问题文件**: - 检查项目目录中是否有名为`selenium.py`的文件(如引用[2]中路径:`C:\Users\lenovo\PycharmProjects\untitled\selenium.py`) - 将文件重命名为其他名称(如`my_selenium_script.py`),避免与selenium包冲突[^2] 2. **清理缓存文件**: - 删除项目目录下的`__pycache__`文件夹(或所有`.pyc`文件) - Windows命令行操作: ```cmd cd C:\Users\lenovo\PycharmProjects\untitled del /S *.pyc rmdir /S /Q __pycache__ ``` ### 二、验证selenium安装 1. 确保正确安装selenium包: ```cmd pip show selenium # 确认安装信息 pip install --upgrade selenium # 更新到最新版 ``` ### 三、重构导入代码 1. 在重命名后的脚本中使用标准导入方式: ```python from selenium import webdriver # 此时应正常导入 ``` ### 四、运行测试 ```python # 重命名后的脚本内容示例(my_selenium_script.py) from selenium import webdriver driver = webdriver.Chrome() driver.get("https://www.baidu.com") print(driver.title) driver.quit() ``` ### 五、常见问题延伸 - **项目结构规范**:避免使用与Python标准库或第三方库同名的文件名(如`math.py`, `requests.py`等) - **虚拟环境**:使用虚拟环境(如venv)隔离项目依赖,避免全局包污染[^4] ### 错误重现分析(可选) ```mermaid graph LR A[执行 selenium.py] --> B[尝试导入selenium.webdriver] B --> C{查找selenium模块} C -->|优先当前目录| D[找到用户文件selenium.py] D --> E[尝试从该文件中导入webdriver] E --> F[失败并抛出循环导入错误] ``` §§相关问题§§ 1. 如何正确配置Python项目的文件结构? 2. 除了重命名文件,还有哪些解决循环导入的方法? 3. 在PyCharm中如何快速清理项目缓存? 4. 为什么虚拟环境能避免此类导入冲突?</think>### 解决 Python Selenium "ImportError: cannot import name 'webdriver'" 错误(循环导入问题) #### 问题原因分析 根据引用[2]的错误信息: ```python File "C:/Users/lenovo/PycharmProjects/untitled/selenium.py", line 1 ``` 根本原因是**文件命名冲突**:你的脚本文件名 `selenium.py` 与 Selenium 库同名,导致 Python 解释器优先加载你的文件而非真正的库,形成循环导入[^2]。 #### 完整解决方案 ##### 1. **重命名冲突文件(关键步骤)** ```diff - 原文件名: C:\Users\lenovo\PycharmProjects\untitled\selenium.py + 新文件名: C:\Users\lenovo\PycharmProjects\untitled\my_script.py # 任意非库名 ``` > 📌 避免使用以下保留名称:`selenium.py`, `ddt.py`(引用[3]), `unittest.py` 等标准库/第三方库名称 ##### 2. **清理Python缓存文件** 在项目目录执行: ```cmd del /S *.pyc # 删除所有字节码缓存 rmdir /S /Q __pycache__ # 删除缓存目录 ``` ##### 3. **验证Selenium安装** ```cmd pip uninstall -y selenium # 卸载旧版本 pip install --upgrade selenium # 安装最新版 pip show selenium # 验证版本 ``` ##### 4. **修正导入语法** 在新脚本中使用: ```python # 正确导入方式 from selenium import webdriver # 标准导入 from selenium.webdriver.common.by import By # 子模块导入 # 错误示例(会导致循环导入) import selenium # 部分情况下会触发问题 ``` ##### 5. **虚拟环境配置(可选但推荐)** ```cmd python -m venv venv # 创建虚拟环境 venv\Scripts\activate # 激活环境 pip install selenium # 重新安装 ``` #### 验证代码 ```python # 重命名后的测试脚本(如 test_browser.py) from selenium import webdriver def test_chrome(): driver = webdriver.Chrome() driver.get("https://www.baidu.com") assert "百度" in driver.title driver.quit() if __name__ == "__main__": test_chrome() ``` #### 常见错误场景预防 | 场景 | 错误示例 | 正确做法 | |------|----------|----------| | 文件命名 | `selenium.py` | `baidu_test.py` | | 导入方式 | `import selenium.webdriver` | `from selenium import webdriver` | | 项目结构 | 脚本与库同名 | 建立`tests/`目录隔离 | > 💡 **经验提示**:在PyCharm中,当输入`from selenium import`时出现黄色警告线,通常意味着命名冲突[^2] #### 相关问题 1. 如何解决 "This version of ChromeDriver only supports Chrome version XX" 错误? 2. 在 PyCharm 中如何配置虚拟环境避免包冲突? 3. 除了文件命名冲突,还有哪些原因会导致 Python 循环导入错误? 4. 如何正确使用 DDT 或 Parameterized 进行 Selenium 数据驱动测试(参考引用[4])? 5. 企业级项目中如何规范 Python 文件和目录命名?
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值