1.ChromeDriverのタイムアウト
SeleniumでChromeを自動操作している時に、ファイルアップロード等で60秒以上かかる場合に、タイムアウトが発生します。
これを回避するには、new ChromeDriver()で第3引数(TimeSpan)にタイムアウト時間を指定します。
海外のサイトで--no-sandboxオプションをつけたらうまくいったという話も見かけましたが、私はうまくいきませんでした。
アップロードの場合には、併せてページロードタイムアウトも設定が必要です。
C#で確認しています。
2.セッション0で実行時にnew ChromeDriver()でタイムアウトまで待たされて例外が発生することがある
Windowsサービス(セッション0で動作)からセッション0にC# exeを起動し、そのexeでnew ChromeDriver()を行うと、数回~数十回に一度、上記のタイムアウトまで待たされてから例外が発生します。
new ChromeDriver()の第3引数タイムアウトの指定の有無に関わらず発生します。
ChromeとChromeDriverのバージョン91と92で確認しています。
サービスをLocalSYSTEMで動作させた場合と、Administratorsに所属するユーザーで動作させた場合のどちらでも発生します。
LocalSYSTEMで動作しているサービスからCreateProcessしてexeをLocalSYSTEMで実行させた場合と、CreateProcessAsUserでAdministratorsに所属するユーザーで動作させた場合のどちらでも発生します。
HttpWebRequestのGetResponse()で例外と出力されるので、ChromeDriverとchromedriver.exeが通信できていないのではないかと推測。
サービスを経由しないでexeを実行した場合(セッション1)や、サービスからコンソールユーザーのセッション(セッション1)に起動した場合には100回実行しても発生しません。
画面表示なしで実行させるためにセッション0で実行させたいため、new ChromeDriver()が済んでいるexeをプールしてワーカープロセスとして利用するようにして回避しました。そのまま半常駐として動かすとメモリリークが怖いので、一定時間でリサイクルさせています。
画面表示なしにするためには、セッション1でChromeのheadlessモードを使用する手もありますが、ファイルのダウンロードが失敗するので使用していません。回避方法もあるようですが、未確認です。ログオンしていない状態で動かしたいのでheadlessモードは使用しません。
ただ、このタイムアウトが発生した場合なのか、chromedriver.exeとchrome.exe複数が残る場合があります。 ChromeDriver初期化を一度に1つしかしないようにして、ChromeDriver初期化前と初期化後のchromedriver.exeとchrome.exeのプロセスID一覧を取得して、タイムアウト発生時に増加した分のchromedriver.exeとchrome.exe複数をKILLするようにしました。 サービス側にKILL処理を入れたのですが、それだとサービス停止タイミングによってchromedriver.exeとchrome.exe複数が残る場合があるので、ワーカープロセス側にセマフォを導入し、最後のワーカープロセスの終了時にKILL処理を追加しました。それでもワーカープロセスの起動停止を短時間で繰り返すと半日でなぜかchromedriver.exeとchrome.exeが増えるので、ワーカープロセスの実行Windowsユーザーを2つにして 一定時間で起動ユーザーを変更し 、ユーザー1のワーカープロセスのみを実行しているはずなのにユーザー2のchromedriver.exeとchrome.exeが残っていたらKILLするようにしました。
KILLするプロセスはセッション0のもののみとし、セッション1以降のプロセスはKILLしないようにしています。
Chrome 93にしたら、セッション0の場合にPCによって上記動作だったり、new ChromeDriver()が全てタイムアウトになったりしました。Chromeを92に戻して、gpedit.mscを使用する公式手順で自動アップデートを無効にしました。
3.セッション0で実行時に?GoToUrl()が例外を返し続けることがある
4秒くらいかかって、例外が発生します。一度発生したらそのプロセスでは発生し続けるので、終了させて別プロセスを起動しています。
→「ChromeDriver初期化前と初期化後のchromedriver.exeとchrome.exeのプロセスID一覧を取得して、タイムアウト発生時に増加した分のchromedriver.exeとchrome.exe複数をKILLするようにしました。」が必要なchromedriver.exeとchrome.exeをKILLしていたっぽいです。これをなくすと安定しました。
4.セッション0で実行時に?ChromeDriverのメソッドがlocalhost:XXXXXに繋がらずに例外を返すことがある
PC環境によって、セッション0でも正常に動作し続けるPCと、例外が頻発するPCがあり、現在調査中です。例外の内容(覚えていないので後で追記します)から、ChromeDriverがchromedriver.exeに接続できなさそうです。例外が発生するとサイトからログアウトできなくなり、次回二重ログインとなってログインできず、困っています。
→これも上記のKILL処理を省いたら直りました。
WebDriverWaitのポーリング間隔(メソッドは後で追記します)を設定しましたが、変わらず。
localhostの名前解決に時間がかかっているのかと思い、hostsに 127.0.0.1 localhost を記述しましたが、変わらず。IPv6を無効化しても変わらず。netsh interface ipv6 set prefixpolicy でIPv4を優先させても変わらず。
例外発生時に新しいChromeDriverをnewして、既存のChromeのポートに接続して操作を続行できないか検討中です。短いテストプログラムで下記のコードは動作しました。
→念のため実装しましたが、いまのところ必要なさそうなのでコンフィグで殺しています。
ChromeDriver driver = new ChromeDriver(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location));
Dictionary<string, object> chromeOptions = (Dictionary<string, object>)driver.Capabilities["goog:chromeOptions"];
string debuggerAddress = (string)chromeOptions["debuggerAddress"];
:
ChromeOptions chromeOptions2 = new ChromeOptions();
chromeOptions2.DebuggerAddress = debuggerAddress ;
ChromeDriver driver2 = new ChromeDriver(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), chromeOptions2);