58 lines
1.9 KiB
Python
58 lines
1.9 KiB
Python
"""Convert CSS selectors into XPath selectors"""
|
|
from cssselect.xpath import GenericTranslator
|
|
|
|
|
|
class ConvertibleToCssTranslator(GenericTranslator):
|
|
"""An implementation of :py:class:`cssselect.GenericTranslator` with
|
|
XPath output that more readily converts back to CSS selectors.
|
|
The simplified examples in https://devhints.io/xpath were used as a
|
|
reference here."""
|
|
|
|
def css_to_xpath(self, css, prefix="//"):
|
|
return super().css_to_xpath(css, prefix)
|
|
|
|
def xpath_attrib_equals(self, xpath, name, value):
|
|
xpath.add_condition("%s=%s" % (name, self.xpath_literal(value)))
|
|
return xpath
|
|
|
|
def xpath_attrib_includes(self, xpath, name, value):
|
|
from cssselect.xpath import is_non_whitespace
|
|
|
|
if is_non_whitespace(value):
|
|
xpath.add_condition(
|
|
"contains(%s, %s)" % (name, self.xpath_literal(value))
|
|
)
|
|
else:
|
|
xpath.add_condition("0")
|
|
return xpath
|
|
|
|
def xpath_attrib_substringmatch(self, xpath, name, value):
|
|
if value:
|
|
# Attribute selectors are case sensitive
|
|
xpath.add_condition(
|
|
"contains(%s, %s)" % (name, self.xpath_literal(value))
|
|
)
|
|
else:
|
|
xpath.add_condition("0")
|
|
return xpath
|
|
|
|
def xpath_class(self, class_selector):
|
|
xpath = self.xpath(class_selector.selector)
|
|
return self.xpath_attrib_includes(
|
|
xpath, "@class", class_selector.class_name
|
|
)
|
|
|
|
def xpath_descendant_combinator(self, left, right):
|
|
"""right is a child, grand-child or further descendant of left"""
|
|
return left.join("//", right)
|
|
|
|
|
|
def convert_css_to_xpath(css):
|
|
"""Convert CSS Selectors to XPath Selectors.
|
|
Example:
|
|
convert_css_to_xpath('button:contains("Next")')
|
|
Output => "//button[contains(., 'Next')]"
|
|
"""
|
|
xpath = ConvertibleToCssTranslator().css_to_xpath(css)
|
|
return xpath
|