Sending value to input not working - python webscraping - python

I am currently scraping website https://glasschain.org (to be precise let's focus on example - https://glasschain.org/btc/wallet/100478015). There is a tab named 'Addresses', when I want to go to the X-th page (in this example equals 5 000) from available over 12 000 (to parallel some operations and not do it iteratively). To do it manually, I need to click on ... button and write a number and click ENTER. I use Python and selenium library to do it automatically and I have managed with clicking the button but I can't send a value to a field which appears after my click (in source code of website after clicking to button there is no new input field where I can send that value so I have tried to send it to the only existing element - earlier clicked button). Can anyone help me with that ? :)
My code:
chrome_options = Options()
chrome_options.add_argument("--start-maximized")
driver = webdriver.Chrome(path, options=chrome_options)
driver.get('https://glasschain.org/btc/wallet/100478015')
time.sleep(5)
adr_button = driver.find_elements_by_id('tab-Addresses')[1]
driver.execute_script("arguments[0].click();", adr_button)
time.sleep(5)
dots_button = driver.find_elements_by_class_name('ellipse')[0]
driver.execute_script("arguments[0].click();", dots_button)
dots_button.send_keys(5000)
My error:
selenium.common.exceptions.ElementNotInteractableException: Message: element not interactable (Session info: chrome=110.0.5481.77)
Version of selenium I am working with: 3.141.0

I don't have your specific version of selenium, but here's a solution that works with a more recent version, selenium 4.7.2:
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as ec
from selenium.webdriver.common.by import By
from selenium.common.exceptions import TimeoutException
from time import sleep
s = Service('bin/chromedriver.exe')
chrome_options = Options()
chrome_options.add_argument("--start-maximized")
driver = webdriver.Chrome(service=s, options=chrome_options)
driver.get('https://glasschain.org/btc/wallet/100478015')
delay = 3 # seconds
try:
adr_button = WebDriverWait(driver, delay).until(ec.presence_of_element_located((By.ID, 'tab-Addresses')))
except TimeoutException:
print("Loading timed out, exiting...")
exit()
# click the GDPR cookies accept button, if that hasn't happened yet
gdpr_button = driver.find_element(by='id', value='gdpr')
if gdpr_button:
driver.execute_script("gdpr('accept');")
driver.execute_script("arguments[0].click();", adr_button)
try:
dots_button = WebDriverWait(driver, delay).until(ec.presence_of_element_located((By.CLASS_NAME, 'ellipse')))
except TimeoutException:
print("Loading timed out, exiting...")
exit()
# click the dots button, to make the input appear
driver.execute_script("arguments[0].click();", dots_button)
# select the input, modify the value and then make it lose focus
dots_input = dots_button.find_element('xpath', "//span[#class='ellipse clickable']/input")
print(dots_input)
driver.execute_script("arguments[0].setAttribute('value','5000');", dots_input)
driver.execute_script("arguments[0].blur();", dots_input)
# just so you can see before the driver closes the browser again
sleep(10)
Note that, instead of waiting a number of seconds, the script simply waits until a specific element is loaded and then continues. There's still a delay defined, but this is now the maximum amount of time the script will wait for the element to load, instead of just waiting a fixed amount of time.
Once the button is clicked, a nested input element appears, which is selected and then JavaScript is executed to change its value and cause it to lose focus (blur), so that the new value gets applied.
(note that I have the Chrome driver in a subfolder next to the script, called bin)

Related

It closes when click runs. (selenium)

It closes when click runs.
and ı wanna just click button.
ı wanna do click that xpath. but its doesnt work. it close without doesnt click. so it open edge. waiting waiting and close. it didint run my click command. and there a error. its "webdriver object has no attribute find_element_by_xpath"
from selenium import webdriver
import time
driver = webdriver.Edge()
driver.get("https://www.instagram.com/")
time.sleep(7)
log_in = driver.find_element_by_xpath("//*[#id='mount_0_0_+h']/div/div/div/div[1]/div/div/div/div[1]/section/main/article/div[2]/div[2]/div/p/a/span")
log_in.click()
driver.close()
I think this is what you want:
# Needed libs
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time
#Open the browser
driver = webdriver.Edge()
driver.get("https://www.instagram.com/")
# Wait and click for allow cookies button
allow_cookies_button = date = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, f"//button[text()='Only allow essential cookies']")))
allow_cookies_button.click()
# Wait and click for Sign in button
signup_button = date = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, f"//span[text()='Sign up']")))
signup_button.click()
# This time.sleep is to allow you to see what happened, you can delete it
time.sleep(5)
# Close the browser
driver.close()
Why your code did not work? Because as you console told you "find_element_by_xpath" is not a valid method/attribute for driver.
Advises for automation/Scraping:
Wait for the element you will use (it does not matter if you want to write, click or whatever, make sure the element is on the screen)
Try to use xpaths (Or any selector) that are understandable, you will reuse them in the future. What do you understand better, //span[text()='Sign up'] or //*[#id='mount_0_0_+h']/div/div/div/div[1]/div/div/div/div[1]/section/main/article/div[2]/div[2]/div/p/a/span? Good selectors will save a lot of time in the future

