Looking for 2 things in 2 different Xpaths - Python Selenium - python

I am working on a bot for a website and it requires a color and keyword to find the item. I am using selenium to look for the item from the keyword and then pick a color option (some items on the website, provide the item in multiple colors). I am having trouble looking for both the keyword and color at the same time, and then after choosing the correct colored version of the item from the user's color and keyword input. I want it to select on that option.
Formula I am trying to make in Python:
If the first Xpath(keyword) is found and the 2nd Xpath(color) is found
Then select on the item that contains those 2 properties.
This is the current code I have:
Item = driver.find_element_by_xpath('//*[contains(text(), "MLK")]' and contains ("Black")]')
if (item != None):
actions.moveToElement(item).click()
I've tried the code above and it doesn't work.
Here are the 2 pieces of code that I want to merge to find the item:
driver.find_element_by_xpath('//a[contains(text(), "MLK")]')
driver.find_element_by_xpath('//a[contains(text(), "Black")]')
The keyword is called MLK
The Color is called Black
After Merging, I want to find that Exact Element (Called MLK, Color version = Black)
This combined item should be clicked on, I only know to use .click()
If a better way, please let me know.
The website I am using to make a bot for: supremenewyork.com
The item I am using as an example, to pick a certain color (It's the Sweatshirt with MLK on it): http://www.supremenewyork.com/shop/all/sweatshirts

It took me a second to realize that there are 3 A tags for each shirt... one for the image, one for the name of the shirt, and one for the color. Since the last two A tags are the ones you are wanting to text search, you can't look for both strings in the same A tag. I've tested the XPath below and it works.
//article[.//a[contains(.,'MLK')]][.//a[.='Black']]//a
ARTICLE is the container for the shirt. This XPath is looking for an ARTICLE tag that contains an A tag that contains 'MLK' and then another A tag that contains 'Black' then finds the A tags that are descendants of the ARTICLE tag. You can click on any of them, they are all the same link.
BTW, your code has a problem. The first line below will throw an exception if there is no match so the next line will never be reached to test for None.
Item = driver.find_element_by_xpath('//*[contains(text(), "MLK")]' and contains ("Black")]')
if (Item != None):
actions.moveToElement(item).click()
A better practice is to use .find_elements() (plural) and check for an empty list. If the list is empty, that means there was no element that matched the locator.
Putting the pieces together:
items = driver.find_elements_by_xpath("//article[.//a[contains(.,'MLK')]][.//a[.='Black']]//a")
if items:
items[0].click()
I'm assuming you will be calling this code repeatedly so I would suggest that you put this in a function and pass the two strings to be searched for. I'll let you take it from here...

Try union "|" operator to combine two xpath.
Example:-
//p[#id='para1'] | //p[#id='para2']
('//a[contains(text(), "MLK")]' | '//a[contains(text(), "Black")]')

You can use a full XPath to select the item you want based on two conditions, you just need to start from a parent node and then apply the conditions on the child nodes:
//div[contains(./h1/a/text(), "MLK") and contains(./p/a/text(), "Black")]/a/#href
You first need to select the element itself, after that you need to get the attribute #href from the element, something like this:
Item = driver.find_element_by_xpath('//div[contains(./h1/a/text(), "MLK") and contains(./p/a/text(), "Black")]/a')
href = Item .get_attribute("href")

Related

How to differentiate between elements that contain the same text Python Selenium

I'm trying to take user input in order to navigate through a website. I'm trying to identify elements in the webpage based on the users input, however sometimes the string the user inputs will match multiple elements and therefore I'll click on the wrong one.
search = input("Enter something to search for")
elem = browser.find_elements_by_xpath(f'//span[text()="{search}"]')
Are there any tips to identify elements in a dynamic enviroment? I was thinking perhaps I could identify the element by two criteria - such as it matching the text (as above), but also by an attribute the other two elements don't have?
As it's dynamic there aren't many set attributes that I can base the search off (I won't know the class name upfront, or the id). The only thing I can think of to add on top of the string matching is the text of the outer class it will be in, as this is consistent across all the searches I'll conduct but I'm not sure if this is possible?
Any tips in identifying elements in a dynamic enviroment are appreciated.
The xpaths for the three elements with exactly the same text are:
/html/body/div[3]/div/div/div[1]/div/div/div/div/div/div/div/div[2]/div/div[1]/li/button/span/ul/li/span[2]/span
/html/body/div[3]/div/div/div[1]/div/div/div/div/div/div/div/div[2]/div/div[2]/li[1]/a/span/ul/li[1]/span/span
/html/body/div[3]/div/div/div[1]/div/div/div/div/div/div/div/div[2]/div/div[2]/li[2]/a/span/ul/li[1]/span[1]/span
An example of one of the elements (they all look like this):
<span class="ccl-19882374e640f487 ccl-1daa0367dee37c3b">TEXT</span>
And an h4 "title" tag, that sits above some of the matching elements:
<h4 class="ccl-2a4b5924e2237093 ccl-21bead492ce4ada2 ccl-706ed5a06ead17d8 ccl-2b95b58eee016846">Categories</h4>
Screenshot of the problem below. When you search hotels for "London" you are given a list of multiple elements under a Locations heading and multiple elements under a Properties heading. I'd like to click the first element that appears under the properties heading. This is just one example and it will vary depending on what place the user searches for so there may not always be a known number of locations/properties returned - there may never be any locations returned!
Thanks
Maybe simply adding if statement will slightly help:
search = input("Enter something to search for")
elem = browser.find_elements_by_xpath(f'//span[text()="{search}"]')
if len(elem) > 1:
elem[0].click() # select the element you want to click from the list
If the elements in the list will contain some extra attributes you can filter them in that way:
search = input("Enter something to search for")
elem = browser.find_elements_by_xpath(f'//span[text()="{search}"]')
if len(elem) > 1:
elem.find_element_by_css_selector('type_of_selector[class="some_class"]')
Could you provide more details about those elements and we'll think about the solution. Regards!

PyWinAuto - Xpath like identification

I'm looking for way how to search objects in GUI according to xpath. part of identification of application
I have xpath as follows:
./pane[0]/pane[0]/pane[1]/pane[0]/pane[1]/pane[0]/pane[0]/pane[0]/text[0]/edit[0]
This should (if i have no problem there) point to selected EDIT in application element tree.
I tried to use this xpath to identify items like this
#app is the application under test, this is working correctly
top = app.top_window()
first = top.child_window(control_type="Pane")
print first #here is first problem: This will find all the child_windows, no just the direct children, without depth search (is it possible to just search particular direct childrens, without deeper search?)
first = top.child_window(control_type="Pane", ctrl_index=0)
#this is much better
second = first.child_window(control_type="Pane", ctrl_index=0)
print second
#this is working, i'm looking for [0] indexed Pane under first found element
third = second.child_window(control_type="Pane", ctrl_index=1)
print third
# we have another problem , the algorithm is depth first, so ctrl_index=1 is not referencing to '2nd child of element named second', but instead to the first element of first pane, founded under 2nd element. (I'm looking for wide first algorithm)
I don't have recursive function written by now, but maybe I'm going the wrong way.
So the question is, is there any way how to recover path to element in xpath like style?
Thanks
For the first case it should look so:
first = top.child_window(control_type="Pane", depth=2)
# a bit confusing, will bind to depth=1 in future major release.
For third case yes, ctrl_index is used before filtering by other criteria. If you need to apply search criteria first and then choose from small filtered list, there is another param suitable for your case: found_index.
It should be changed so:
third = second.child_window(control_type="Pane", found_index=1)

How to locate an element by class name and its text in python selenium

Hi I am trying to locate an element by its class name and the text that it contains
<div class="fc-day-number">15</div>
there are a bunch of fc-day-number on the page with different values, I need the one with for example 15.
I do
driver.find_element_by_class_name("fc-day-content")
but I also need it to be equal to 15 and I am stuck here, please help.
You can use xpath:
driver.find_element_by_xpath("//div[#class='fc-day-content' and text()='15']")
fc_day_contents = driver.find_elements_by_class_name("fc-day-content")
the_one_you_want = [x for x in fc_day_contents if "15" == x.text][0]
first line puts all elements with class name "fc-day-content" in a list ( also notice how its elementSSSSSSSSS with an S, this returns a list of all elements by_class_name, by_name, by_id or wahtever)
second line, goes through each element and looks to see if it has the text "15" as its text, and returns it as a (probably smaller) list
the [0] at the end of it, returns the first item in the list (you can remove it, if you want a list of all the ones that are "15" )
For things like this, prefer to use JavaScript:
els = driver.execute_script("""
return Array.prototype.slice.call(document.getElementsByClassName("fc-day-content"))
.filter(function (x) { return x.textContent === "15"; });
""")
assert len(els) == 1
el = els[0]
What this does is get all elements that have the class fc-day-content as the class. (By the way, your question uses fc-day-content and fc-day-number. Unclear which one you're really looking for but it does not matter in the grand scheme of things.) The call to Array.prototype.slice creates an array from the return value of getElementsByClassName because this method returns an HTMLCollection and thus filter is not available on it. Once we have the array, run a filter to narrow it to the elements that have 15 for text. This array of elements is returned by the JavaScript code. The assert is to make sure we don't unwittingly get more than one element, which would be a surprise. And then the element is extracted from the list.
(If you care about IE compatibility, textContent is not available before IE 9. If you need support for IE 8 or earlier, you might be able to get by with innerText or innerHTML or you could check that the element holds a single text node with value 15.)
I prefer not to do it like TehTris does (find_elements_by_class_name plus a Python loop to find the one with the text) because that method takes 1 round-trip between Selenium client and Selenium server to get all the elements of class fc-day-content plus 1 round-trip per element that was found. So if you have 15 elements on your page with the class fc-day-content, that's 16 round-trips. If you run a test through Browser Stack or Sauce Labs, that's going to slow things down considerably.
And I prefer to avoid an XPath expression with #class='fc-day-content' because as soon as you add a new class to your element, this expression breaks. Maybe the element you care about has just one CSS class now but applications change. You could use XPath's contains() function but then you run into other complications, and once you take care of everything, it becomes a bit unwieldy. See this answer for how to use it robustly.

[Python]Get a XPath Value from Steam and print it

I want to get an XPATH-Value from a Steamstoresite, e.g. http://store.steampowered.com/app/234160/. On the right side are 2 boxes. The first one contains Title, Genre, Developer ... I just need the Genre here. There is a different count on every game. Some have 4 Genres, some just one. And then there is another block, where the gamefeatures are listet (like Singleplayer, Multiplayer, Coop, Gamepad, ...)
I need all those values.
Also sometimes there is an image between (PEGI/USK)
http://store.steampowered.com/app/233290.
import requests
from lxml import html
page = requests.get('http://store.steampowered.com/app/234160/')
tree = html.fromstring(page.text)
blockone = tree.xpath(".//*[#id='main_content']/div[4]/div[3]/div[2]/div/div[1]")
blocktwo = tree.xpath(".//*[#id='main_content']/div[4]/div[3]/div[2]/div/div[2]")
print "Detailblock:" , blockone
print "Featureblock:" , blocktwo
This is the code I have so far. When I try it it just prints:
Detailblock: [<Element div at 0x2ce5868>]
Featureblock: [<Element div at 0x2ce58b8>]
How do I make this work?
xpath returns a list of matching elements. You're just printing out that list.
If you want the first element, you need blockone[0]. If you want all elements, you have to loop over them (e.g., with a comprehension).
And meanwhile, what do you want to print for each element? The direct inner text? The HTML for the whole subtree rooted at that element? Something else? Whatever you want, you need to use the appropriate method on the Element type to get it; lxml can't read your mind and figure out what you want, and neither can we.
It sounds like what you really want is just some elements deeper in the tree. You could xpath your way there. (Instead of going through all of the elements one by one and relying on index as you did, I'm just going to write the simplest way to get to what I think you're asking for.)
genres = [a.text for a in blockone[0].xpath('.//a')]
Or, really, why even get that blockone in the first place? Why not just xpath directly to the elements you wanted in the first place?
gtags = tree.xpath(".//*[#id='main_content']/div[4]/div[3]/div[2]/div/div[1]//a")
genres = [a.text for a in gtags]
Also, you could make this a lot simpler—and a lot more robust—if you used the information in the tags instead of finding them by explicitly walking the structure:
gtags = tree.xpath(".//div[#class='glance_tags popular_tags']//a")
Or, since there don't seem to be any other app_tag items anywhere, just:
gtags = tree.xpath(".//a[#class='app_tag']")

Get element inside current element using xpath

I have multiple questions inside which there are more than 1 options.
After selecting the required question element as question_element
I am unable to get the first text box inside this element. I used
question_element.find_elements_by_xpath("//textarea")
but it gives me list of all the elements with tag textarea in the whole webpage. I tried
question_element.find_elements_by_xpath("/textarea")
question_element.find_elements_by_xpath("./textarea")
but they didn't give any results. How do I get the first element with tag name textarea inside the question_element
There are two variants that work for search within already found element (not within the whole page):
question_element.find_elements_by_xpath(".//textarea")
Try like this
question_element.find_elements_by_xpath("//textarea[position()=1]")

Categories

Resources