Selenium WebdriverをNode.jsで動かして、CMSのWYSIWYGエディタを操作しJSONで用意した複数の記事データを自動登録する

静的HTMLで用意されているデータをCMSの記事として登録する作業があったのだけど、枚数がパッと見てもものすごく多く、人力でやるには人手も時間も足りなそうだったのでSeleniumを初めて使ってみた。

CMSで使っているエディタのライブラリが原因でソース入力ができず結構悩んでしまった。解決したので書いておく。

使用ツール・環境など

やりたかったこと

  • タイトルとHTML形式の本文をJSONで用意
  • Seleniumで自動登録
  • CMSに乗ったCKEditorをソース表示モードにしてHTMLで本文を登録する

つっかかったこと

  • Seleniumで動かした時にCKEditorをソース表示モードにできずにターミナルにエラーが表示される
  • もちろん本文も流し込めない

解決方法

Seleniumで以下のように操作するよう記述。

  1. 一旦は初期表示のビジュアルモードのままダミーテキストを入力
  2. その後、ソース表示ボタンを押す動作を入れる
  3. ダミーテキストを削除
  4. 本文を流し込む

CKEditor特有のものだったらしい。公式サイトのデモをSeleniumで操作してみて気づいた。

用意したJSONファイル

以下のようなtitle(記事のタイトル)、url(記事のURL)、souce(本文のHTML)を入れてあります。

gist.github.com

サンプル

仮の内容にしてあります。

ちなみにSelenium Webdriverの入手方法は公式サイトに。

コードはnpmのページのサンプルを元に作っていった。

gist.github.com

実行方法

ターミナルから以下コマンドで実行する

$ cd ファイルを保存したディレクトリ
$ node ファイル名.js

コードの解説

const {Builder, By, Key, until} = require('selenium-webdriver');

Selenium Webdriver を呼び出し

const posts = require('sample.json');

用意しておいたJSONファイルを呼び出し

  let driver = await new Builder().forBrowser('firefox').build();

Firefoxで動かす

    for(let i = 0; i<posts.length; i++){

記事データを1項目ずつ取り出す

      await driver.get('http://入力フォームのURL');

入力フォームのページへ遷移

      await driver.findElement(By.name('title')).sendKeys(posts[i]['title']);

titleのinputに用意したtitleの内容を入れる

      await driver.findElement(By.name('url_name')).sendKeys(posts[i]['url_name']);

url_nameのinputに用意したurl_nameの内容を入れる

      await driver.wait(until.elementLocated(By.tagName("iframe")), 1000);
      let iframe = await driver.findElement(By.tagName("iframe"));
      await driver.switchTo().frame(iframe);

CKEditorの入力フォームは表示に少し時間がかかっていたので1000ミリ秒待ってから、iframe内に移動

      await driver.findElement(By.tagName("body")).sendKeys("dummy");

「dummy」というテキストを入れる

      await driver.switchTo().defaultContent();

iframeから脱出する

      await driver.findElement(By.className('cke_button__source')).click();

cke_button__source というクラスのついた項目をクリックする(これがソース表示ボタン)

      await driver.findElement(By.className('cke_source')).clear();

さっき「dummy」と入れたのを消す

      await driver.findElement(By.className('cke_source')).sendKeys(posts[i]['source']);

jsonファイルの「souce」に用意してあったHTMLの内容を入力する

      await driver.findElement(By.name('button')).click();

登録ボタンを押す

  } finally {
    await driver.quit();
  }

作業が完了したらブラウザを閉じる

参考URL

感想など

  • エディター特有の問題でそのままの解決方法は検索結果に出てこずだったので一応記事にしてみた
  • Seleniumは今回Node.js版を使ってみたが、JavaC#RubyPythonでも提供されている
  • Stack Overflowをみてみると JavaPythonで書かれているのが多かった。
  • どちらも書いたことはないけどよく読めばなんとなくわかることがわかってちょっと感動した