2nd 'Element not interactable exception' when iterating on the loop, SELENIUM PYTHON

Selenium does see displayed element visible on the page on the second iteration.
I click on a link, and a box within a website appears. I need to close that box.
This action will be performed 1000+ times. On the first iteration, Selenium opens the link and closes the box. On the second iteration, Selenium opens the link, and cannot close the box. At this point, it gives error message:
Exception has occurred: ElementNotInteractableException Message: element not interactable (Session info: chrome=105.0.5195.102)
My code + HTML of relevant element below.
from selenium import webdriver
from selenium.webdriver.common.by import By
import time
options = webdriver.ChromeOptions()
options.add_argument("start-maximized")
options.add_argument("disable-infobars")
options.add_argument("--disable-extensions")
driver = webdriver.Chrome(chrome_options=options, executable_path=r"D:\SeleniumDriver\chromedriver.exe")
driver.get('https://sprawozdaniaopp.niw.gov.pl/')
find_button = driver.find_element("id", "btnsearch")
find_button.click()
interesting_links = driver.find_elements(By.CLASS_NAME, "dialog")
for i in range(len(interesting_links)):
interesting_links[i].click()
time.sleep(10) # I tried 60 seconds, no change
#
# HERE I WOULD DO MY THINGS
#
close_box = driver.find_element(By.CLASS_NAME, "ui-dialog-titlebar-close")
print(close_box.is_displayed())
close_box.click() # Here is where the program crushes on the 2nd iteration
if i == 4: # Stop the program after 5 iterations
break
HTML code of the relevant element:
<span class="ui-icon ui-icon-closethick">close</span>
I tried to locate the element that closes the box by CSS SELECTOR AND XPATH.
The CSS SELECTOR of the X/close button is the same every time, but
only the first time Selenium will see the X button displayed.
THE XPATH is strange. On the first opening of the link, X/close button will have path:
/html/body/div[6]/div[1]/a
However, if you open the next link, path will look this:
/html/body/div[8]/div[1]/a
Let me know what you think of that :-)
This is one way to achieve your goal:
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time as t
chrome_options = Options()
chrome_options.add_argument("--no-sandbox")
chrome_options.add_argument('disable-notifications')
chrome_options.add_argument("window-size=1280,720")
webdriver_service = Service("chromedriver/chromedriver") ## path to where you saved chromedriver binary
browser = webdriver.Chrome(service=webdriver_service, options=chrome_options)
wait = WebDriverWait(browser, 20)
url = 'https://sprawozdaniaopp.niw.gov.pl/'
browser.get(url)
wait.until(EC.element_to_be_clickable((By.ID, "btnsearch"))).click()
links = wait.until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, "a[class='dialog']")))
counter = 0
for link in links[:5]:
link.click()
print('clicked link', link.text)
### do your stuff ###
t.sleep(1)
wait.until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, 'span[class="ui-icon ui-icon-closethick"]')))[counter].click()
print('closed the popup')
counter = counter+1
This will print out in terminal:
clicked link STOWARZYSZENIE POMOCY DZIECIOM Z PORAŻENIEM MÓZGOWYM "JASNY CEL"
closed the popup
clicked link FUNDACJA NA RZECZ POMOCY DZIECIOM Z GRODZIEŃSZCZYZNY
closed the popup
clicked link FUNDACJA "ADAMA"
closed the popup
clicked link KUJAWSKO-POMORSKI ZWIĄZEK LEKKIEJ ATLETYKI
closed the popup
clicked link "RYBNICKI KLUB PIŁKARSKI - SZKÓŁKA PIŁKARSKA ROW W RYBNIKU"
closed the popup
Every time you click on a link, a new popup is created. When you close it, that popup will not disappear, but it will stay hidden. So when you click on a new link and then you want to close the new popup, you need to select the new (nth) close button. This should also apply to popup elements, so make sure you account for it. I stopped after the 5th link, of course you will need to remove the slicing to handle all links present in page.
Selenium setup above is chromedriver on linux - you just have to observe the imports, and the code after defining the browser(driver).
Selenium documentation can be found at https://www.selenium.dev/documentation/

Python ElementClickInterceptedException in Selenium

