改めて…Selenium な「How to wait until page is completely loaded」の「別解」

取り急ぎ。

寝る直前にやったらいかんなぁ…。昨晩のは、大ポカ。「うまくいってる」の解と違うのを引っ張ってきてしまってた。

取り急ぎという意味で:

断片
 1 # -*- coding: utf-8 -*-
 2 import time
 3 from contextlib import contextmanager
 4 
 5 from selenium import webdriver
 6 from selenium.webdriver.common.by import By
 7 
 8 
 9 class _MyScraper:
10     def __init__(self):
11         self._driver_inst = None
12 
13     @property
14     def _driver(self):
15         if self._driver_inst is None:
16             import sys
17             from selenium.webdriver.chrome.options import Options
18 
19             opts = Options()
20             if sys.platform == "win32":
21                 opts.add_argument("--disable-gpu")
22             else:
23                 # linux only
24                 opts.add_argument("--no-sandbox")
25                 # for on docker
26                 opts.add_argument("--disable-dev-shm-usage")
27             opts.add_argument("--disable-extensions")
28             opts.add_argument("--headless")
29             # opts.add_argument('--log-level=3')
30             opts.add_experimental_option("excludeSwitches", ["enable-logging"])
31             self._driver_inst = webdriver.Chrome(options=opts)
32         return self._driver_inst
33 
34     @contextmanager
35     def _wait0(self, sleep_time=0.2, **kwargs):
36         oldid = self._driver.find_element(By.TAG_NAME, 'html').id
37         yield
38         while oldid == self._driver.find_element(By.TAG_NAME, 'html').id:
39             time.sleep(sleep_time)
40 
41     @contextmanager
42     def _wait1(self, sleep_time=0.2, **kwargs):
43         def _pagehash():
44             dom = self._driver.find_element(
45                 By.TAG_NAME, 'html').get_attribute('innerHTML')
46             return hash(dom.encode('utf-8'))
47         yield
48         ph0, ph1 = "empty", ""
49         while ph0 != ph1:
50             ph0 = _pagehash()
51             time.sleep(sleep_time)
52             ph1 = _pagehash()
53 
54     def opentree(self, pkg):
55         with self._wait1():
56             self._driver.get(
57                 f"https://maven.google.com/web/index.html?q={pkg}")
58         # print(self._driver.page_source)
59         for node in self._driver.find_elements(By.CLASS_NAME, "indexItem"):
60             if node.text == pkg:
61                 with self._wait1():
62                     node.click()
63                 break
64         print(self._driver.page_source)
65 
66 
67 s = _MyScraper()
68 s.opentree("exoplayer")

これの「wait1」は昨晩の言い回しでいう「試してみてる範囲内ではうまくいってる」やつで、昨晩のに対応する「wait0」は、これはうまくいかない。

で、昨晩のでいうところの「readyState との関係」なのだけれど、これは、wait1 については愚問。wait1 のアプローチは wait0 と逆で、「変化が落ち着くのを待つ」ということをしてるだけなので、コードから全てわかる。これが不完全たりうる状況はすぐにわかるでしょ、そして「大抵うまくいく」理由もわかるはず。

取り急ぎでざっくり書いてしまったので、あとで改めて整理して書く…かもしれないし、書かないかもしれない。まぁ気分が乗れば、ね。

2023-04-06追記:
本題とは関係ないんだけれど、「for on docker」というコメントを付けて --disable-dev-shm-usage しているのが気持ち悪くて、解決したかった。これは dbus デーモンが docker ではデフォルトでは動作してないために必要なオプションだった。

ビルドステップにて必要なオペレーション:

1 # ...
2 RUN apt-get install -yqq dbus
3 RUN dbus-uuidgen > /var/lib/dbus/machine-id
4 RUN mkdir -p /var/run/dbus
5 # ...

で、エントリポイントでは例えば:

dockerentry.sh とかにして、これを Dockerfile の ENTRYPOINT とする、とか
1 #! /bin/sh
2 # ...
3 dbus-daemon --config-file=/usr/share/dbus-1/system.conf --print-address
4 # ...

こうしておけば --disable-dev-shm-usage しなくても docker で google-chrome は動作するようになる。