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.
Related
With XPath, how would you search for elements that only contain another specific element? For example, what expression would result in getting all <p> tags that contain <strong> elements within them?
<p>This is some text that <strong>contains another HTML element</strong></p>
In XPath you use square brackets to filter. It is called the predicate. See I.e. this tutorial .
To select all p’s with a element strong you use
//p[strong]
If you want to find all p’s with only the element strong and no other elements, you add
//p[strong][count(*)=count(strong)]
The * stands for any element.
If, as in your example , you only interested in p’s with the strong element being the last child node you use
//p[strong[not(following-sibling::node())]]
Predicates are the way to go.
what expression would result in getting all p tags that contain elements within them?
If you only want to select p elements with direct strong children, you can use p[strong], if you're looking for any descendants, use p[descendant::strong]. In both cases the context node has to be at the level of the p elements.
I want to ignore elements from my xpath query that are not visible, either because they or any of their ancestors are display:none.
I tried the below, but it's including elements which non displayable ancestors
//button[
not(contains(#style,'display:none'))
and
not(ancestor::div[contains(#style,'display:none')])
and
contains(translate(., 'ABCDEFGHIJKLMNÑOPQRSTUVWXYZ', 'abcdefghijklmnñopqrstuvwxyz'), 'acept')
]
"
Try to apply this
//*[not(ancestor-or-self::*[contains(#style,'display: none;')])]
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
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")]
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.