I'm working on a selenium based python automation project. The code automates some regular tasks by connecting to the company's internal webpage url. However, the code occasionally throws the same exception between two button click actions. Could you please help me to figure out what's the point that I miss? Thanks in advance.
You can find my code snippet and error screenshot here:
selenium.common.exceptions.ElementClickInterceptedException: Message:
element click intercepted: Element ... is not clickable at point (180,
447). Other element would receive the click: ...
pycharm_error_output_screenshot
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options
timeout = 200
options = Options()
options.headless = False
driver = webdriver.Chrome(options=options)
driver.implicitly_wait(3)
wait = WebDriverWait(driver, timeout)
driver.get("https://internal_web_appplication_page_for_company_x")
apply_button_v4 = wait.until(EC.visibility_of_element_located((By.XPATH, "//body//button[2]")))
apply_button_v4.click()
both_button = wait.until(EC.element_to_be_clickable((By.XPATH, "// label[3] // span[1]")))
wait.until_not(EC.element_to_be_clickable((By.CLASS_NAME, "map-mask")))
wait.until_not(EC.element_to_be_clickable((By.XPATH, "// *[ contains( # class, 'map-mask')]")))
both_button.click()
The intercepted element is full screen "loading" notification which is visible for a short period of time following any click action.
Moreover, my code works as expected if i put time.sleep(5) before clicking "both_button".
Try below solution for clicking on both_button. There are multiple reasons of this exception like javascript or ajax call , element is not in the view port.
actions = ActionChains(driver)
actions.move_to_element(both_button)
actions.click(both_button)
actions.perform()

Python Selenium how to wait before clicking on link

I just wonder, how to have the browser wait before clicking on a link? My goal is that I'm scraping from a dynamic webpage, the content is dynamic but I manage to get the form id. The only problem is that the submit button is only displayed after 2-3 seconds. However, my Firefox driver start clicking on the link immediately when the page is loaded (not the dynamic part).
Is there any way that I can make my browser wait 2-3 seconds until the submit button appears? I tried to use time.sleep() but it pauses everything, the submit button doesn't appear during time.sleep but appears after 2-3 seconds when time.sleep ends.
You can set wait like following :
Explicit wait :
element = WebDriverWait(driver, 20).until(
EC.presence_of_element_located((By.ID, "myElement"))
Implicit wait :
driver.implicitly_wait(20) # seconds
driver.get("Your-URL")
myElement = driver.find_element_by_id("myElement")
You can use any of above. Both are valid.
You need to use Selenium Waits.
In particular, element_to_be_clickable expected condition is what fits better than others:
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
element = WebDriverWait(driver, 10).until(
EC.element_to_be_clickable((By.ID, "myDynamicElement"))
)
element.click()
where driver is your webdriver instance, 10 is the amount of seconds to wait for an element. With this setup, selenium would try to locate an element every 500 milliseconds for 10 seconds in total. It would throw TimeoutException after 10 seconds left if element would not be found.

webdriver wait for ajax request in python

Currently I am writing webdriver test for search which uses ajax for suggestions. Test works well if I add explicit wait after typing the search content and before pressing enter.
wd.find_element_by_xpath("//div[#class='searchbox']/input").send_keys("obama")
time.sleep(2)
wd.find_element_by_xpath("//div[#class='searchbox']/input").send_keys(Keys.RETURN)
but
wd.find_element_by_xpath("//div[#class='searchbox']/input").send_keys("obama")
wd.find_element_by_xpath("//div[#class='searchbox']/input").send_keys(Keys.RETURN)
fails. I am running tests on ec2 with 1 virtual cpu. I am suspecting, I pressed enter even before GET requests related to search are sent and if I press enter before suggestions, it fails.
Is there any better way that adding explicit waits?
Add this method, where I ensure the API responses are back from server
def wait_for_ajax(driver):
wait = WebDriverWait(driver, 15)
try:
wait.until(lambda driver: driver.execute_script('return jQuery.active') == 0)
wait.until(lambda driver: driver.execute_script('return document.readyState') == 'complete')
except Exception as e:
pass
You indeed can add an explicit wait for the presence of an element like
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait # available since 2.4.0
from selenium.webdriver.support import expected_conditions as EC # available since 2.26.0
ff = webdriver.Firefox()
ff.get("http://somedomain/url_that_delays_loading")
ff.find_element_by_xpath("//div[#class='searchbox']/input").send_keys("obama")
try:
element = WebDriverWait(ff, 10).until(EC.presence_of_element_located((By.ID, "keywordSuggestion")))
finally:
ff.find_element_by_xpath("//div[#class='searchbox']/input").send_keys(Keys.RETURN)
ff.quit()
See: http://docs.seleniumhq.org/docs/04_webdriver_advanced.jsp#explicit-and-implicit-waits
And what about:
driver.implicitly_wait(10)
for your example:
wd.implicitly_wait(10)
In this case every time you are going to click or find element driver will try to do this action every 0.5 second during 10 seconds. In this case you don't need to add wait every time.
Note: But it is only about element on screen. It will not wait until some JS actions ends.

Categories

Resources