ITPub博客

首页 > 应用开发 > IT综合 > Python中基于文本内容定位网页元素的全面指南

Python中基于文本内容定位网页元素的全面指南

投稿 IT综合 作者:bhluo 时间:2025-12-18 17:01:33 0 删除 编辑

# Python中基于文本内容定位网页元素的全面指南


## 引言:文本定位在Web自动化中的重要性


在Web自动化和爬虫开发中,基于文本内容定位元素是一项至关重要的技能。无论是测试网页功能、提取特定数据,还是模拟用户交互,精准地找到包含特定文本的元素都是成功的关键。Python提供了多种强大的库和方法来实现这一目标,本文将深入探讨各种文本定位技术及其实际应用。


## 基础定位方法


### XPath文本定位基础


XPath是定位包含特定文本元素的最强大工具之一,提供了多种文本匹配 方式。


```python

from selenium import webdriver

from selenium.webdriver.common.by import By

from selenium.webdriver.support.ui import WebDriverWait

from selenium.webdriver.support import expected_conditions as EC


def basic_text_locating():

    """基础文本定位示例"""

    driver = webdriver.Chrome()

    

    try:

        driver.get("https://example.com")

        

        # 精确文本匹配

        exact_text_element = driver.find_element(

            By.XPATH, "//*[text()='登录']"

        )

        

        # 包含特定文本

        contains_text_element = driver.find_element(

            By.XPATH, "//*[contains(text(), '欢迎')]"

        )

        

        # 文本开头匹配

        starts_with_element = driver.find_element(

            By.XPATH, "//*[starts-with(text(), '用户')]"

        )

        

        # 文本规范化(处理空白字符)

        normalized_element = driver.find_element(

            By.XPATH, "//*[normalize-space(text())='搜索']"

        )

        

        # 多个文本条件组合

        complex_element = driver.find_element(

            By.XPATH, "//button[contains(text(), '提交') and @type='button']"

        )

        

        print("成功定位到元素")

        

    finally:

        driver.quit()


def xpath_text_variations():

    """XPath文本定位的各种变体"""

    

    # 示例XPath表达式

    xpath_examples = {

        "精确匹配": "//button[text()='保存']",

        "忽略大小写": "//*[translate(text(), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')='login']",

        "部分匹配": "//a[contains(text(), '关于我们')]",

        "开头匹配": "//h1[starts-with(text(), '欢迎来到')]",

        "结尾匹配": "//span[substring(text(), string-length(text()) - string-length('公司') + 1) = '公司']",

        "正则表达式匹配(XPath 2.0)": "//*[matches(text(), '^用户\\d+$')]",

        "文本长度限制": "//p[string-length(text()) > 100]",

        "文本包含且具有特定属性": "//div[contains(text(), '价格') and @class='price']",

        "多个文本条件": "//li[contains(text(), '产品') and contains(text(), '介绍')]",

        "兄弟元素中的文本": "//label[text()='用户名:']/following-sibling::input",

        "父元素基于子元素文本": "//div[.//span[text()='热门']]",

    }

    

    return xpath_examples

```


### CSS选择器文本定位


虽然CSS选择器原生不支持文本匹配,但可以结合其他属性和伪类实现类似效果。


```python

def css_text_locating_workarounds():

    """使用CSS选择器间接定位文本元素"""

    

    css_examples = {

        # 通过属性间接定位

        "通过title属性": "[title*='搜索']",

        "通过aria-label": "[aria-label*='菜单']",

        "通过data属性": "[data-text*='提示']",

        

        # 结合伪类

        "通过位置和类型": "button:nth-of-type(2)",

        

        # 通过其他可见属性

        "placeholder包含文本": "input[placeholder*='请输入']",

        "value属性": "input[value='提交']",

        "alt文本": "img[alt*='图标']",

        

        # 组合选择器

        "类名和属性组合": ".btn-primary[type='submit']",

    }

    

    return css_examples


def find_elements_by_css_and_text(driver, css_selector, expected_text):

    """

    结合CSS选择器和文本过滤定位元素

    """

    elements = driver.find_elements(By.CSS_SELECTOR, css_selector)

    

    # 过滤包含预期文本的元素

    matching_elements = [

        elem for elem in elements 

        if expected_text in elem.text

    ]

    

    return matching_elements

```


