This is a inverse question of
$driver.get_element_by_xpath(....)
Background
I have a series of dynamically generated pages to parse. The target element doesn't have a good locator, xpath, text, or id. However, the neighbor element has a unique text to match. My plan is to locate the neighbor element and use its xpath to come up with the target xpath.
$neighbor_element = $driver.get_element_by_text("unique text"); # or some other way
$neighbor_xpath = $neighbor_element.xpath; # this step is the question
$target_xpath = modify($neighbor_xpath); # this is my function
$target_element = $driver.get_element_by_xpath($target_xpath);
I have searched around. cannot find a function or method to get xpath from an element, Perl or Python.
Update
I apologize that I cannot post the example page because it is owned by a company, but I hope the question is straightforward enough. When I use chrome devtools to inspect, I see the xpath are related
neighbor xpath = //*[@id="lable_ni.dynmic_string 123456"]/lable/span[2]
target xpath = //*[@id="dynmic_string 123456"]
CodePudding user response:
Using Selenium Python clients incase you are able to locate the neighbor element as:
neighbor_element = driver.find_element(By.XPATH, "unique text")
Depending upon the location of the target element with respect to neighbor_element
you can use either of the following locator strategies:
Incase the target_element is a
<span>
element located left to the neighbor_element:target_element = driver.find_element(locate_with(By.TAG_NAME, "span").to_left_of(neighbor_element))
You have to include the following imports:
from selenium.webdriver.support.relative_locator import locate_with
CodePudding user response:
Find the parent of that "known-neighbor" then their select child, or children that are next in the list to the known one.
I am a little confused by what is known about the target. The original text says that
The target element doesn't have a good locator, xpath, text, or id.
but the edit shows
target xpath = //*[@id="dynmic_string 123456"]
Since there's got to be some way to tell I'll take it as a fact that something is known about the target. Another way would be to get all children (so, all siblings) and browse through them and locate the ones next to the known one.
Here is an example with Perl. For this very page, take that known-neighbor to be a <p>
with some text and find the target which is a sibling <p>
and which has some given text in it. (As an equivalent of finding a sibling with that id
given in the question's edit.)
use warnings;
use strict;
use feature 'say';
use Selenium::Chrome;
my $url_SO = q(https://stackoverflow.com/questions/71849162/)
. q(how-to-find-the-neighbour-element-of-an-active-element-)
. q(using-selenium-with-pytho);
my $drv = Selenium::Chrome->new( 'extra_capabilities' =>
{ 'goog:chromeOptions' => { args => [ 'headless' ] }} );
$drv->get($url_SO);
say "\nPage title: ", $drv->get_title, "\n";
# Our "neighbor": <p> with text 'This is...'
# Get parent with: 'element-spec/..'
my $parent = eval {
$drv->find_element(
'//p[text()="This is a inverse question of"]/..') };
if ($@) { die "Error on <p>'s parent: $@" }
say "known-<p>'s parent tag: ", $parent->get_tag_name;
say "known-<p>'s parent text:";
say '-'x50, "\n", $parent->get_text, "\n", '-'x50;
# Target: <p> with text that contains word 'searched'
my $tgt = eval {
$drv->find_child_element(
$parent, q(./p[contains(text(), 'searched')]) ) };
if ($@) { die "find-child error: $@" }
say "target text: ", $tgt->get_text;
This does as expected. I can't really post a closer match to the question because not much is given. If I misunderstood that that shown id
of the target is known then use find_child_elements on the $parent
and go down the list probing for the known neighbor. The target should be the one before or after (and you better know which :)
If the target isn't in fact a true sibling, but is rather a child of a further ascendant (than the immediate parent), the xpath expressoin can go up the hierarchy with the additional /../..
(etc).
This uses the (server-less) Selenium::Chrome, with methods in Selenium::Remote::Driver and Selenium::Remote::WebElement
(It should be possible to do the whole known-element->parent->child thing with a single XPath expression, or perhaps even look directly for siblings, I'll look once there's time.)