Open6

Google Form の勤怠管理を自動化したい

ひげひげ

フォームの入力内容を取得する

Google Form のHTMLは素直な実装がされてない。inputタグが記入欄と送信用で分けられている。試しに「あああ」と入力し、DevToolsで「あああ」を検索すれば2箇所ヒットする。記入欄はinputtypetextになっていて、送信用のタグはinputtypehiddenになっている。表にして比較しておく。なお、入力欄のinputは不要な属性がたくさんあったので省略している。

入力欄 送信用
<input type="text" data-initial-value="ああああ" > <input type="hidden" name="entry.1210620850" value="ああああ">


入力したデータをquerySelector()で取得したいので、その値の場所を示す一意なCSSセレクターを探す。今回は送信用のinputから取得したい。周辺のinputタグを見るとname属性がentry.XXXXXXXXと共通しているので、これをCSSセレクタにする。

このセレクタで値が取得できるかは、DevToolsのコンソールから確かめることができる。CSSセレクタが正しければ、コードの下に取得した結果が表示される。

ひげひげ

入力済みURLの作成

上の方法は手動でしなきゃいけないが、Google Formは入力済みのページを作る機能が提供されていたのでもっと簡単にできる。
https://blog.nakachon.com/2016/12/22/how-to-add-url-parameter-for-google-form/
この方法を使えば入力したい内容をURLパラメーターに埋め込むだけで自動入力っぽいことができる。さらに、上で取得したentry.XXXXという値も簡単に取得できる。

入力する内容はあえてアルファベットで書いた。日本語だとエンコードされて、入力した内容と対応するパラメーターを探すのに手間がかかるからだ。一方、日本語で書いても正しく動作するか確認するため、あとで日本語の回答も入れたらいいだろう。今回はたまたま必須事項に日本語入力があるので仕方なく日本語がついたURlが作成される。

ひげひげ

TSで入力済みURLの再現

先ほど取得したリンクをTSで使いまわしたい。
URLからパラメーターのキーとバリューを取りたいからルURLオブジェクトに変換して、searchParamsでパラメーターを取得し、ループを回して収集した。ついでに値をコピペするのは大変なので、変数に代入する雛形を書いてコピペする手間を減らした。

const url = new URL("https://docs.google.com/forms/d/e/1FAIpQLSfzp0GFGnlKpiC04D3bnhNye-AjBB44RnlzZsISeOOrB2vUGA/viewform?usp=pp_url&entry.877086558=%E8%BF%94%E4%BF%A1%E3%81%AB%E8%A1%A8%E7%A4%BA%E3%81%99%E3%82%8B%E3%83%A1%E3%83%BC%E3%83%AB%E3%81%A8%E3%81%97%E3%81%A6xxx@gmail.com%E3%82%92%E8%A8%98%E9%8C%B2%E3%81%99%E3%82%8B&entry.173198784=%E4%BD%9C%E6%A5%AD%E9%96%8B%E5%A7%8B%E5%A0%B1%E5%91%8A&entry.1498135098=10:00~15:00&entry.1424661284=sagyonaiyo&entry.2606285=sonota")
let params = url.searchParams;

for (const [key, value] of params) {
      console.log(`const = "${key}"`);
      console.log(`let = "${value}"`);
}

このコードを実行すると、entry.XXXconstとして、入力値はletとして代入できる。自動入力のURLを作成すると?usp=pp_urlというパラメーターが自動でついてくるので一応つけておく。

次に取得したURLからパラメーターを取り除き、再びつなげて元の入力済みページのURLが再現するコードを書いた。?usp=pp_url"という部分は自動でついているパラメーターなので、取り除かずにそのままにしてある。



const form_url = "https://docs.google.com/forms/d/e/1FAIpQLSfzp0GFGnlKpiC04D3bnhNye-AjBB44RnlzZsISeOOrB2vUGA/viewform?usp=pp_url"

const email  = "entry.877086558"
const report_type = "entry.173198784"
const duration = "entry.1498135098"
const todo = "entry.1424661284"
const others = "entry.2606285"

const email_ans = "返信に表示するメールとしてxxx@gmail.comを記録する"
const report_type_ans = "作業開始報告"
const duration_ans = "10:00~15:00"
const todo_ans = "sagyonaiyo"
const others_ans = "sonota"

// &, = の入力ミス防止のため改行して書いている
const params = `&${email}=${email_ans}` +
    `&${report_type}=${report_type_ans}` +
    `&${duration}=${duration_ans}` +
    `&${todo}=${todo_ans}` +
    `&${others}=${others_ans}`;

 const url = new URL(form_url + params)

 console.log(url.toString())

さらっと書いてるが $=の入力漏れや、空白の消し忘れでかなり手こずった。URLの文字列は長くてややこしいから目視で確認せず、差分チェッカーのサイトを使い、何が間違っているか確認しつつ修正した。

パラメーターに日本語を含める場合、自分でエンコードしなくてもいい。URLオブジェクトに代入したら自動でエンコードされる。

https://difff.jp/

ひげひげ

使う人によって変わるタイプのフォーム

Google Form の仕様として、チェックボックスもテキストエリアもボタンも、全て送信するときは文字列に変換される。1番目の返信に使うメールアドレスの確認用チェックボックスもテキストに変換しなければならない。しかしメールアドレスはログインしているユーザーによって異なるので、先ほどのようにURLのパラメーターに埋め込むのは無理そうだ。
そこで、Google Form に訪れたとき、要素をquerySelector()で取得し、click()を使うことでチェックボックスを入れることにする。チェックボックスはinputタグで実装されてないのでどの要素をクリックすればチェックがつくのか調べる必要がある。(そもそもclickイベントでチェックされるのかも分からない)。

返信に表示するメールという文字列を属性のどこかに含みクリックしたら反応する条件にあったDOMを探す。もしそういうのがなくても、子孫や兄弟要素にあればいい。探してみると見つけた。

とても複雑なタグだが、文字列を含みさえすればいい。次のCSSセレクタでDOMにアクセスできるかコンソールタブを開いて確かめた。

document.querySelector('div[aria-label*="返信に表示するメール"]').click()

するとチェックボックスにチェックが入ったのでこのCSSセレクタを使うことにする。次のような関数を作った。この関数を、ページを読み込んだときに実行すればいい。

function selectReplyCheckBox() {
    const checkBox = document.querySelector('div[aria-label*="返信に表示するメール"]') as HTMLBRElement;
    checkBox.click();
}

https://qiita.com/fufufukakaka/items/5d4a2f2272b8f1a4a16f