## 高级定位策略


### 模糊匹配与正则表达式


```python

import re

from typing import List, Optional

from selenium.webdriver.remote.webelement import WebElement


class AdvancedTextLocator:

    """高级文本定位器"""

    

    def __init__(self, driver):

        self.driver = driver

    

    def find_by_regex(self, pattern: str, element_type: str = "*", 

                     timeout: int = 10) -> List[WebElement]:

        """

        使用正则表达式定位元素

        

        Args:

            pattern: 正则表达式模式

            element_type: 元素类型,如'div', 'span', '*'表示所有类型

            timeout: 等待超时时间

        

        Returns:

            匹配的元素列表

        """

        try:

            # 等待页面加载

            WebDriverWait(self.driver, timeout).until(

                lambda d: d.execute_script(

                    "return document.readyState") == "complete"

            )

            

            # 查找所有指定类型的元素

            elements = self.driver.find_elements(

                By.XPATH, f"//{element_type}"

            )

            

            # 使用正则表达式过滤

            regex = re.compile(pattern, re.IGNORECASE)

            matched_elements = []

            

            for element in elements:

                text = element.text.strip()

                if text and regex.search(text):

                    matched_elements.append(element)

            

            return matched_elements

            

        except Exception as e:

            print(f"正则定位失败: {e}")

            return []

    

    def find_by_fuzzy_match(self, target_text: str, 

                           threshold: float = 0.8) -> List[WebElement]:

        """

        模糊文本匹配

        

        Args:

            target_text: 目标文本

            threshold: 相似度阈值(0-1)

        

        Returns:

            匹配的元素列表

        """

        from difflib import SequenceMatcher

        

        elements = self.driver.find_elements(By.XPATH, "//*[text()]")

        matched_elements = []

        

        for element in elements:

            element_text = element.text.strip()

            if not element_text:

                continue

                

            # 计算相似度

            similarity = SequenceMatcher(

                None, target_text.lower(), element_text.lower()

            ).ratio()

            

            if similarity >= threshold:

                matched_elements.append({

                    'element': element,

                    'text': element_text,

                    'similarity': similarity

                })

        

        # 按相似度排序

        matched_elements.sort(key=lambda x: x['similarity'], reverse=True)

        return [item['element'] for item in matched_elements]

    

    def find_near_text(self, anchor_text: str, 

                      target_tag: str = "input") -> Optional[WebElement]:

        """

        定位特定文本附近的元素

        

        Args:

            anchor_text: 锚点文本

            target_tag: 要定位的目标元素标签

        

        Returns:

            目标元素或None

        """

        try:

            # 查找包含锚点文本的元素

            xpath = f"//*[contains(text(), '{anchor_text}')]"

            anchor_element = self.driver.find_element(By.XPATH, xpath)

            

            # 查找附近的同辈元素

            nearby_xpath = f"./following-sibling::{target_tag}[1]"

            target_element = anchor_element.find_element(

                By.XPATH, nearby_xpath)

            

            return target_element

            

        except Exception:

            # 尝试其他定位策略

            try:

                # 查找父元素内的目标元素

                xpath = f"//*[contains(text(), '{anchor_text}')]/..//{target_tag}"

                return self.driver.find_element(By.XPATH, xpath)

            except Exception as e:

                print(f"无法定位'{anchor_text}'附近的{target_tag}元素: {e}")

                return None

```


### 动态文本处理


