Package: xpath-helper
Build complicated XPath queries without the hassle - JavaScript & PythonA chainable API to build complex XPath queries along the differentXPath axes. Available both in Python and JavaScript.Documentation— Consult the quick start guide and the online documentation.Installationxpath-helperrequires python 3.5+ and can be installed using pip:pipinstallxpath-helperQuick-startYou can chain method call on the differentXPath axesand easily add filters.fromxpath_helperimportxh,filter# Finds a paragraph <p> containing a CSS class 'very-nice-p'p=xh.get_element_by_tag('p',filter.attribute_contains('class','very-nice-p'))str(p)# "//p[contains(@class, 'very-nice-p')]"# Finds the paragraph that is following the above onenext_p=p.get_following_sibling_by_tag('p')str(next_p)# "//p[contains(@class, 'very-nice-p')]/following-sibling::p"# Finds the modal containing a button with text "Register"modal=xh.get_element(filter.value_equals('Register')).get_ancestor(filter.attribute_equals('class','modal'))str(modal)# "//*[text() = 'Register']/ancestor::*[@class='modal']"# An elaborated filter with a boolean expressionli=xh.get_element_by_tag("li",filter.and_operator(filter.or_operator(filter.value_contains("JavaScript"),filter.value_contains("Python")),filter.has_attribute("data-description")))str(li)# "//li[((text()[contains(., 'JavaScript')] or text()[contains(., 'Python')]) and @data-description)]"ChainingXPath natively lets your build complex queries chaining them along its different axes. Read thisarticle to understand the different XPath axes.This library let you do exactly the same by chaining method calls along the different axes:descendantaliased aselement,descendant-or-self,child,parent,ancestor,ancestor-or-self,preceding,preceding-sibling,following,following-sibling.For each axis, xpath-helper provides 3 methods, like for instanceget_element(filter),get_element_by_tag(tag, filter),get_element_by_svg_tag(svgTag, filter)for thedescendantaxis aliased aselement.The complete Python API can be foundhere.fromxpath_helperimportxh,filter# Finds an element into the page, moves to its parent,# finds a brother node of the parent positioned after it.el=xh.get_element_by_tag('p',filter.attribute_contains('class','very-nice-p')).get_parent().get_following_sibling_by_tag('p')str(el)# "//p[contains(@class, 'very-nice-p')]/../following-sibling::p"# Finds an element into the page, moves to its ancestor# containing 'very-nice-p' ass CSS class,# finds a brother node of the ancestor positioned before it.el=xh.get_element_by_tag('p',filter.attribute_contains('class','very-nice-p')).get_ancestor_by_tag('div').get_preceding_sibling(filter.has_attribute('data-foo-bar'))str(el)# "//p[contains(@class, 'very-nice-p')]/../following-sibling::p//p[contains(@class, 'very-nice-p')]/ancestor::div/preceding-sibling::*[@data-foo-bar]"It is also possible to keep a relative path in a variable and re-use it after.fromxpath_helperimportxh,filter# Stores the path of a modal windowmodal=xh.get_element(filter.attribute_contains('class','modal'))# Finds the Submit button inside the modal windowsubmit_button=modal.get_element_by_tag('button',filter.value_equals('Submit'))# Finds the Cancel button inside the modal windowcancel_button=modal.get_element_by_tag('button',filter.value_equals('Cancel'))FiltersTo select elements more precisely you can add filters:on attributes, onelement values,element position, and combining them with conditional operators:and_operator(...),or_operator(...), andnot_operator(...).The complete filter API can be foundhere.AttributesFind below a few examples of filters on attributes.fromxpath_helperimportxh,filter# Looks for an element that has a class attribute equals to 'foo'el=xh.get_element(filter.attribute_equals('class','foo'))# Looks for an element that has a class attribute containing 'bar'el=xh.get_element(filter.attribute_contains('class','bar'))# Looks for an element that has the attribute 'alt'img=xh.get_element_by_tag('img',filter.has_attribute('alt'))# Looks for all the li element with a data-attribute superior to 3li=xh.get_element_by_tag('li',filter.attribute_greater_than('data-index',3))ValuesFind below a few examples of filters on node values.fromxpath_helperimportxh,filter# Looks for a button whose text is 'Submit'button=xh.get_element_by_tag('button',filter.value_equals('Submit'))# Looks for an element whose text contains 'foobar'el=xh.get_element(filter.value_contains('foobar'))# Looks for all the li element with a value superior to 3li=xh.get_element_by_tag('li',filter.value_greater_than(3))PositionFind below a few examples of filters on node position.fromxpath_helperimportxh,filter# Looks for the first li element in ul listfirst=xh.get_element_by_tag('ul').get_element_by_tag('li',filter.get_first())# Looks for the first li element in ul listlast=xh.get_element_by_tag('ul').get_element_by_tag('li',filter.get_last())# Looks for the third li element in ul listthird=xh.get_element_by_tag('ul').get_element_by_tag('li',filter.get(3))Conditional expressionFind below a few examples of filters with conditional expression.fromxpath_helperimportxh,filter# Finds an element that has a CSS class 'a-link' and contains an attribute hrefel=xh.get_element(filter.attribute_contains('class','a-link').and_operator(filter.has_attribute('href')))str(el)# "//*[contains(@class, 'a-link') and (@href)]"# Finds an element that has a CSS class 'foo' or a CSS class 'bar'el=xh.get_element(filter.attribute_contains('class','foo').or_operator(filter.attribute_contains('class','bar')))str(el)# "//*[contains(@class, 'foo') or (contains(@class, 'bar'))]"# Builds a complex logical expression combining and & orel=xh.get_element(filter.and_operator(filter.or_operator(filter.value_contains("JavaScript"),filter.value_contains("Pyhton")),filter.value_contains("package")))str(el)# "//*[((text()[contains(., 'JavaScript')] or text()[contains(., 'Pyhton')]) and text()[contains(., 'package')])]"SVGNavigating into SVG elements from an HTML file can be tricky with XPath, that is why a subset of functions have been added. They are all ending with...by_svg_tagand can be used as below.fromxpath_helperimportxh,filter# Stores the path of a modal windowpath=xh.get_element_by_svg_tag('path',filter.attribute_equals('d','M 310 130 L 90 130 L 90 183.63'))str(path)# "//*[local-name() = 'path'][@d='M 310 130 L 90 130 L 90 183.63']"# Finds the Submit button inside the modal windowg=xh.get_element_by_svg_tag('path',filter.attribute_equals('id','id-path')).get_ancestor_by_svg_tag('g')str(g)# "//*[local-name() = 'path'][@id='id-path']/ancestor::*[local-name() = 'g']"