Combining XPath contains() Function with AND Operator: In-depth Analysis and Best Practices

Nov 10, 2025 · Programming · 16 views · 7.8

Keywords: XPath | contains() function | AND operator | text matching | node-set conversion | web automation

Abstract: This article provides a comprehensive exploration of combining XPath contains() function with AND operator, analyzing common error causes through practical examples and presenting correct XPath expression formulations. It explains node-set to string conversion mechanisms, compares differences across XPath versions, and offers various text matching strategies with performance optimization recommendations for developing more precise and efficient XPath queries.

Combining XPath contains() Function with AND Operator

In the domains of web automation and data scraping, XPath serves as a powerful XML path language widely used for locating and selecting HTML elements. The contains() function is a crucial tool for partial text matching, and when combined with logical operators, it can construct more precise query conditions. This article delves into the methods and considerations of combining contains() with AND operator through a typical problem scenario.

Problem Scenario Analysis

Consider the following HTML structure fragment:

<ul class="featureList">
<li><b>Type:</b> Clip Fan</li>
<li><b>Feature:</b> Air Moved: 65 ft. Amps: 1.1...</li>
<li><b>Model #: </b>CR1-0081-06</li>
<li><b>Item #: </b>N82E16896817007</li>
</ul>

The developer's goal is to select <ul> elements with class="featureList" that contain <li> child elements with text content including "Model". The initial XPath expression attempt was:

//ul[@class='featureList' and contains(li, 'Model')]

However, this expression fails to correctly match the target elements due to insufficient understanding of the contains() function's parameter handling mechanism.

Error Cause Analysis

In XPath 1.0, when the first argument of the contains() function is a node-set, the system converts this node-set to a string. The conversion rule is: only the string value of the first node in the node-set is considered, while other nodes are ignored.

In the given problem, the li in contains(li, 'Model') is actually a node-set containing all <li> child elements under the <ul> element. During conversion, the system only takes the text content of the first <li> element (i.e., "Type: Clip Fan") for matching. Since this text doesn't contain "Model", the entire expression returns false.

If the first <li> element coincidentally contains the target text, such as:

//ul[@class='featureList' and contains(li, 'Type')]

This expression would correctly match, but this is merely coincidental and not the intended query logic.

Correct Solution

To resolve this issue, it's essential to ensure that the contains() function checks all <li> child elements under the <ul> element. The correct XPath expression should be:

//ul[@class='featureList' and ./li[contains(.,'Model')]]

The core improvements in this expression include:

XPath Version Differences and Compatibility Considerations

Different XPath versions exhibit important differences in handling the contains() function:

XPath 1.0

In XPath 1.0, the contains() function can accept a node-set as the first argument, but only the first node is considered during conversion. This characteristic can lead to unexpected matching results in certain scenarios.

XPath 2.0+

Starting from XPath 2.0, if the first argument of the contains() function contains multiple items, the system throws an error. This means expressions like //*[contains(text(), 'target')] cannot work properly in XPath 2.0+ environments.

To maintain cross-version compatibility, it's recommended to use more explicit expressions:

//*[text()[contains(., 'target string')]]

This formulation works correctly in both XPath 1.0 and 2.0+ because it explicitly specifies the text nodes to be checked.

Advanced Text Matching Techniques

Beyond the basic contains() function, XPath provides other text matching functions to meet various query requirements:

Exact Match vs Partial Match

Exact match uses text()='value', requiring the element's text content to exactly match the specified value:

//h1[text()='Welcome']

Partial match uses contains(text(), 'value'), matching as long as the text contains the specified substring:

//h1[contains(text(), 'Welcome')]

Prefix Match and Whitespace Handling

Prefix match uses starts-with(text(), 'value'):

//*[starts-with(text(), 'Welcome')]

Whitespace normalization uses normalize-space(text()), which removes leading and trailing whitespace and collapses consecutive spaces into single spaces:

//button[normalize-space(text()) = 'Submit']

Case-Insensitive Matching

XPath is case-sensitive by default, but case-insensitive matching can be achieved using the translate() function:

//*[translate(text(), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz') = 'submit']

Performance Optimization Recommendations

In practical applications, performance optimization of XPath expressions is crucial:

Practical Application Example

The following Python code demonstrates the practical implementation of correctly using XPath contains() with AND operator:

from lxml import etree

html_content = """
<ul class="featureList">
<li><b>Type:</b> Clip Fan</li>
<li><b>Model #: </b>CR1-0081-06</li>
</ul>
"""

dom = etree.HTML(html_content)

# Correct query approach
correct_result = dom.xpath("//ul[@class='featureList' and ./li[contains(.,'Model')]]")
print(f"Number of correctly matched elements: {len(correct_result)}")

# Incorrect query approach (for comparison only)
wrong_result = dom.xpath("//ul[@class='featureList' and contains(li, 'Model')]")
print(f"Number of incorrectly matched elements: {len(wrong_result)}")

The execution results will show that the correct expression successfully matches the target elements, while the incorrect expression fails to match.

Conclusion

The combination of XPath contains() function with AND operator is a common requirement in web automation and data scraping. The key lies in understanding the node-set to string conversion mechanism and compatibility requirements across different XPath versions. By using explicit path expressions like ./li[contains(.,'Model')], developers can ensure query logic accuracy and cross-version compatibility. Combined with other text matching functions and performance optimization techniques, developers can create more precise and efficient XPath query expressions.

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.