xpath: find a node whose content has a provided string - xpath

I have some HTML like this:
<div> Make </div>
And I want to match it based on the fact that the content of the node contains the text "Make".
Put another way "Make" is a substring of the div node's content and I want to make such a match on this node using XPath.

The obvious solution would be
//div[contains(., 'Make')]
but this will find all divs that contain the string "Make" anywhere within their content, so not only will it find the example you've given in the question but also any ancestor div of that one, or any divs where that substring is buried deep in a descendant element.
If you only want cases where that string is directly inside the div with no other intervening elements then you'd have to use the slightly more complex
//div[text()[contains(., 'Make')]]
This is subtly different from
//div[contains(text(), 'Make')]
which would look only in the first text node child of the div, so it would find <div>Make<br/>Break</div> but not <div>Break<br/>Make</div>
If you want to allow for intervening elements other than div, then try
//div[contains(., 'Make')][not(.//div[contains(., 'Make'])]

Seems like this is what you are looking for: //div[contains(text(),'Make')]
If this will not work you can try: //div[contains(.,'Make')]. This will find all divs, which contain 'Make' in any attribute.

To find that node anywhere in the document, you would need this:
//div[contains(text(), "Make")]

Related

Get element name by containing text

I'm looking through HTML documents for the text: "Required". What I need to find is the element that holds the text. For example:
<p>... Required<p>
I would get to element name = p
However, it might not be in a <p> tag. It could be in any kind of tag, which is where this question differs from some of the other search text Stack Overflow questions.
Right now I'm using:
page.at(':contains("Required")')
but this only get me the full HTML element
The problem you have is the :contains pseudo class matches any element that has the searched for text anywhere in its descendants. You need to find the innermost element that contains such text. Since html is the ancestor of all elements, if the page contains the text anywhere then html will contain, and so that will be the first matching element.
I’m not sure you can achieve this with CSS, but you can use XPath like this:
page.at_xpath('//*[text()[contains(., "Required")]]')
This finds the first element node that has a text() node as a child that contains Required. When you have that node (if it exists) you can then call name on it to give the name of the element.
For CSS you can do:
page.at('[text()*="Required"]')
It's not real CSS though, or even a jQuery extra.
You should use CSS selectors:
page.css('p').text

Would like to use wildcard within an xpath

I have two links on a page that have the text 'London'. I want to choose the second one on the page, but I want to define the xpath in a way that it chooses by the parent div, but I want to use wildcard in case the link is moved.
So, the two xpaths are
//div[#id="first-id"]/div/div[2]/a[text()="London"]
//div[#id="second-id"]/div[2]/div[3]/div/a[text()="London"]
I want to use a wildcard and define the xpath within the parent div:
i.e. //div[#id="second-id"]/*/a[text="London"]
I already understand I can just use the full xpath and not have any wildcards, but I want to know if there's a way to do what I am proposing using xpath. I thought maybe contains() in some way would work but am not familiar enough with it.
To find the a element wherever it may appear within the div element, the descendant path is represented simply by //:
//div[#id="second-id"]//a[text="London"]

XPATH - grab content from DIV with nestled a text

Can anyone help me with this? I cannot grab the 'Blue Shoes' text from this div no matter what I try! Been over an hour now and still cannot work it out. Tried:
//div[#class='breadcrumbs']/text(
//div[#class='breadcrumbs']
//div[#class='breadcrumbs']/div
Nothing seems to work. Any help MUCH appreciated.
<div class="breadcrumbs">Home/Blue Shoes</div>
</div>
//div[#class='breadcrumbs']/text()
should give you what you need in this case - it will select the set of all text nodes that lie directly under the breadcrumbs div. if you want to specifically target the one at the end (e.g. if there's more than two levels of breadcrumb and there's another text node for, say, a slash between two a elements) then the slightly more specific
//div[#class='breadcrumbs']/text()[last()]
may work better.
If this doesn't work then there are two other possibilities I can think of. Firstly, the HTML DOM uses upper case for element names, and since XPath is case-sensitive you may find you need //DIV instead of //div. Or maybe there's a namespace issue - if your document has an xmlns="..." on the root element then that puts your div elements in a namespace, and unprefixed names in xpath refer to nodes in no namespace. To select namespaced nodes you have to bind a prefix to the corresponding namespace URI and then use the prefix in your expressions (//xhtml:div). Exactly how you go about mapping prefixes depends on what library/tool/language you're using to execute the xpath queries.

Checking the HTML structure with XPATH, any count of nodes

I want to check the structure of some html piece of markup, just checking the structure.
For example I need to check that SOMEWHERE in <list-item-canvas> tag is <image name='category-pic'> tag.
I write:
//div[#class='list-item-canvas'][1]/*/img[#name='category-pic']
That's working if <img> is a second node after any ('*') node in the hierarchy, BUT if I have <img> somewhere deep-deep in the structure, AND I do not want to care about the level hierarchy how then I should write my xpath-query? I would think that instead '*' I might write '**' but I can not..
Is it possible?
Use:
(//div[#class='list-item-canvas'])[1]//img[#name='category-pic']
This selects any img the string value of whose name attribute is 'category-pic' and that is a descendant of the first (in document order) div the string value of whose class attribute is 'list-item-canvas'.
Do note the bracets surrounding the subexpression:
(//div[#class='list-item-canvas'])[1]
this is quite different from:
//div[#class='list-item-canvas'][1]
the latter selects every div element in the document that is the first div child of its parent -- and there may be potentially more than one such elements.
Do this:
//div[#class='list-item-canvas'][1]//img[#name='category-pic']
The // before img lets you find any descendant of the div that is an img, instead of just children or grandchildren of the div.
Also are you sure you want the [1] there? It may not be doing what you think.

XPath intersection of two sets

I need to extract all links from a html document having text as the inner element and not a reference to an image. Basically I would like to do a doc.select("//a/attribute::href") for all elements in a tree where doc.select("//a/text()") returns anything. Thanks!
Well you can write conditions in XPath in a predicate in square brackets, e.g. //a[text()]/#href selects the href attributes of all link (a) elements that have at least one text node child. Or if you want to make sure there is no img child element in the link you can use e.g. //a[not(img)]/#href.

Resources