普通视图
🔥《一头扎进》系列之Python+Selenium框架实战篇24- 年底升职加薪,年终奖全靠它!
使用Python爬虫获取淘宝App商品详情
在电商领域,获取商品详情数据对于市场分析、竞品研究和用户体验优化至关重要。淘宝作为国内领先的电商平台,提供了丰富的商品资源。虽然淘宝App的数据获取相对复杂,但通过Python爬虫技术,我们可以高效地获取淘宝App商品的详细信息,包括商品名称、价格、图片、描述等。本文将详细介绍如何利用Python爬虫获取淘宝App商品详情,并提供完整的代码示例。
一、准备工作
1. 注册淘宝开放平台账号
首先,你需要在淘宝开放平台注册一个开发者账号。登录后,创建一个新的应用,获取应用的App Key
和App Secret
,这些凭证将用于后续的API调用。
2. 安装必要的Python库
安装以下Python库,用于发送HTTP请求和解析JSON数据:
pip install requests
二、编写爬虫代码
1. 发送HTTP请求
使用requests
库发送GET请求,获取商品页面的HTML内容。
import requests
def get_product_details(product_id):
url = f"https://api.taobao.com/api3/item/getItemDetail.htm?itemId={product_id}"
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3'
}
response = requests.get(url, headers=headers)
if response.status_code == 200:
return response.json()
else:
print(f"请求失败,状态码:{response.status_code}")
return None
2. 解析JSON数据
解析返回的JSON数据,提取商品详情。
def parse_product_details(data):
product_details = {}
if data and 'item' in data:
item = data['item']
product_details['title'] = item.get('title', '')
product_details['price'] = item.get('price', '')
product_details['description'] = item.get('desc', '')
product_details['images'] = item.get('images', [])
product_details['sales'] = item.get('sold', 0)
return product_details
3. 整合代码
将上述功能整合到主程序中,实现完整的爬虫程序。
def main():
product_id = "1234567890" # 替换为实际商品ID
product_data = get_product_details(product_id)
if product_data:
product_details = parse_product_details(product_data)
print("商品详情:")
print(f"标题: {product_details['title']}")
print(f"价格: {product_details['price']}")
print(f"描述: {product_details['description']}")
print(f"图片: {product_details['images']}")
print(f"销量: {product_details['sales']}")
else:
print("未获取到商品详情")
if __name__ == "__main__":
main()
三、注意事项
1. 遵守法律法规
在进行爬虫操作时,必须严格遵守相关法律法规,尊重网站的robots.txt
文件规定。
2. 合理设置请求频率
避免过高的请求频率导致对方服务器压力过大,甚至被封禁IP。
3. 应对反爬机制
淘宝可能会采取一些反爬措施,如限制IP访问频率、识别爬虫特征等。可以通过使用动态代理、模拟正常用户行为等方式应对。
四、总结
通过上述步骤和代码示例,你可以高效地利用Python爬虫获取淘宝App商品详情,并解析返回的数据。无论是用于市场调研、竞品分析还是用户体验优化,这些数据都将为你提供强大的支持。希望本文的示例和策略能帮助你在爬虫开发中更好地应对各种挑战,确保爬虫程序的高效、稳定运行。
如果你在实践中遇到任何问题,欢迎随时交流和讨论。让我们一起用技术的力量,解锁更多可能!
PyO3 教程:连接 Python 与 Rust 的桥梁
简介
PyO3 是一个 Rust 库,它提供了 Rust 和 Python 之间的双向绑定。通过 PyO3,你可以:
- 在 Python 中调用 Rust 函数和类
- 在 Rust 中调用 Python 代码
- 用 Rust 编写 Python 扩展模块
PyO3 的主要优势包括:
- 性能:Rust 的执行速度通常比 Python 快得多,特别是在计算密集型任务中
- 安全性:Rust 的内存安全保证可以避免许多常见的错误
- 并发:利用 Rust 的并发特性,同时保持与 Python 的兼容性
- 生态系统:结合 Python 丰富的库生态系统和 Rust 的性能优势
安装与配置
前提条件
- Rust(推荐使用 rustup 安装)
- Python 3.7+
- 适当的开发工具(如 gcc、Visual Studio 等,取决于你的平台)
创建新项目
- 创建一个新的 Rust 库项目:
cargo new --lib my_python_module
cd my_python_module
- 在
Cargo.toml
中添加 PyO3 依赖:
[package]
name = "my_python_module"
version = "0.1.0"
edition = "2021"
[lib]
name = "my_python_module"
# "cdylib" 用于创建可以被 Python 导入的动态库
crate-type = ["cdylib"]
[dependencies]
pyo3 = { version = "0.20.0", features = ["extension-module"] }
- 安装
maturin
,这是一个用于构建和发布 PyO3 模块的工具:
pip install maturin
基础用法
创建 Python 模块
在 src/lib.rs
中,你可以定义一个 Python 模块:
use pyo3::prelude::*;
/// 这个模块是用 Rust 实现的 Python 模块
#[pymodule]
fn my_python_module(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(sum_as_string, m)?)?;
m.add_class::<MyClass>()?;
Ok(())
}
导出函数到 Python
使用 #[pyfunction]
装饰器可以将 Rust 函数导出到 Python:
/// 计算两个数的和并返回字符串
#[pyfunction]
fn sum_as_string(a: usize, b: usize) -> PyResult<String> {
Ok((a + b).to_string())
}
在 Python 中使用:
import my_python_module
result = my_python_module.sum_as_string(5, 7) # 返回 "12"
print(result) # 输出: 12
导出类到 Python
使用 #[pyclass]
和 #[pymethods]
装饰器可以将 Rust 结构体导出为 Python 类:
#[pyclass]
struct MyClass {
#[pyo3(get, set)]
value: i32,
internal_value: String,
}
#[pymethods]
impl MyClass {
#[new]
fn new(value: i32) -> Self {
MyClass {
value,
internal_value: String::from("内部值"),
}
}
fn get_internal_value(&self) -> PyResult<String> {
Ok(self.internal_value.clone())
}
fn double_value(&mut self) -> PyResult<i32> {
self.value *= 2;
Ok(self.value)
}
#[staticmethod]
fn static_method(value: i32) -> PyResult<i32> {
Ok(value * 2)
}
#[classmethod]
fn class_method(_cls: &PyType, value: i32) -> PyResult<i32> {
Ok(value * 3)
}
}
在 Python 中使用:
from my_python_module import MyClass
# 创建实例
obj = MyClass(10)
# 访问属性
print(obj.value) # 输出: 10
# 修改属性
obj.value = 20
print(obj.value) # 输出: 20
# 调用方法
print(obj.get_internal_value()) # 输出: 内部值
print(obj.double_value()) # 输出: 40
# 调用静态方法
print(MyClass.static_method(5)) # 输出: 10
# 调用类方法
print(MyClass.class_method(5)) # 输出: 15
类型转换
基本类型
PyO3 自动处理 Rust 和 Python 之间的基本类型转换:
Rust 类型 | Python 类型 |
---|---|
bool |
bool |
i8 , i16 , i32 , i64
|
int |
u8 , u16 , u32 , u64
|
int |
f32 , f64
|
float |
&str , String
|
str |
() |
None |
Option<T> |
None 或 T 的 Python 等价物 |
Result<T, E> |
T 的 Python 等价物或引发异常 |
容器类型
对于容器类型,PyO3 提供了转换机制:
use pyo3::prelude::*;
use pyo3::types::{PyDict, PyList};
#[pyfunction]
fn create_list(py: Python) -> PyResult<&PyList> {
let list = PyList::new(py, &[1, 2, 3, 4]);
Ok(list)
}
#[pyfunction]
fn create_dict(py: Python) -> PyResult<&PyDict> {
let dict = PyDict::new(py);
dict.set_item("key1", "value1")?;
dict.set_item("key2", 42)?;
Ok(dict)
}
#[pyfunction]
fn process_list(py: Python, list: &PyList) -> PyResult<usize> {
let mut sum = 0;
for item in list.iter() {
sum += item.extract::<usize>()?;
}
Ok(sum)
}
自定义类型
对于自定义类型,你可以实现 FromPyObject
和 IntoPy
trait:
use pyo3::prelude::*;
#[derive(Debug)]
struct Point {
x: f64,
y: f64,
}
impl<'source> FromPyObject<'source> for Point {
fn extract(ob: &'source PyAny) -> PyResult<Self> {
let x = ob.getattr("x")?.extract::<f64>()?;
let y = ob.getattr("y")?.extract::<f64>()?;
Ok(Point { x, y })
}
}
impl IntoPy<PyObject> for Point {
fn into_py(self, py: Python) -> PyObject {
let dict = PyDict::new(py);
dict.set_item("x", self.x).unwrap();
dict.set_item("y", self.y).unwrap();
dict.into()
}
}
#[pyfunction]
fn distance(point: Point) -> PyResult<f64> {
Ok((point.x.powi(2) + point.y.powi(2)).sqrt())
}
错误处理
PyO3 使用 PyResult<T>
类型来处理错误,这是 Result<T, PyErr>
的类型别名:
use pyo3::prelude::*;
use pyo3::exceptions::PyValueError;
#[pyfunction]
fn divide(a: i32, b: i32) -> PyResult<i32> {
if b == 0 {
return Err(PyValueError::new_err("除数不能为零"));
}
Ok(a / b)
}
你也可以创建自定义异常类型:
use pyo3::prelude::*;
use pyo3::create_exception;
create_exception!(my_module, CustomError, pyo3::exceptions::PyException);
#[pyfunction]
fn may_raise_custom_error(value: i32) -> PyResult<i32> {
if value < 0 {
return Err(CustomError::new_err("值不能为负数"));
}
Ok(value)
}
#[pymodule]
fn my_module(py: Python, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(may_raise_custom_error, m)?)?;
m.add("CustomError", py.get_type::<CustomError>())?;
Ok(())
}
Python GIL 管理
Python 的全局解释器锁(GIL)在使用 PyO3 时需要特别注意:
use pyo3::prelude::*;
#[pyfunction]
fn gil_example(py: Python) -> PyResult<usize> {
// 这段代码在持有 GIL 的情况下执行
let result1 = py.allow_threads(|| {
// 这段代码在释放 GIL 的情况下执行
// 可以进行耗时的计算而不阻塞 Python 解释器
let mut sum = 0;
for i in 0..1_000_000 {
sum += i;
}
sum
});
// 现在我们又持有 GIL 了
Ok(result1)
}
高级主题
异步支持
PyO3 支持与 Python 的异步功能集成:
use pyo3::prelude::*;
use pyo3_asyncio::tokio::future_into_py;
use std::time::Duration;
#[pyfunction]
fn sleep_and_return(py: Python, seconds: f64) -> PyResult<&PyAny> {
let fut = async move {
tokio::time::sleep(Duration::from_secs_f64(seconds)).await;
seconds * 2.0
};
future_into_py(py, fut)
}
#[pymodule]
fn async_module(py: Python, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(sleep_and_return, m)?)?;
Ok(())
}
在 Python 中使用:
import asyncio
from async_module import sleep_and_return
async def main():
result = await sleep_and_return(1.5) # 等待 1.5 秒
print(result) # 输出: 3.0
asyncio.run(main())
内存管理
PyO3 使用引用计数来管理内存,与 Python 的内存管理模型一致:
use pyo3::prelude::*;
#[pyfunction]
fn memory_example(py: Python) -> PyResult<()> {
let locals = PyDict::new(py);
// 创建一个 Python 对象
locals.set_item("x", vec![1, 2, 3])?;
// 执行 Python 代码
py.run("assert x == [1, 2, 3]", None, Some(locals))?;
Ok(())
}
回调函数
PyO3 允许将 Python 函数作为回调传递给 Rust 函数:
use pyo3::prelude::*;
use pyo3::types::PyFunction;
#[pyfunction]
fn call_with_callback(py: Python, callback: &PyFunction, value: i32) -> PyResult<i32> {
// 调用 Python 函数
let result = callback.call1((value,))?;
result.extract::<i32>()
}
#[pyfunction]
fn process_items(py: Python, items: Vec<i32>, callback: &PyFunction) -> PyResult<Vec<i32>> {
let mut results = Vec::new();
for item in items {
let result = callback.call1((item,))?.extract::<i32>()?;
results.push(result);
}
Ok(results)
}
在 Python 中使用:
from my_module import call_with_callback, process_items
def double(x):
return x * 2
result = call_with_callback(double, 5) # 返回 10
print(result) # 输出: 10
results = process_items([1, 2, 3, 4], double) # 返回 [2, 4, 6, 8]
print(results) # 输出: [2, 4, 6, 8]
最佳实践
-
保持简单接口:尽量使 Rust 和 Python 之间的接口简单明了,避免复杂的类型转换
-
合理使用 GIL:对于计算密集型任务,考虑使用
py.allow_threads()
释放 GIL -
错误处理:始终返回
PyResult<T>
并提供有意义的错误信息 -
文档:为你的函数和类添加文档字符串,这些会被转换为 Python 的文档字符串
-
测试:同时编写 Rust 和 Python 测试来确保你的绑定正常工作
-
性能优化:将性能关键部分移至 Rust,保持 Python 接口简洁
常见问题解答
Q: 如何在 Rust 中导入 Python 模块?
use pyo3::prelude::*;
use pyo3::types::IntoPyDict;
#[pyfunction]
fn use_numpy(py: Python) -> PyResult<()> {
let np = py.import("numpy")?;
let array = np.call_method1("array", ([1, 2, 3],))?;
let result = np.call_method1("sum", (array,))?;
println!("{}", result);
Ok(())
}
Q: 如何处理 Python 的字典、列表等复杂类型?
use pyo3::prelude::*;
use pyo3::types::{PyDict, PyList};
#[pyfunction]
fn process_dict_and_list(py: Python, dict: &PyDict, list: &PyList) -> PyResult<&PyDict> {
let result = PyDict::new(py);
// 处理字典
for (key, value) in dict.iter() {
let key_str = key.extract::<String>()?;
let value_int = value.extract::<i32>()?;
result.set_item(key_str, value_int * 2)?;
}
// 处理列表
let sum: i32 = list.iter()
.map(|item| item.extract::<i32>().unwrap_or(0))
.sum();
result.set_item("sum", sum)?;
Ok(result)
}
Q: 如何在 Rust 中捕获 Python 异常?
use pyo3::prelude::*;
use pyo3::exceptions::PyValueError;
#[pyfunction]
fn catch_python_exception(py: Python) -> PyResult<()> {
let result = py.run("1/0", None, None);
match result {
Ok(_) => println!("代码执行成功"),
Err(e) => {
println!("捕获到 Python 异常: {}", e);
if e.is_instance_of::<pyo3::exceptions::PyZeroDivisionError>(py) {
println!("这是一个除零错误");
}
}
}
Ok(())
}
参考资源
官方资源
- PyO3 官方文档 - 完整的API文档和使用指南
- PyO3 GitHub 仓库 - 源代码和问题跟踪
- PyO3 用户指南 - 详细的使用教程
- PyO3 API 参考 - 详细的API文档
构建工具
- maturin - 用于构建和发布PyO3模块的工具
- setuptools-rust - Setuptools的Rust扩展
示例和教程
- PyO3 示例仓库 - 官方示例代码
- Rust for Python开发者 - Red Hat开发者博客
- 使用PyO3加速Python - 实用教程
相关技术
- Rust 编程语言官网 - Rust语言官方网站
- Python 官网 - Python语言官方网站
- CFFI - Python的C外部函数接口
- ctypes - Python的外部函数库
这个教程提供了 PyO3 的基础知识和一些高级用法,希望能帮助你开始使用 PyO3 构建高性能的 Python 扩展。随着你对 PyO3 的深入了解,你将能够充分利用 Rust 的性能和安全性,同时保持 Python 的易用性和丰富的生态系统,写作不易,点个赞收藏一下吧~。
HelloGitHub 第 108 期
🔥《一头扎进》系列之Python+Selenium框架实战篇21- 价值好几K的框架,呵!这个框架有点意思啊!!!
1.简介
前面文章,我们实现了框架的一部分功能,包括日志类和浏览器引擎类的封装,今天我们继续封装一个基类和介绍如何实现POM。关于基类,是这样定义的:把一些常见的页面操作的selenium封装到base_page.py这个类文件,以后每个POM中的页面类,都继承这个基类,这样每个页面类都有基类的方法,这个我们会在这篇文章由宏哥实现。
2.项目层级结构
- 上一篇中我们已经创建好了项目层级结构,具体项目层级结构如下图。这里不再赘述,相关文件也如下:
3. 定位和截图类封装
-
在实现封装基类里,我们实现了元素八大方式的定位和截图类封装。
-
基类base_page.py的具体实现代码,这里就封装了几个常用方法,其他方法,你自己去练习封装下。
3.1 代码实现:
3.2 参考代码:
# -*- coding:utf-8 -*-
# 1.先设置编码,utf-8可支持中英文,如上,一般放在第一行
# 2.注释:包括记录创建时间,创建人,项目名称。
'''
Created on 2025-03-27
@author: 北京-宏哥 QQ交流群:705269076
Project: 《《一头扎进》系列之Python+Selenium框架设计篇4- 价值好几K的框架,不看别后悔,过时不候
'''
# 3.导入模块
import time
from selenium.common.exceptions import NoSuchElementException
import os.path
from automation_framework_demo.framework.logger import Logger
# create a logger instance
logger = Logger(logger="BasePage").getlog()
class BasePage(object):
"""
定义一个页面基类,让所有页面都继承这个类,封装一些常用的页面操作方法到这个类
"""
def __init__(self, driver):
self.driver = driver
# quit browser and end testing
def quit_browser(self):
self.driver.quit()
# 浏览器前进操作
def forward(self):
self.driver.forward()
logger.info("Click forward on current page.")
# 浏览器后退操作
def back(self):
self.driver.back()
logger.info("Click back on current page.")
# 隐式等待
def wait(self, seconds):
self.driver.implicitly_wait(seconds)
logger.info("wait for %d seconds." % seconds)
# 点击关闭当前窗口
def close(self):
try:
self.driver.close()
logger.info("Closing and quit the browser.")
except NameError as e:
logger.error("Failed to quit the browser with %s" % e)
# 保存图片
def get_windows_img(self):
"""
在这里我们把file_path这个参数写死,直接保存到我们项目根目录的一个文件夹.\Screenshots下
"""
file_path = os.path.dirname(os.path.abspath('.')) + '/screenshots/'
rq = time.strftime('%Y%m%d%H%M', time.localtime(time.time()))
screen_name = file_path + rq + '.png'
try:
self.driver.get_screenshot_as_file(screen_name)
logger.info("Had take screenshot and save to folder : /screenshots")
except NameError as e:
logger.error("Failed to take screenshot! %s" % e)
self.get_windows_img()
# 定位元素方法
def find_element(self, selector):
"""
这个地方为什么是根据=>来切割字符串,请看页面里定位元素的方法
submit_btn = "id=>su"
login_lnk = "xpath => //*[@id='u1']/a[7]" # 百度首页登录链接定位
如果采用等号,结果很多xpath表达式中包含一个=,这样会造成切割不准确,影响元素定位
:param selector:
:return: element
"""
element = ''
if '=>' not in selector:
return self.driver.find_element_by_id(selector)
selector_by = selector.split('=>')[0]
selector_value = selector.split('=>')[1]
print(selector_value)
if selector_by == "i" or selector_by == 'id':
try:
element = self.driver.find_element_by_id(selector_value)
logger.info("Had find the element ' %s ' successful "
"by %s via value: %s " % (element.text, selector_by, selector_value))
except NoSuchElementException as e:
logger.error("NoSuchElementException: %s" % e)
self.get_windows_img() # take screenshot
elif selector_by == "n" or selector_by == 'name':
element = self.driver.find_element_by_name(selector_value)
elif selector_by == "c" or selector_by == 'class_name':
element = self.driver.find_element_by_class_name(selector_value)
elif selector_by == "l" or selector_by == 'link_text':
element = self.driver.find_element_by_link_text(selector_value)
elif selector_by == "p" or selector_by == 'partial_link_text':
element = self.driver.find_element_by_partial_link_text(selector_value)
elif selector_by == "t" or selector_by == 'tag_name':
element = self.driver.find_element_by_tag_name(selector_value)
elif selector_by == "x" or selector_by == 'xpath':
try:
element = self.driver.find_element_by_xpath(selector_value)
logger.info("Had find the element ' %s ' successful "
"by %s via value: %s " % (element.text, selector_by, selector_value))
except NoSuchElementException as e:
logger.error("NoSuchElementException: %s" % e)
self.get_windows_img()
elif selector_by == "s" or selector_by == 'selector_selector':
element = self.driver.find_element_by_css_selector(selector_value)
else:
raise NameError("Please enter a valid type of targeting elements.")
print(element)
return element
# 输入
def type(self, selector, text):
el = self.find_element(selector)
el.clear()
try:
el.send_keys(text)
logger.info("Had type ' %s ' in inputBox" % text)
except NameError as e:
logger.error("Failed to type in input box with %s" % e)
self.get_windows_img()
# 清除文本框
def clear(self, selector):
el = self.find_element(selector)
try:
el.clear()
logger.info("Clear text in input box before typing.")
except NameError as e:
logger.error("Failed to clear in input box with %s" % e)
self.get_windows_img()
# 点击元素
def click(self, selector):
el = self.find_element(selector)
try:
el.click()
logger.info("The element ' %s ' was clicked." % el)
except NameError as e:
logger.error("Failed to click the element with %s" % e)
# 或者网页标题
def get_page_title(self):
logger.info("Current page title is %s" % self.driver.title)
return self.driver.title
@staticmethod
def sleep(seconds):
time.sleep(seconds)
logger.info("Sleep for %d seconds" % seconds)
4. pageObjects文件夹下相关代码
1.页面对象中,百度主页的元素定位和简单的操作函数,页面类主要是元素定位和页面操作写成函数,供测试类调用。
baidu_homepage.py
4.1 代码实现:
4.2 参考代码:
# -*- coding:utf-8 -*-
# 1.先设置编码,utf-8可支持中英文,如上,一般放在第一行
# 2.注释:包括记录创建时间,创建人,项目名称。
'''
Created on 2025-03-27
@author: 北京-宏哥 QQ交流群:705269076
Project: 《《一头扎进》系列之Python+Selenium框架设计篇4- 价值好几K的框架,不看别后悔,过时不候
'''
# 3.导入模块
from automation_framework_demo.framework.base_page import BasePage
class HomePage(BasePage):
input_box = "id=>kw"
search_submit_btn = "xpath=>//*[@id='su']"
#百度新闻入口
#news_link = "xpath=>//*[@id='u1']/a[@name='tj_trnews']"
news_link = "xpath=>//*[@id='u1']/a[@name='tj_trnews']"
def type_search(self, text):
self.type(self.input_box, text)
def send_submit_btn(self):
self.click(self.search_submit_btn)
def click_news(self,):
self.click(self.news_link)
self.sleep(2)
这里注意下元素定位写法,=>和base_page.py中find_element()方法元素定位切割有关系,网上有些人写根据逗号切割或者等号切割,在实际使用xpath定位,发现单独逗号或者单独等号切割都不精确,造成元素定位失败。
5. 测试类的写法举例
1.新建一个测试类baidu_search1.py。
baidu_search1.py
5.1 代码实现:
5.2 参考代码:
# -*- coding:utf-8 -*-
# 1.先设置编码,utf-8可支持中英文,如上,一般放在第一行
# 2.注释:包括记录创建时间,创建人,项目名称。
'''
Created on 2025-03-27
@author: 北京-宏哥 QQ交流群:705269076
Project: 《《一头扎进》系列之Python+Selenium框架设计篇4- 价值好几K的框架,不看别后悔,过时不候
'''
# 3.导入模块
import time
import unittest
from automation_framework_demo.framework.browser_engine import BrowserEngine
from automation_framework_demo.pageobjects.baidu_homepage import HomePage
class BaiduSearch(unittest.TestCase):
def setUpClass(cls):
"""
测试固件的setUp()的代码,主要是测试的前提准备工作
:return:
"""
browse = BrowserEngine(cls)
cls.driver = browse.open_browser(cls)
def tearDownClass(cls):
"""
测试结束后的操作,这里基本上都是关闭浏览器
:return:
"""
cls.driver.quit()
def test_baidu_search(self):
"""
这里一定要test开头,把测试逻辑代码封装到一个test开头的方法里。
:return:
"""
# self.driver.find_element_by_id('kw').send_keys('selenium')
# time.sleep(1)
homepage = HomePage(self.driver)
homepage.type_search('selenium') # 调用页面对象中的方法
homepage.send_submit_btn() # 调用页面对象类中的点击搜索按钮方法
time.sleep(2)
homepage.get_windows_img() # 调用基类截图方法
print(self.driver.title)
try:
assert('selenium' in HomePage.get_page_title(self))
print('Test Pass.')
except Exception as e:
print('Test Fail.', format(e))
def test_search2(self):
homepage = HomePage(self.driver)
homepage.type_search('python') # 调用页面对象中的方法
homepage.send_submit_btn() # 调用页面对象类中的点击搜索按钮方法
time.sleep(2)
homepage.get_windows_img() # 调用基类截图方法
if __name__ == '__main__':
unittest.main()
5.3 运行结果:
运行代码后,控制台打印如下图的结果
5.4 代码说明
homepage = HomePage(self.driver)
上面这行代码要注意,意思是:到一个页面,第一件事情是初始化这个页面的一个页面对象实例。注意,一定要带self.driver,不然会报错,这个self.driver,可以这样理解:在当前测试类里面,self.driver是来自浏览器引擎类中方法得到的,在初始化一个页面对象
的时候,也把这个来自浏览器引擎类的driver给赋值给当前的页面对象,这样,才能执行页面对象或者基类里面的相关driver方法。写多了selenium的自动化脚本,你会明白,最重要的是保持前后driver的唯一性。
5.5 生成图片
1.测试结果:会在logs文件夹生成一个日志文件,也会在screenshots文件夹生成一个png图片。日志看过了,这里我们看一下图片
2.图片内容如下图:
6. 小结
好了,今天的分享就到这里吧!!!谢谢各位的耐心阅读。有问题加群交流讨论
您的肯定就是我进步的动力。如果你感觉还不错,就请鼓励一下吧!记得随手点波 推荐 不要忘记哦!!!
别忘了点 推荐 留下您来过的痕迹