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!
Related
I have a page with a list of cards with information.
The XPATHS of each cards are:
self.automatic_payments_cards_list = (By.XPATH , '//*[#id="page-inner"]/div/div/main/lseg-gateway-billing-payment-line-info')
I'm trying to get the text of a specific elements for every card in the page.
//*[#id="page-inner"]/div/div/main/lseg-gateway-billing-payment-line-info[1]/lseg-card/div/lseg-card-container/ng-transclude/div/div[4]/div/div[3]
//*[#id="page-inner"]/div/div/main/lseg-gateway-billing-payment-line-info[2]/lseg-card/div/lseg-card-container/ng-transclude/div/div[4]/div/div[3]
//*[#id="page-inner"]/div/div/main/lseg-gateway-billing-payment-line-info[3]/lseg-card/div/lseg-card-container/ng-transclude/div/div[4]/div/div[3]
I know that with this code i get all the text on each card
for i in range(len(self.driver.find_elements(*self.automatic_payments_cards_list))):
print(self.driver.find_element(*self.automatic_payments_cards_list)[i].text)
But i don't want to get all the text on the cards, only the text on this specifics XPATHS
//*[#id="page-inner"]/div/div/main/lseg-gateway-billing-payment-line-info[**X**]**/lseg-card/div/lseg-card-container/ng-transclude/div/div[4]/div/div[3]**
Can you guys guide me in finding a solution to this?
The best way to actually achieve this is by using find element_by_xpath on the element.
all_card_els = driver.find_elements_by_xpath('//*[#id="page-inner"]/div/div/main/lseg-gateway-billing-payment-line-info')
for card_el in all_card_els:
specific_el_within_card = card_el.find_element_by_xpath('.//lseg-card/div/lseg-card-container/ng-transclude/div/div[4]/div/div[3]')
The . at the starting of the xpath is essential to make sure that the search is within the selected el, without the . you will always end up getting the first el which matches this xpath on the page. You can now use specific_el_within_card however way you like inside the loop, or append it to an external list.
PS: You can access the text via specific_el_within_card.text() as you mentioned you wanted to extract info for each card.
It is simple.:-) Transfer your xpath as dynamical string and pass it, like you do in a loop for e.g.
parent_locator_String=string1+ iterator + string2
"//*[#id="page-inner"]/div/div/main/lseg-gateway-billing-payment-line-info["+i+"]/lseg-card/div/lseg-card-container/ng-transclude/div/div[4]/div/div[3]"
I am working on a script that aims "taking all of the entries which are written by users" under a specific title in a website(In this case, the title is "python(programlama dili"). I would like to read the number which shows the current number of pages under this specific title.
The reason behind reading the number of this elements is that number of pages can increase at the time when we run the script due to increasing number of entries by users. Thus, I should take the number which exist within the element via script.
In this case, I need to read "122" as the value and assign it to a int variable . I use Selenium to take all entries and Firefox web driver.
It Would be better if you try to access it using the xpath.
Try and get the value attribute of the element, you've mentioned you can find the element using xpath so you can do the following.
user_count = element.get_attribute('value')
If that gets you the number (as a string) then you can just convert to an int as usual
value = int(user_count)
First pick the selector .last and then you can extract the reference of that. Don't forget to split the reference.
my_val = driver.find_element_by_css_selector(".last [href]").split("p=")[1]
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")
I saw a question like this about an XML doc but I have an HTML doc and want to know if it works the same.
I'm trying to loop through many HTML pages and determine if there are any instances of a span element that contains the text "text":
//*[#id="all_totals"]/div[1]/div/span[7]//span[text()="Export"]
link to image of HTML: http://f.cl.ly/items/290L2v373y3c3P050x2m/Image%202016-05-05%20at%202.59.25%20PM.png
In the above Xpath, the parent element I want ends at the second "div". I added the rest ("/span[7]...") as the specific element I want to grab. I want to check if any of the spans contained in the parent element contain the text "text" because, if any of them do, the span element I would want would be [6] instead of [7] (the "text" span is an occasional extra that happens before the one I want leading to the positive increment of 1 place)
I'm sure I'm doing this wrong so any help is greatly appreciated.
Hope this makes sense. Happy to clarify more if needed!
To check “parents“ in any level use ancestor
Try something like this (not tested) :
(//*[#id="all_totals"]/div[1]/div
/span[ ancestor::*/span[contains( text(), "text" )] and position() = 6
or position() = 7 ])[1]
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']")