```python

class DynamicTextHandler:

    """处理动态生成文本的定位器"""

    

    def __init__(self, driver):

        self.driver = driver

    

    def wait_for_text(self, expected_text: str, 

                     timeout: int = 30,

                     poll_frequency: float = 0.5) -> bool:

        """

        等待特定文本出现

        

        Args:

            expected_text: 期望出现的文本

            timeout: 最大等待时间

            poll_frequency: 轮询频率

        

        Returns:

            是否成功等到文本

        """

        try:

            WebDriverWait(self.driver, timeout, poll_frequency).until(

                EC.text_to_be_present_in_element(

                    (By.XPATH, f"//*[contains(text(), '{expected_text}')]"),

                    expected_text

                )

            )

            return True

        except Exception:

            return False

    

    def wait_for_text_change(self, element_locator: tuple,

                            original_text: str,

                            timeout: int = 30) -> bool:

        """

        等待元素的文本发生变化

        

        Args:

            element_locator: 元素定位器元组

            original_text: 原始文本

            timeout: 最大等待时间

        

        Returns:

            文本是否发生变化

        """

        try:

            WebDriverWait(self.driver, timeout).until_not(

                lambda d: d.find_element(*element_locator).text == original_text

            )

            return True

        except Exception:

            return False

    

    def locate_dynamic_element(self, text_pattern: str,

                              element_type: str = "div") -> List[WebElement]:

        """

        定位包含动态文本的元素

        

        Args:

            text_pattern: 文本模式(可包含通配符)

            element_type: 元素类型

        

        Returns:

            匹配的元素列表

        """

        # 将模式转换为正则表达式

        regex_pattern = text_pattern.replace("*", ".*").replace("?", ".")

        

        # 查找所有可能的元素

        elements = self.driver.find_elements(By.TAG_NAME, element_type)

        

        matched_elements = []

        for element in elements:

            element_text = element.text

            if re.search(regex_pattern, element_text, re.IGNORECASE):

                matched_elements.append(element)

        

        return matched_elements

    

    def extract_text_with_context(self, search_text: str,

                                 context_chars: int = 50) -> List[dict]:

        """

        提取包含搜索文本的上下文

        

        Args:

            search_text: 搜索文本

            context_chars: 上下文字符数

        

        Returns:

            包含上下文信息的字典列表

        """

        elements = self.driver.find_elements(

            By.XPATH, f"//*[contains(text(), '{search_text}')]"

        )

        

        results = []

        for element in elements:

            full_text = element.text

            text_index = full_text.find(search_text)

            

            if text_index >= 0:

                start = max(0, text_index - context_chars)

                end = min(len(full_text), text_index + len(search_text) + context_chars)

                

                context = full_text[start:end]

                if start > 0:

                    context = "..." + context

                if end < len(full_text):

                    context = context + "..."

                

                results.append({

                    'element': element,

                    'full_text': full_text,

                    'context': context,

                    'position': text_index,

                    'tag': element.tag_name,

                    'attributes': element.get_attribute('outerHTML')[:100]

                })

        

        return results

```


## 实战应用案例


### 登录页面元素定位


