Selenium Docker

docker-composeで公式サンプルに一部追記

appサービスとしてPython実行のシェルのためのContainerを用意。
tty: trueを忘れずに。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# To execute this docker-compose yml file use `docker-compose -f <file_name> up`
# Add the `-d` flag at the end for detached execution
version: "3"
services:
selenium-hub:
image: selenium/hub:3.141.59-20200326
container_name: selenium-hub
ports:
- "4444:4444"

chrome:
image: selenium/node-chrome:3.141.59-20200326
volumes:
- /dev/shm:/dev/shm
depends_on:
- selenium-hub
environment:
- HUB_HOST=selenium-hub
- HUB_PORT=4444

firefox:
image: selenium/node-firefox:3.141.59-20200326
volumes:
- /dev/shm:/dev/shm
depends_on:
- selenium-hub
environment:
- HUB_HOST=selenium-hub
- HUB_PORT=4444

opera:
image: selenium/node-opera:3.141.59-20200326
volumes:
- /dev/shm:/dev/shm
depends_on:
- selenium-hub
environment:
- HUB_HOST=selenium-hub
- HUB_PORT=4444

app:
image: python:3-slim
working_dir: /app
command: /bin/bash
tty: true
volumes:
- ./app:/app

起動

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
PS > docker-compose up
Creating network "selenium_default" with the default driver
Pulling selenium-hub (selenium/hub:3.141.59-20200326)...
3.141.59-20200326: Pulling from selenium/hub
7ddbc47eeb70: Pull complete
c1bbdc448b72: Pull complete
8c3b70e39044: Pull complete
45d437916d57: Pull complete
19f82ee05cd5: Pull complete
320494ffe513: Pull complete
37bf31601241: Pull complete
ff4462d9e211: Pull complete
56d14078f879: Pull complete
c524497381c0: Pull complete
713cd1a80bdc: Pull complete
f27f03ce7183: Pull complete
23c2a3478434: Pull complete
39d077d89173: Pull complete
Digest: sha256:f401d133c344a8ede3e987431224e0e565364dc8bd65204a811a4bd537c9a924
Status: Downloaded newer image for selenium/hub:3.141.59-20200326
Pulling chrome (selenium/node-chrome:3.141.59-20200326)...
3.141.59-20200326: Pulling from selenium/node-chrome
7ddbc47eeb70: Already exists
c1bbdc448b72: Already exists
8c3b70e39044: Already exists
45d437916d57: Already exists
19f82ee05cd5: Already exists
320494ffe513: Already exists
37bf31601241: Already exists
ff4462d9e211: Already exists
56d14078f879: Already exists
c524497381c0: Already exists
713cd1a80bdc: Already exists
a17229ca0b94: Pull complete
af50740d57b3: Pull complete
d765b18646a8: Pull complete
f5db115c40d5: Pull complete
56afc6231b00: Pull complete
768a071aba5c: Pull complete
1204ec367cb8: Pull complete
c54b2a66c3aa: Pull complete
8dbdd3292558: Pull complete
5154a257e987: Pull complete
412ac1592b70: Pull complete
aebae33acd52: Pull complete
Digest: sha256:eab11d28aa7d1fb5907b66168f6ba78b7ad59a28e3659707c91a07da450fb573
Status: Downloaded newer image for selenium/node-chrome:3.141.59-20200326
Pulling firefox (selenium/node-firefox:3.141.59-20200326)...
3.141.59-20200326: Pulling from selenium/node-firefox
7ddbc47eeb70: Already exists
c1bbdc448b72: Already exists
8c3b70e39044: Already exists
45d437916d57: Already exists
19f82ee05cd5: Already exists
320494ffe513: Already exists
37bf31601241: Already exists
ff4462d9e211: Already exists
56d14078f879: Already exists
c524497381c0: Already exists
713cd1a80bdc: Already exists
a17229ca0b94: Already exists
af50740d57b3: Already exists
d765b18646a8: Already exists
f5db115c40d5: Already exists
56afc6231b00: Already exists
768a071aba5c: Already exists
b1ebf7fa7db5: Pull complete
6de8b6349cdc: Pull complete
c5c4c2bb4918: Pull complete
0ad2361bb86d: Pull complete
c3ee03a93323: Pull complete
Digest: sha256:ed6dd678c4223251adaa01bd6d2e1ac99077595581065d1e55620661733f81d9
Status: Downloaded newer image for selenium/node-firefox:3.141.59-20200326
Pulling opera (selenium/node-opera:3.141.59-20200326)...
3.141.59-20200326: Pulling from selenium/node-opera
7ddbc47eeb70: Already exists
c1bbdc448b72: Already exists
8c3b70e39044: Already exists
45d437916d57: Already exists
19f82ee05cd5: Already exists
320494ffe513: Already exists
37bf31601241: Already exists
ff4462d9e211: Already exists
56d14078f879: Already exists
c524497381c0: Already exists
713cd1a80bdc: Already exists
a17229ca0b94: Already exists
af50740d57b3: Already exists
d765b18646a8: Already exists
f5db115c40d5: Already exists
56afc6231b00: Already exists
768a071aba5c: Already exists
f5936530da40: Pull complete
be9409772e25: Pull complete
f5eb1736be2c: Pull complete
984d18a0f01a: Pull complete
6ee0a766ad86: Pull complete
8c094c1d5599: Pull complete
Digest: sha256:ce92510cac73440669472f67d79ccdd2b9b6b7640f6963ad5882e6379b79426d
Status: Downloaded newer image for selenium/node-opera:3.141.59-20200326
Creating selenium-hub ... done
Creating selenium_firefox_1 ... done
Creating selenium_opera_1 ... done
Creating selenium_chrome_1 ... done
Attaching to selenium-hub, selenium_chrome_1, selenium_opera_1, selenium_firefox_1
selenium-hub | 2020-04-17 01:27:42,777 INFO Included extra file "/etc/supervisor/conf.d/selenium-hub.conf" during parsing
selenium-hub | 2020-04-17 01:27:42,779 INFO supervisord started with pid 7
chrome_1 | 2020-04-17 01:27:43,330 INFO Included extra file "/etc/supervisor/conf.d/selenium.conf" during parsing
chrome_1 | 2020-04-17 01:27:43,331 INFO supervisord started with pid 7
opera_1 | 2020-04-17 01:27:43,449 INFO Included extra file "/etc/supervisor/conf.d/selenium.conf" during parsing
opera_1 | 2020-04-17 01:27:43,451 INFO supervisord started with pid 7
firefox_1 | 2020-04-17 01:27:43,455 INFO Included extra file "/etc/supervisor/conf.d/selenium.conf" during parsing
firefox_1 | 2020-04-17 01:27:43,456 INFO supervisord started with pid 7
selenium-hub | 2020-04-17 01:27:43,781 INFO spawned: 'selenium-hub' with pid 10
selenium-hub | Starting Selenium Hub with configuration:
selenium-hub | 2020-04-17 01:27:43,791 INFO success: selenium-hub entered RUNNING state, process has stayed up for > than 0 seconds (startsecs)
selenium-hub | {
selenium-hub | "host": "0.0.0.0",
selenium-hub | "port": 4444,
selenium-hub | "role": "hub",
selenium-hub | "maxSession": 5,
selenium-hub | "newSessionWaitTimeout": -1,
selenium-hub | "capabilityMatcher": "org.openqa.grid.internal.utils.DefaultCapabilityMatcher",
selenium-hub | "throwOnCapabilityNotPresent": true,
selenium-hub | "jettyMaxThreads": -1,
selenium-hub | "cleanUpCycle": 5000,
selenium-hub | "browserTimeout": 0,
selenium-hub | "timeout": 1800,
selenium-hub | "debug": false
selenium-hub | }
selenium-hub | 01:27:43.953 INFO [GridLauncherV3.parse] - Selenium server version: 3.141.59, revision: e82be7d358
selenium-hub | 01:27:44.026 INFO [GridLauncherV3.lambda$buildLaunchers$5] - Launching Selenium Grid hub on port 4444
chrome_1 | 2020-04-17 01:27:44,333 INFO spawned: 'xvfb' with pid 10
chrome_1 | 2020-04-17 01:27:44,333 INFO spawned: 'selenium-node' with pid 11
selenium-hub | 2020-04-17 01:27:44.386:INFO::main: Logging initialized @589ms to org.seleniumhq.jetty9.util.log.StdErrLog
opera_1 | 2020-04-17 01:27:44,452 INFO spawned: 'xvfb' with pid 10
opera_1 | 2020-04-17 01:27:44,453 INFO spawned: 'selenium-node' with pid 11
firefox_1 | 2020-04-17 01:27:44,458 INFO spawned: 'xvfb' with pid 10
firefox_1 | 2020-04-17 01:27:44,459 INFO spawned: 'selenium-node' with pid 11
selenium-hub | 01:27:44.614 INFO [Hub.start] - Selenium Grid hub is up and running
selenium-hub | 01:27:44.614 INFO [Hub.start] - Nodes should register to http://172.18.0.2:4444/grid/register/
selenium-hub | 01:27:44.615 INFO [Hub.start] - Clients should connect to http://172.18.0.2:4444/wd/hub
chrome_1 | Connecting to the Hub using the host selenium-hub and port 4444
chrome_1 | 2020-04-17 01:27:44,745 INFO success: xvfb entered RUNNING state, process has stayed up for > than 0 seconds (startsecs)
chrome_1 | 2020-04-17 01:27:44,745 INFO success: selenium-node entered RUNNING state, process has stayed up for > than 0 seconds (startsecs)
opera_1 | Connecting to the Hub using the host selenium-hub and port 4444
opera_1 | 2020-04-17 01:27:44,895 INFO success: xvfb entered RUNNING state, process has stayed up for > than 0 seconds (startsecs)
opera_1 | 2020-04-17 01:27:44,895 INFO success: selenium-node entered RUNNING state, process has stayed up for > than 0 seconds (startsecs)
firefox_1 | Connecting to the Hub using the host selenium-hub and port 4444
firefox_1 | 2020-04-17 01:27:44,896 INFO success: xvfb entered RUNNING state, process has stayed up for > than 0 seconds (startsecs)
firefox_1 | 2020-04-17 01:27:44,896 INFO success: selenium-node entered RUNNING state, process has stayed up for > than 0 seconds (startsecs)
chrome_1 | 01:27:44.937 INFO [GridLauncherV3.parse] - Selenium server version: 3.141.59, revision: e82be7d358
chrome_1 | 01:27:45.092 INFO [GridLauncherV3.lambda$buildLaunchers$7] - Launching a Selenium Grid node on port 5555
opera_1 | 01:27:45.234 INFO [GridLauncherV3.parse] - Selenium server version: 3.141.59, revision: e82be7d358
chrome_1 | 2020-04-17 01:27:45.294:INFO::main: Logging initialized @545ms to org.seleniumhq.jetty9.util.log.StdErrLog
firefox_1 | 01:27:45.380 INFO [GridLauncherV3.parse] - Selenium server version: 3.141.59, revision: e82be7d358
opera_1 | 01:27:45.454 INFO [GridLauncherV3.lambda$buildLaunchers$7] - Launching a Selenium Grid node on port 5555
firefox_1 | 01:27:45.602 INFO [GridLauncherV3.lambda$buildLaunchers$7] - Launching a Selenium Grid node on port 5555
opera_1 | 2020-04-17 01:27:45.607:INFO::main: Logging initialized @708ms to org.seleniumhq.jetty9.util.log.StdErrLog
opera_1 | 01:27:46.445 INFO [SelfRegisteringRemote$1.run] - Starting auto registration thread. Will try to register every 5000 ms.
firefox_1 | 01:27:46.523 INFO [SelfRegisteringRemote$1.run] - Starting auto registration thread. Will try to register every 5000 ms.
chrome_1 | 01:27:46.582 INFO [SelfRegisteringRemote.registerToHub] - Registering the node to the hub: http://selenium-hub:4444/grid/register
chrome_1 | 01:27:46.706 INFO [SelfRegisteringRemote.registerToHub] - The node is registered to the hub and ready to use
selenium-hub | 01:27:46.707 INFO [DefaultGridRegistry.add] - Registered a node http://172.18.0.3:5555
firefox_1 | 01:27:46.831 INFO [SelfRegisteringRemote.registerToHub] - Registering the node to the hub: http://selenium-hub:4444/grid/register
firefox_1 | 01:27:46.863 INFO [SelfRegisteringRemote.registerToHub] - The node is registered to the hub and ready to use
selenium-hub | 01:27:46.863 INFO [DefaultGridRegistry.add] - Registered a node http://172.18.0.5:5555
opera_1 | 01:27:46.897 INFO [SelfRegisteringRemote.registerToHub] - Registering the node to the hub: http://selenium-hub:4444/grid/register
opera_1 | 01:27:46.913 INFO [SelfRegisteringRemote.registerToHub] - The node is registered to the hub and ready to use
selenium-hub | 01:27:46.912 INFO [DefaultGridRegistry.add] - Registered a node http://172.18.0.4:5555

