0

The code below is an automated CookieClicker I wrote for experimenting with ActionChains. It's based on a tutorial video, at 9:42. (Link)

When I run this code, the for loop runs down 1000 times but only 1 click happens. Multiple clicks only happen if I remove "#" from the commented line, so I run actions.click(cookie) each time. As for the video, that one extra line of code is not necessary. What can be the cause of that?

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.action_chains import ActionChains

s = Service("C:\Program Files (x86)\chromedriver.exe")
driver = webdriver.Chrome(service=s)

driver.maximize_window()
driver.get("https://orteil.dashnet.org/cookieclicker/")

driver.implicitly_wait(5)

cookie=driver.find_element(By.ID,"bigCookie")
cookie_count = driver.find_element(By.ID,"cookies")

actions = ActionChains(driver)
actions.click(cookie)

for i in range(1000):
    #actions.click(cookie)
    actions.perform()
    count=int(cookie_count.text.split(" ")[0])
    print(i,count)
driver.quit()
undetected Selenium
  • 183,867
  • 41
  • 278
  • 352
mgergo
  • 3
  • 4

2 Answers2

0

I assume you are using actions, for the sake of using it, or learning about it, since you could simply call cookie.click() to get the desired result.

Actions are used when you need to perform some "action" to an element other than find it or click on it, i.e. you want to right click, or click and hold, or send a keystroke combination and so on. Check Selenium Actions for more info.

As for using actions to click, you need to also understand that perform gets the composite object (call the build function of Actions) of your actions and execute them. Since your actions are declared outside the for loop, after the first click, the perform() function has no more actions to perform.

TLDR:
Either remove the comment of actions.click(cookie) inside your for loop or use cookie.click() to get the same result without using actions.

for i in range(10):
    actions.click(cookie)
    actions.perform()
    #cookie.click()
    count=int(cookie_count.text.split(" ")[0])
    print(i,count)

driver.quit() 

Colab Notebook of it working

  • Thank you for your answer. I edited the question, to be more clear. The thing I don't understand is why the code runs properly in the mentioned video WITHOUT the `actions.click(cookie)` line in the for loop. (At 9:42, [link](https://www.youtube.com/watch?v=OISEEL5eBqg&t=582s) ) It just looks controversial. – mgergo Nov 20 '21 at 14:38
  • @mgergo please read the whole answer. The answer to your comment is in the last sentence of the second paragraph – Enrique Castaneda Nov 21 '21 at 10:38
  • Thank you. So if I understand correctly, the behavior of `ActionChains()` must have changed since the linked video was made. – mgergo Nov 21 '21 at 22:38
0

The ActionChains implementation

ActionChains can be used in a chain pattern. When you call methods for actions on the ActionChains object, the actions are stored in a queue in the ActionChains object. When you call perform(), the events are fired in the order they are queued up.


perform()

Performs all stored actions.


Conclusion

perform() would fire the events stored in the queue. In your usecase, the actions.click(cookie) is the event.

Your optimal code block will be:

driver.get("https://orteil.dashnet.org/cookieclicker/")
cookie_count = WebDriverWait(driver, 20).until(EC.visibility_of_element_located((By.CSS_SELECTOR, "#cookies")))
cookie = WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "#bigCookie")))
for i in range(100):
    ActionChains(driver).click(cookie).perform()
    count = cookie_count.text.split(" ")[0]
    print(i,count)
driver.quit()

Console Output:

0 0
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9
10 10
11 11
12 12
13 13
14 14
15 15
16 16
17 17
18 18
19 19
20 20
21 21
22 22
23 23
24 24
25 25
26 26
27 27
28 28
29 29
30 30
31 31
32 32
33 33
34 34
35 35
36 36
37 37
38 38
39 39
40 40
41 41
42 42
43 43
44 44
45 45
46 46
47 47
48 48
49 49
50 50
51 51
52 52
53 53
54 54
55 55
56 56
57 57
58 58
59 59
60 60
61 61
62 62
63 63
64 64
65 65
66 66
67 67
68 68
69 69
70 70
71 71
72 72
73 73
74 74
75 75
76 76
77 77
78 78
79 79
80 80
81 81
82 82
83 83
84 84
85 85
86 86
87 87
88 88
89 89
90 90
91 91
92 92
93 93
94 94
95 95
96 96
97 97
98 98
99 99
undetected Selenium
  • 183,867
  • 41
  • 278
  • 352
  • Thanks, but my question was about using `actions.perform()` like this: 1. `actions = ActionChains(driver).click(cookie)` ... than 2. `actions.perform()` ... this time it works. Than 3. `actions.perform()` again ... but this time it does nothing. Why? – mgergo Nov 20 '21 at 19:22
  • Please pay attention to the first line of the conclusion and additionally the minor (but significant) change in the lines of code. – undetected Selenium Nov 20 '21 at 19:25
  • @mgergo You may be right, I haven't seen the video but several of my _`ActionChains()`_ and _`perform()`_ related long posts following the _Selenium_ documentation and best practices were beneficial to the community members. – undetected Selenium Nov 21 '21 at 22:20
  • @mgergo Absolutely, you have hit the right chord. – undetected Selenium Nov 21 '21 at 22:49
  • [accidentally deleted]: Ok, thank you. I got it. So using `.perform()` empties the queue of events. Maybe the behavior of `ActionChains()` has been changed since the video was made. – mgergo Nov 21 '21 at 22:57
  • @mgergo Honestly, I can't comment on that. – undetected Selenium Nov 21 '21 at 23:00