```python

class LoginPageLocator:

    """登录页面文本定位实战"""

    

    def __init__(self, driver):

        self.driver = driver

        self.wait = WebDriverWait(driver, 10)

    

    def locate_login_elements(self) -> dict:

        """

        定位登录页面所有相关元素

        """

        elements = {}

        

        # 定位各种登录相关文本

        login_text_variations = [

            "登录", "登陆", "登入", "Sign in", "Sign In", "LOGIN",

            "Log in", "登录/注册", "用户登录"

        ]www.qj.zizhitong.cn>www.hl.zizhitong.cn

        

        for text in login_text_variations:

            try:

                elements[f"login_button_{text}"] = self.driver.find_elements(

                    By.XPATH, f"//*[contains(text(), '{text}')]"

                )

            except:

                pass

        

        # 定位用户名/邮箱字段

        username_labels = ["用户名", "账号", "邮箱", "手机号", 

                          "Username", "Email", "Account"]

        

        for label in username_labels:

            try:

                # 找到标签,然后定位对应的输入框

                label_elements = self.driver.find_elements(

                    By.XPATH, f"//*[contains(text(), '{label}')]"

                )

                

                for label_elem in label_elements:

                    # 尝试找到相邻的输入框

                    try:

                        input_elem = label_elem.find_element(

                            By.XPATH, "./following-sibling::input[1]"

                        )

                        elements[f"username_input_{label}"] = input_elem

                    except:

                        pass

            except:

                pass

        

        # 定位密码字段

        password_labels = ["密码", "Password", "pass", "pwd"]

        

        for label in password_labels:

            try:

                password_elements = self.driver.find_elements(

                    By.XPATH, f"//*[contains(text(), '{label}')]"

                )

                elements[f"password_label_{label}"] = password_elements

            except:

                pass

        

        # 定位记住我/自动登录

        remember_texts = ["记住我", "自动登录", "保持登录", "Remember me"]

        

        for text in remember_texts:

            try:

                elements[f"remember_{text}"] = self.driver.find_elements(

                    By.XPATH, f"//*[contains(text(), '{text}')]"

                )

            except:

                pass

        

        return elements

    

    def smart_login_button_finder(self) -> Optional[WebElement]:

        """

        智能查找登录按钮

        """

        button_candidates = []

        

        # 策略1:查找包含登录文本的按钮

        login_texts = ["登录", "Sign in", "LOGIN", "登入"]

        

        for text in login_texts:

            try:

                buttons = self.driver.find_elements(

                    By.XPATH, f"//button[contains(text(), '{text}')]"

                )

                button_candidates.extend(buttons)

            except:

                pass

        

        # 策略2:查找包含登录文本的a标签

        for text in login_texts:

            try:

                links = self.driver.find_elements(

                    By.XPATH, f"//a[contains(text(), '{text}')]"

                )

                button_candidates.extend(links)

            except:

                pass

        

        # 策略3:查找提交类型的输入框

        try:

            submit_inputs = self.driver.find_elements(

                By.XPATH, "//input[@type='submit' or @type='button']"

            )

            button_candidates.extend(submit_inputs)

        except:

            pass

        

        # 策略4:通过表单定位

        try:

            forms = self.driver.find_elements(By.TAG_NAME, "form")

            for form in forms:

                try:

                    submit_in_form = form.find_element(

                        By.XPATH, ".//*[@type='submit']"

                    )

                    button_candidates.append(submit_in_form)

                except:

                    pass

        except:

            pass

        

        # 选择最佳候选

        if button_candidates:

            # 优先选择可见的按钮

            visible_buttons = [

                btn for btn in button_candidates if btn.is_displayed()

            ]

            

            if visible_buttons:

                # 选择第一个可见按钮

                return visible_buttons[0]

            

            return button_candidates[0]

        

        return None

    

    def fill_login_form(self, username: str, password: str) -> bool:

        """

        自动填写登录表单

        """

        try:

            # 查找用户名输入框

            username_input = self.find_username_field()

            if username_input:

                username_input.clear()

                username_input.send_keys(username)

            

            # 查找密码输入框

            password_input = self.find_password_field()

            if password_input:

                password_input.clear()

                password_input.send_keys(password)

            

            # 点击登录按钮

            login_button = self.smart_login_button_finder()

            if login_button:

                login_button.click()

                return True

            

            return False

            

        except Exception as e:

            print(f"填写登录表单失败: {e}")

            return False

    

    def find_username_field(self) -> Optional[WebElement]:

        """查找用户名输入框"""

        # 常见的用户名输入框特征

        username_indicators = [

            ("input", "@type='text' and (@name='username' or @name='email')"),

            ("input", "@placeholder*='用户名' or @placeholder*='邮箱'"),

            ("input", "@id*='user' or @id*='email'"),

        ]

        

        for tag, condition in username_indicators:

            try:

                return self.driver.find_element(

                    By.XPATH, f"//{tag}[{condition}]"

                )

            except:

                continue

        

        return None

    

    def find_password_field(self) -> Optional[WebElement]:

        """查找密码输入框"""

        password_indicators = [

            ("input", "@type='password'"),

            ("input", "@name*='pass' or @id*='pass'"),

            ("input", "@placeholder*='密码'"),

        ]

        

        for tag, condition in password_indicators:

            try:

                return self.driver.find_element(

                    By.XPATH, f"//{tag}[{condition}]"

                )

            except:

                continue

        

        return None

```