Selenium Gridの管理画面

localhost:4444にアクセス。

SeleniumGridCosole width=640

SeleniumGridCosole width=640

テストスクリプト

appで実行するテストスクリプト

appで動かすこのスクリプト記述した接続先のselenium-hubはdocker-composeによって名前解決される(docker-compose upで起動する必要がある)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from selenium import webdriver
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities

def test_access(driver):
driver.get('https://www.google.com')
driver.save_screenshot('test.png')
print(driver.title)
#driver.quit()

if __name__ == '__main__':
options = {
'command_executor': 'http://selenium-hub:4444/wd/hub',
'desired_capabilities': DesiredCapabilities.FIREFOX,
#'desired_capabilities': DesiredCapabilities.CHROME,
}
with webdriver.Remote(**options) as driver:
test_access(driver)

テストスクリプトによるテスト

実行するapp上ではpip install seleniumでモジュールをインストール。
実行するとコンソール上にGoogleが表示され、画面キャプチャがtest.pngというファイル名で保存される。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
PS > docker-compose up -d
Creating network "selenium_default" with the default driver
Creating selenium_app_1 ... done
Creating selenium-hub ... done
Creating selenium_firefox_1 ... done
Creating selenium_chrome_1 ... done
Creating selenium_opera_1 ... done
PS m> docker-compose exec app bash
root@9ca8831c0042:/app# pip install selenium
Collecting selenium
Downloading selenium-3.141.0-py2.py3-none-any.whl (904 kB)
|████████████████████████████████| 904 kB 2.6 MB/s
Collecting urllib3
Downloading urllib3-1.25.9-py2.py3-none-any.whl (126 kB)
|████████████████████████████████| 126 kB 11.3 MB/s
root@9ca8831c0042:/app# python test_code.py
Google

