普通视图

发现新文章,点击刷新页面。
昨天以前首页

🔥《一头扎进》系列之Python+Selenium框架实战篇25 - 年底升职加薪,年终奖就差最后这一步你知道不???

作者 北京_宏哥
2025年3月31日 10:28
到上一篇为止,测试报告已经完美的生成,但是你此时不要沾沾自喜,因为还差点意思,你才能升职加薪、拿年终奖。差点啥了???听宏哥给你慢慢道来。那就是把你生成的高端大气上档次的测试报告给领导展示。

🔥《一头扎进》系列之Python+Selenium框架实战篇24- 年底升职加薪,年终奖全靠它!

作者 北京_宏哥
2025年3月31日 10:15
截止到上一篇文章为止,框架基本完全搭建完成。那么今天我们要做什么呢????聪明如你的小伙伴或者是童鞋一定已经猜到了,都测试完了,当然是要生成一份高端大气上档次的测试报告了。

使用Python爬虫获取淘宝App商品详情

作者 onejason
2025年3月30日 14:01

在电商领域,获取商品详情数据对于市场分析、竞品研究和用户体验优化至关重要。淘宝作为国内领先的电商平台,提供了丰富的商品资源。虽然淘宝App的数据获取相对复杂,但通过Python爬虫技术,我们可以高效地获取淘宝App商品的详细信息,包括商品名称、价格、图片、描述等。本文将详细介绍如何利用Python爬虫获取淘宝App商品详情,并提供完整的代码示例。


一、准备工作

1. 注册淘宝开放平台账号

首先,你需要在淘宝开放平台注册一个开发者账号。登录后,创建一个新的应用,获取应用的App KeyApp 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 的桥梁

作者 没逻辑
2025年3月30日 10:21

简介

PyO3 是一个 Rust 库,它提供了 Rust 和 Python 之间的双向绑定。通过 PyO3,你可以:

  • 在 Python 中调用 Rust 函数和类
  • 在 Rust 中调用 Python 代码
  • 用 Rust 编写 Python 扩展模块

PyO3 的主要优势包括:

  1. 性能:Rust 的执行速度通常比 Python 快得多,特别是在计算密集型任务中
  2. 安全性:Rust 的内存安全保证可以避免许多常见的错误
  3. 并发:利用 Rust 的并发特性,同时保持与 Python 的兼容性
  4. 生态系统:结合 Python 丰富的库生态系统和 Rust 的性能优势

安装与配置

前提条件

  • Rust(推荐使用 rustup 安装)
  • Python 3.7+
  • 适当的开发工具(如 gcc、Visual Studio 等,取决于你的平台)

创建新项目

  1. 创建一个新的 Rust 库项目:
cargo new --lib my_python_module
cd my_python_module
  1. 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"] }
  1. 安装 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> NoneT 的 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)
}

自定义类型

对于自定义类型,你可以实现 FromPyObjectIntoPy 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]

最佳实践

  1. 保持简单接口:尽量使 Rust 和 Python 之间的接口简单明了,避免复杂的类型转换

  2. 合理使用 GIL:对于计算密集型任务,考虑使用 py.allow_threads() 释放 GIL

  3. 错误处理:始终返回 PyResult<T> 并提供有意义的错误信息

  4. 文档:为你的函数和类添加文档字符串,这些会被转换为 Python 的文档字符串

  5. 测试:同时编写 Rust 和 Python 测试来确保你的绑定正常工作

  6. 性能优化:将性能关键部分移至 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 的基础知识和一些高级用法,希望能帮助你开始使用 PyO3 构建高性能的 Python 扩展。随着你对 PyO3 的深入了解,你将能够充分利用 Rust 的性能和安全性,同时保持 Python 的易用性和丰富的生态系统,写作不易,点个赞收藏一下吧~。

HelloGitHub 第 108 期

2025年3月28日 07:52
本期共有 39 个项目,包含 C 项目 (3),C# 项目 (1),C++ 项目 (3),CSS 项目 (1),Go 项目 (3),Java 项目 (1),JavaScript 项目 (5),Kotlin 项目 (1),Python 项目 (5),Rust 项目 (2),Swift 项目 (1),人工智能 (5),其它 (5),开源书籍 (3)

🔥《一头扎进》系列之Python+Selenium框架实战篇21- 价值好几K的框架,呵!这个框架有点意思啊!!!

作者 北京_宏哥
2025年3月27日 10:06

1.简介

  前面文章,我们实现了框架的一部分功能,包括日志类和浏览器引擎类的封装,今天我们继续封装一个基类和介绍如何实现POM。关于基类,是这样定义的:把一些常见的页面操作的selenium封装到base_page.py这个类文件,以后每个POM中的页面类,都继承这个基类,这样每个页面类都有基类的方法,这个我们会在这篇文章由宏哥实现。

2.项目层级结构

  1. 上一篇中我们已经创建好了项目层级结构,具体项目层级结构如下图。这里不再赘述,相关文件也如下:

3. 定位和截图类封装

  1. 在实现封装基类里,我们实现了元素八大方式的定位和截图类封装。

  2. 基类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. 小结

好了,今天的分享就到这里吧!!!谢谢各位的耐心阅读。有问题加群交流讨论

您的肯定就是我进步的动力。如果你感觉还不错,就请鼓励一下吧!记得随手点波 推荐 不要忘记哦!!!

别忘了点 推荐 留下您来过的痕迹

❌
❌