### 数据提取与解析


```python

class DataExtractor:

    """基于文本的数据提取器"""

    

    def __init__(self, driver):

        self.driver = driver

    

    def extract_table_data(self, table_locator: tuple) -> List[dict]:

        """

        提取表格数据

        """

        try:

            table = self.driver.find_element(*table_locator)

            rows = table.find_elements(By.TAG_NAME, "tr")

            

            data = []

            headers = []

            

            # 提取表头

            header_row = rows[0]

            header_cells = header_row.find_elements(By.TAG_NAME, "th")

            if not header_cells:

                header_cells = header_row.find_elements(By.TAG_NAME, "td")

            

            headers = [cell.text.strip() for cell in header_cells]

            

            # 提取数据行

            for row in rows[1:]:

                cells = row.find_elements(By.TAG_NAME, "td")

                row_data = {}www.cw.zizhitong.cn>www.ny.zizhitong.cn

                

                for i, cell in enumerate(cells):

                    if i < len(headers):

                        header = headers[i]

                        row_data[header] = cell.text.strip()

                    else:

                        row_data[f"column_{i}"] = cell.text.strip()

                

                if row_data:  # 忽略空行

                    data.append(row_data)

            

            return data

            

        except Exception as e:

            print(f"提取表格数据失败: {e}")

            return []

    

    def extract_by_pattern(self, pattern: str, 

                          context_element: WebElement = None) -> List[str]:

        """

        根据模式提取文本

        """

        if context_element:

            root = context_element

        else:

            root = self.driver

        

        # 获取所有文本

        all_text = root.find_element(By.TAG_NAME, "body").text

        

        # 使用正则表达式匹配模式

        matches = re.findall(pattern, all_text, re.MULTILINE | re.DOTALL)

        

        return matches

    

    def extract_numbers_from_text(self, text: str) -> List[float]:

        """

        从文本中提取数字

        """

        # 匹配各种数字格式

        number_pattern = r'[-+]?\d*\.?\d+'

        matches = re.findall(number_pattern, text)

        

        numbers = []

        for match in matches:

            try:

                numbers.append(float(match))

            except ValueError:

                continue

        

        return numbers

    

    def extract_key_value_pairs(self, separator: str = ":") -> dict:

        """

        提取键值对数据

        """

        # 查找所有可能包含键值对的元素

        elements = self.driver.find_elements(

            By.XPATH, f"//*[contains(text(), '{separator}')]"

        )

        

        key_value_pairs = {}

        

        for element in elements:

            text = element.text.strip()

            if separator in text:

                parts = text.split(separator, 1)

                if len(parts) == 2:

                    key = parts[0].strip()

                    value = parts[1].strip()

                    key_value_pairs[key] = value

        

        return key_value_pairs

    

    def extract_links_with_text(self, link_text: str = None) -> List[dict]:

        """

        提取包含特定文本的链接

        """

        links = []

        

        if link_text:

            # 查找包含特定文本的链接

            link_elements = self.driver.find_elements(

                By.XPATH, f"//a[contains(text(), '{link_text}')]"

            )

        else:

            # 查找所有链接

            link_elements = self.driver.find_elements(By.TAG_NAME, "a")

        

        for link in link_elements:

            if link.text.strip():  # 忽略空文本链接

                links.append({

                    'text': link.text.strip(),

                    'href': link.get_attribute('href'),

                    'element': link,

                    'title': link.get_attribute('title') or ''

                })

        

        return links

```