Selenium Screenshot width=640

quit()の挙動

quit() すると、WebDriverException になる。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
Traceback (most recent call last):
File "test_code.py", line 31, in <module>
title(driver, query)
File "/usr/local/lib/python3.8/site-packages/selenium/webdriver/remote/webdriver.py", line 170, in __exit__
self.quit()
File "/usr/local/lib/python3.8/site-packages/selenium/webdriver/remote/webdriver.py", line 698, in quit
self.execute(Command.QUIT)
File "/usr/local/lib/python3.8/site-packages/selenium/webdriver/remote/webdriver.py", line 321, in execute
self.error_handler.check_response(response)
File "/usr/local/lib/python3.8/site-packages/selenium/webdriver/remote/errorhandler.py", line 242, in check_response
raise exception_class(message, screen, stacktrace)
selenium.common.exceptions.WebDriverException: Message: Session [b3855f5e070d99082bcccd2a32bd9e7c] was terminated due to CLIENT_STOPPED_SESSION
Stacktrace:
at org.openqa.grid.internal.ActiveTestSessions.getExistingSession (ActiveTestSessions.java:115)
at org.openqa.grid.internal.DefaultGridRegistry.getExistingSession (DefaultGridRegistry.java:387)
at org.openqa.grid.web.servlet.handler.RequestHandler.getSession (RequestHandler.java:241)
at org.openqa.grid.web.servlet.handler.RequestHandler.process (RequestHandler.java:123)
at org.openqa.grid.web.servlet.DriverServlet.process (DriverServlet.java:85)
at org.openqa.grid.web.servlet.DriverServlet.doDelete (DriverServlet.java:75)
at javax.servlet.http.HttpServlet.service (HttpServlet.java:713)
at javax.servlet.http.HttpServlet.service (HttpServlet.java:790)
at org.seleniumhq.jetty9.servlet.ServletHolder.handle (ServletHolder.java:865)
at org.seleniumhq.jetty9.servlet.ServletHandler.doHandle (ServletHandler.java:535)
at org.seleniumhq.jetty9.server.handler.ScopedHandler.handle (ScopedHandler.java:146)
at org.seleniumhq.jetty9.security.SecurityHandler.handle (SecurityHandler.java:548)
at org.seleniumhq.jetty9.server.handler.HandlerWrapper.handle (HandlerWrapper.java:132)
at org.seleniumhq.jetty9.server.handler.ScopedHandler.nextHandle (ScopedHandler.java:257)
at org.seleniumhq.jetty9.server.session.SessionHandler.doHandle (SessionHandler.java:1595)
at org.seleniumhq.jetty9.server.handler.ScopedHandler.nextHandle (ScopedHandler.java:255)
at org.seleniumhq.jetty9.server.handler.ContextHandler.doHandle (ContextHandler.java:1340)
at org.seleniumhq.jetty9.server.handler.ScopedHandler.nextScope (ScopedHandler.java:203)
at org.seleniumhq.jetty9.servlet.ServletHandler.doScope (ServletHandler.java:473)
at org.seleniumhq.jetty9.server.session.SessionHandler.doScope (SessionHandler.java:1564)
at org.seleniumhq.jetty9.server.handler.ScopedHandler.nextScope (ScopedHandler.java:201)
at org.seleniumhq.jetty9.server.handler.ContextHandler.doScope (ContextHandler.java:1242)
at org.seleniumhq.jetty9.server.handler.ScopedHandler.handle (ScopedHandler.java:144)
at org.seleniumhq.jetty9.server.handler.HandlerWrapper.handle (HandlerWrapper.java:132)
at org.seleniumhq.jetty9.server.Server.handle (Server.java:503)
at org.seleniumhq.jetty9.server.HttpChannel.handle (HttpChannel.java:364)
at org.seleniumhq.jetty9.server.HttpConnection.onFillable (HttpConnection.java:260)
at org.seleniumhq.jetty9.io.AbstractConnection$ReadCallback.succeeded (AbstractConnection.java:305)
at org.seleniumhq.jetty9.io.FillInterest.fillable (FillInterest.java:103)
at org.seleniumhq.jetty9.io.ChannelEndPoint$2.run (ChannelEndPoint.java:118)
at org.seleniumhq.jetty9.util.thread.strategy.EatWhatYouKill.runTask (EatWhatYouKill.java:333)
at org.seleniumhq.jetty9.util.thread.strategy.EatWhatYouKill.doProduce (EatWhatYouKill.java:310)
at org.seleniumhq.jetty9.util.thread.strategy.EatWhatYouKill.tryProduce (EatWhatYouKill.java:168)
at org.seleniumhq.jetty9.util.thread.strategy.EatWhatYouKill.run (EatWhatYouKill.java:126)
at org.seleniumhq.jetty9.util.thread.ReservedThreadExecutor$ReservedThread.run (ReservedThreadExecutor.java:366)
at org.seleniumhq.jetty9.util.thread.QueuedThreadPool.runJob (QueuedThreadPool.java:765)
at org.seleniumhq.jetty9.util.thread.QueuedThreadPool$2.run (QueuedThreadPool.java:683)
at java.lang.Thread.run (Thread.java:748)