Parthapratim Neog
Parthapratim Neog

Reputation: 4478

Selenium task becomes slower and slower

not sure what the Title should be, please feel free to edit. I am new to Selenium, and was just playing around with it. I tried to experiment on one of my favorite site(10fastfingers.com) which I frequently use for practicing typing. I tried to automate the typing using the latest versions of both Selenium and Python(3.5.*). The automation works, but is REALLY REALLY slow.

Here is my sample script, I have no idea why it is slowing down. Would be great it someone would help me out here. I was expecting the script to go more than 100 WPM, but it only reaches 37 WPM

Problem is, only two lines of words are visible, so, it does not take all the words at once, The words keep appearing as you keep typing them. So, I could not take in all the words at once, and store it in a list, and then loop over them later.

from selenium import webdriver
from selenium.webdriver.common.keys import Keys

driver = webdriver.Firefox()
driver.get("http://10fastfingers.com/typing-test/english")
input = driver.find_element_by_id("inputfield")
for i in range(300):
    word = driver.find_element_by_class_name("highlight")
    word = word.text
    print("Typing word ", word)
    input.send_keys(word)
    input.send_keys(Keys.SPACE)

UPDATE

I tried getting all the words before instead of using find_element_* inside the loop as suggested by @alecxec. For this, I had to use, BeautifulSoup to parse the HTML, as it was in String format,and I could not use any of the find_element_* functions on it. The following script improved the Typing Speed to 138 WPM. But, I noticed that the first 20 seconds of typing is REALLY fast, and then the speed starts falling gradually. Please feel free to try out the script on you machine, and let me know the result of the test(WPM) that the script achieved on your system. Maybe it depends on the system configuration as well. I don't know. Is this some memory issue? or Code issue? Any idea how i can fix this.

Modified code

from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from bs4 import BeautifulSoup
driver = webdriver.Firefox()
driver.get("http://10fastfingers.com/typing-test/english")
input = driver.find_element_by_id("inputfield")

script = "return document.getElementById('row1').innerHTML"
all_words= driver.execute_script(script)
soup = BeautifulSoup(all_words, 'html.parser')
words = soup.find_all('span')
for word in words:
    text = word.text
    input.send_keys(text + Keys.SPACE)

UPDATE 2

I restarted my system, and closed all other applications, and the script ran like magic, it typed all 361 available words before the timer even reached 60 secs, and the result was 361 WPM. So, this does depends on the available RAM.

Upvotes: 3

Views: 2317

Answers (2)

JeffC
JeffC

Reputation: 25597

I love stuff like this. I did mine in Java first like this.

while (true)
{
    String word = driver.findElement(By.cssSelector("span.highlight")).getText();
    driver.findElement(By.id("inputfield")).sendKeys(word + " ");
}

This got me 361 wpm but I noticed that I ran out of words about halfway through. My second attempt was much faster but it was too fast. All the words were wrong.

List<WebElement> words = driver.findElements(By.cssSelector("span[wordnr]"));
WebElement input = driver.findElement(By.id("inputfield"));
System.out.println(words.size());
for (WebElement word : words)
{
    input.sendKeys(word + " ");
}

I realized that my first attempt was using highlight which meant that it would only go as fast as the page registered the word being successfully entered (thus triggering the highlight class change). I think ~360 is as fast as you can get because of the slowness (relatively) of the page.

Upvotes: 1

alecxe
alecxe

Reputation: 473863

Every .find_element_*, .text, .send_keys() etc calls are WebDriver commands which involve HTTP request/response (see JSON wire protocol) processing. You can dramatically decrease the amount of WebDriver commands you send by getting the word value before the loop:

word = driver.find_element_by_class_name("highlight").text
for i in range(300):
    print("Typing word ", word)
    input.send_keys(word + Keys.SPACE)

I've also joined the send_keys() into a single call.


Note that there is a better way to handle this particular use case. All the words are actually there in the HTML, you just need to get them. .text would only return a visible text only, use get_attribute("textContent") instead:

input_elm = driver.find_element_by_id("inputfield")

words_to_type = [word.get_attribute("textContent") + Keys.SPACE 
                 for word in driver.find_elements_by_css_selector("#row1 > span")]
for word in words_to_type:
    input_elm.send_keys(word)

Or, how about that? Use Action Chains - prepare the actions beforehand and then perform (360 words per minute on my machine):

from selenium.webdriver import ActionChains

input_elm = driver.find_element_by_id("inputfield")

actions = ActionChains(driver)
actions = actions.move_to_element(input_elm).click()
for word in driver.find_elements_by_css_selector("#row1 > span"):
    actions = actions.send_keys(word.get_attribute("textContent") + Keys.SPACE)
actions.perform()

Upvotes: 5

Related Questions