## 性能优化与最佳实践


```python

import time

from functools import lru_cache

from selenium.common.exceptions import StaleElementReferenceException


class OptimizedTextLocator:

    """优化性能的文本定位器"""

    

    def __init__(self, driver):

        self.driver = driver

        self.cache = {}

    

    @lru_cache(maxsize=100)

    def locate_with_cache(self, text: str, element_type: str = "*") -> List[WebElement]:

        """

        带缓存的元素定位

        """

        cache_key = f"{text}_{element_type}"

        

        if cache_key in self.cache:

            # 验证缓存元素是否仍然有效

            valid_elements = []

            for element in self.cache[cache_key]:

                try:

                    # 检查元素是否仍然在DOM中

                    element.is_displayed()

                    valid_elements.append(element)

                except StaleElementReferenceException:

                    continue

            

            if valid_elements:

                return valid_elements

        

        # 缓存未命中或失效,重新查找

        xpath = f"//{element_type}[contains(text(), '{text}')]"

        elements = self.driver.find_elements(By.XPATH, xpath)

        

        # 更新缓存

        self.cache[cache_key] = elements

        

        return elements

    

    def batch_locate(self, texts: List[str], 

                    element_type: str = "*") -> dict:

        """

        批量定位多个文本

        """

        results = {}

        

        # 构建组合XPath提高效率

        xpath_conditions = " or ".join(

            [f"contains(text(), '{text}')" for text in texts]

        )

        

        xpath = f"//{element_type}[{xpath_conditions}]"

        

        try:

            all_elements = self.driver.find_elements(By.XPATH, xpath)

            

            # 按文本分组

            for element in all_elements:

                element_text = element.text

                for text in texts:

                    if text in element_text:

                        if text not in results:

                            results[text] = []

                        results[text].append(element)

        

        except Exception as e:

            print(f"批量定位失败: {e}")

        

        return results

    

    def smart_wait_for_element(self, text: str, 

                              timeout: int = 30,

                              element_type: str = "*") -> Optional[WebElement]:

        """

        智能等待元素出现

        """

        start_time = time.time()

        poll_interval = 0.5

        

        while time.time() - start_time < timeout:

            try:

                elements = self.locate_with_cache(text, element_type)

                if elements:

                    # 返回第一个可见元素

                    for element in elements:

                        if element.is_displayed():

                            return element

                

                time.sleep(poll_interval)

                

            except Exception:

                time.sleep(poll_interval)

        

        return None

    

    def find_best_match(self, target_text: str,

                       candidates: List[WebElement]) -> Optional[WebElement]:

        """

        从候选元素中找到最佳匹配

        """

        if not candidates:

            return None

        

        scored_candidates = []

        

        for candidate in candidates:

            element_text = candidate.text.strip()

            

            # 计算匹配分数

            score = 0

            

            # 完全匹配得分最高

            if element_text == target_text:

                score += 100

            

            # 包含目标文本

            if target_text in element_text:

                score += 50

            

            # 文本长度相似

            length_diff = abs(len(element_text) - len(target_text))

            score += max(0, 50 - length_diff)

            

            # 可见性加分

            if candidate.is_displayed():

                score += 20

            

            # 交互元素加分

            if candidate.tag_name in ['button', 'a', 'input']:

                score += 10

            

            scored_candidates.append((score, candidate))

        

        # 按分数排序

        scored_candidates.sort(key=lambda x: x[0], reverse=True)

        

        return scored_candidates[0][1] if scored_candidates else None


class RobustTextLocator:

    """鲁棒性强的文本定位器"""

    

    def __init__(self, driver):

        self.driver = driver

    

    def locate_with_retry(self, text: str, 

                         max_retries: int = 3,

                         element_type: str = "*") -> Optional[WebElement]:

www.at.zizhitong.cn>www.dz.zizhitong.cn

        """

        带重试机制的元素定位

        """

        for attempt in range(max_retries):

            try:

                xpath = f"//{element_type}[contains(text(), '{text}')]"

                elements = self.driver.find_elements(By.XPATH, xpath)

                

                if elements:

                    # 返回第一个可见元素

                    for element in elements:

                        try:

                            if element.is_displayed():

                                return element

                        except StaleElementReferenceException:

                            continue

                

                # 如果没有找到,等待后重试

                if attempt < max_retries - 1:

                    time.sleep(1 * (attempt + 1))  # 指数退避

                

            except Exception as e:

                print(f"定位尝试 {attempt + 1} 失败: {e}")

                if attempt < max_retries - 1:

                    time.sleep(1 * (attempt + 1))

        

        return None

    

    def locate_with_fallback(self, primary_text: str,

                            fallback_texts: List[str]) -> Optional[WebElement]:

        """

        使用后备文本定位

        """

        # 尝试主要文本

        element = self.locate_with_retry(primary_text)

        if element:

            return element

        

        # 尝试后备文本

        for fallback_text in fallback_texts:

            element = self.locate_with_retry(fallback_text)

            if element:

                return element

        

        return None

    

    def handle_dynamic_content(self, text: str,

                              element_type: str = "*") -> Optional[WebElement]:

        """

        处理动态生成的内容

        """

        # 等待元素出现

        element = self.smart_wait_for_element(text, element_type)

        

        if not element:

            # 尝试在iframe中查找

            iframes = self.driver.find_elements(By.TAG_NAME, "iframe")

            

            for iframe in iframes:

                try:

                    self.driver.switch_to.frame(iframe)

                    element = self.locate_with_retry(text, element_type)

                    

                    if element:

                        self.driver.switch_to.default_content()

                        return element

                    

                    self.driver.switch_to.default_content()

                    

                except Exception:

                    self.driver.switch_to.default_content()

                    continue

        

        return element

    

    def smart_wait_for_element(self, text: str,

                              element_type: str = "*",

                              timeout: int = 30) -> Optional[WebElement]:

        """

        智能等待元素出现

        """

        start_time = time.time()

        

        while time.time() - start_time < timeout:

            # 检查主文档

            element = self.locate_with_retry(text, 1, element_type)

            if element:

                return element

            

            # 检查iframe

            iframes = self.driver.find_elements(By.TAG_NAME, "iframe")

            for iframe in iframes:

                try:

                    self.driver.switch_to.frame(iframe)

                    element = self.locate_with_retry(text, 1, element_type)

                    self.driver.switch_to.default_content()

                    

                    if element:

                        return element

                        

                except Exception:

                    self.driver.switch_to.default_content()

                    continue

            

            time.sleep(0.5)

        

        return None

```


## 测试与验证框架


```python

import unittest

from selenium import webdriver

from selenium.webdriver.common.by import By


class TextLocatorTests(unittest.TestCase):

    """文本定位器测试用例"""

    

    @classmethod

    def setUpClass(cls):

        cls.driver = webdriver.Chrome()

        cls.driver.implicitly_wait(10)

    

    @classmethod

    def tearDownClass(cls):

        cls.driver.quit()

    

    def setUp(self):

        self.locator = AdvancedTextLocator(self.driver)

    

    def test_exact_text_match(self):

        """测试精确文本匹配"""

        self.driver.get("https://example.com")

        

        # 查找包含特定文本的元素

        elements = self.locator.find_by_regex("Example")

        self.assertGreater(len(elements), 0)

        

        # 验证元素文本

        for element in elements:

            self.assertIn("Example", element.text)

    

    def test_partial_text_match(self):

        """测试部分文本匹配"""

        self.driver.get("https://example.com")

        

        # 使用contains方法

        elements = self.driver.find_elements(

            By.XPATH, "//*[contains(text(), 'Example')]"

        )

        self.assertGreater(len(elements), 0)

    

    def test_fuzzy_matching(self):

        """测试模糊匹配"""

        self.driver.get("https://example.com")

        

        elements = self.locator.find_by_fuzzy_match("Exampel", 0.7)

        self.assertGreater(len(elements), 0)

    

    def test_dynamic_text_handling(self):

        """测试动态文本处理"""

        handler = DynamicTextHandler(self.driver)

        

        # 测试等待文本出现

        success = handler.wait_for_text("Example", timeout=5)

        self.assertTrue(success)

    

    def test_context_extraction(self):

        """测试上下文提取"""

        self.driver.get("https://example.com")

        

        handler = DynamicTextHandler(self.driver)

        results = handler.extract_text_with_context("Example", 20)

        

        self.assertGreater(len(results), 0)

        for result in results:

            self.assertIn("Example", result['context'])

    

    def test_login_page_elements(self):

        """测试登录页面元素定位"""

        self.driver.get("https://example.com/login")

        

        login_locator = LoginPageLocator(self.driver)

        elements = login_locator.locate_login_elements()

        

        # 验证找到了一些登录相关元素

        self.assertGreater(len(elements), 0)

    

    def test_data_extraction(self):

        """测试数据提取"""

        self.driver.get("https://example.com/data")

        

        extractor = DataExtractor(self.driver)

        

        # 测试表格数据提取

        table_data = extractor.extract_table_data((By.TAG_NAME, "table"))

        self.assertIsInstance(table_data, list)

        

        # 测试键值对提取

        key_value_pairs = extractor.extract_key_value_pairs()

        self.assertIsInstance(key_value_pairs, dict)


def performance_test():

    """性能测试"""

    driver = webdriver.Chrome()

    

    try:

        driver.get("https://example.com")

        

        # 测试各种定位方法的性能

        test_cases = [

            ("XPath精确匹配", lambda: driver.find_elements(

                By.XPATH, "//*[text()='Example Domain']")),

            ("XPath部分匹配", lambda: driver.find_elements(

                By.XPATH, "//*[contains(text(), 'Example')]")),

            ("正则表达式", lambda: AdvancedTextLocator(driver)

                .find_by_regex("Example.*Domain")),

            ("模糊匹配", lambda: AdvancedTextLocator(driver)

                .find_by_fuzzy_match("Example Domain", 0.9)),

        ]

        

        results = {}

        iterations = 100

        

        for name, test_func in test_cases:

            start_time = time.time()

            

            for _ in range(iterations):

                try:

                    test_func()

                except:

                    pass

            

            end_time = time.time()

            avg_time = (end_time - start_time) * 1000 / iterations

            

            results[name] = avg_time

            print(f"{name}: 平均 {avg_time:.2f} ms")

        

        return results

        

    finally:

        driver.quit()


if __name__ == "__main__":

    # 运行单元测试

    unittest.main(verbosity=2)

    

    # 运行性能测试

    print("\n性能测试结果:")

    performance_results = performance_test()

```


## 总结与最佳实践


本文全面探讨了Python中基于文本内容定位网页元素的各种技术和方法。从基础的XPath定位到高级的模糊匹配和正则表达式,从简单的文本搜索到复杂的数据提取,我们提供了丰富的代码示例和实践经验。


### 关键要点总结:


1. **灵活使用XPath**:掌握`contains()`、`starts-with()`等函数,结合属性选择器提高定位精度

2. **多种匹配策略结合**:结合精确匹配、模糊匹配和正则表达式,适应不同的文本定位需求

3. **处理动态内容**:使用等待机制和重试策略,确保在动态页面中可靠定位元素

4. **性能优化**:合理使用缓存、批量操作和智能等待,提高定位效率

5. **异常处理**:实现鲁棒的错误处理和备用定位策略,确保代码稳定性


在实际应用中,建议根据具体场景选择最合适的定位策略,并充分考虑页面的动态性和复杂性。通过合理的架构设计和代码组织,可以构建出既高效又可靠的文本定位解决方案。


来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/19545/viewspace-3105623/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2015-11-01

  • 博文量
    895
  • 访问量
    945558