Google フォームを reCaptcha 付でブログに埋め込む

当サイトでは Googleフォームを使ってお問い合わせ用のフォームメールを導入しています。iframe で埋め込むのではなく、form 内の要素だけを取り出し完全にブログのスタイルにカスタマイズできるようにしています。



その Googleフォームがどうやら最近仕様変更があったようでエラーが出たりしていましたので過去の記事を見直しあらためて手順をまとめました。



Googleフォームをつくる


f:id:ausnichts:20200815102802j:plain


デモですのでメールアドレスとお問い合わせ内容のみです。適宜追加すればいいです。

recaptcha は「記述式」に「必須」を指定しておきます。


プレビューのソースからaction属性、name属性を取り出す

出来上がったフォームをプレビューしソースコードから action属性と各要素の name属性を取り出します。

action 属性

ソース内を action で検索しますと次のような箇所が見つかります。


<form action="https://docs.google.com/forms/u/0/d/e/1FAIpQLSdc7eXx8CPpeEFmUUhMf-AGV38qjDhNfIpJIHbEMCkpYXADrQ/formResponse" target="_self" method="POST" 

action属性の値を取り出しておきます。


name 属性

ソース内を data-params で検索しますと次のような箇所が作成した要素分見つかります。


<div jsmodel="CP1oW" data-params="%.@.[485493716,&quot;メールアドレス&quot;,null,0,[[1736330280,[],true,[],[[2,102,[]]]]],null,null,null,[]],&quot;i1&quot;,&quot;i2&quot;,&quot;i3&quot;,false]" 

上の例で言えば 1736330280 に entry が付加され entry.1736330280 が name属性になります。

以前はこの name属性も各要素のタグの中に入力されていたのですが、いつからかその他の指定とともに配列として上位のタグの中で指定されるようになっています。また、この値は javascript の変数 FB_PUBLIC_LOAD_DATA としても指定されていますのでこの変数名で検索すれば同じものが見つかります。


自前のフォームをつくる

ブログ内に作成したフォームと同じ構成のフォームをつくり、取り出した action属性と 各要素の name属性を入れます。


<div id="formWrapper">
    <form action="https://docs.google.com/forms/**********" method="post">
        <label>メールアドレス</label>
        <input type="email" name="entry.**********" value="" required pattern="^(([-\w\d]+)(\.[-\w\d]+)*@([-\w\d]+)(\.[-\w\d]+)*(\.([a-zA-Z]{2,5}|[\d]{1,3})){1,2})$" />
        <label>お問い合わせ内容</label>
        <textarea required name="entry.**********"></textarea>
        <input type="hidden" name="entry.**********" value="dummy">
        <input type="submit" value="送信">
    </form>
</div>

input要素の recaptcha は hidden 指定し、この段階では使用しませんので値に dummy など適当な文字列を入れておきます。


f:id:ausnichts:20200815172156j:plain

css でスタイル指定しますとこんな感じに出来上がりますので、入力しテスト送信してみます。


f:id:ausnichts:20200815172429j:plain

Googleフォームの画面に変わってしまいますので次はこれを止める方法です。

なお、ここまでがうまくいっていれば Googleフォームの回答にテスト送信の内容が保存されているはずです。


送信後のGoogleフォームの画面遷移を止める

考え方は form要素の target に 非表示の iframe を指定し、応答があれば javascript で画面表示を変更するということです。別ページをつくって遷移させてもいいと思います。


ブログ内の html を次のように変更します。

<script>
function showThxMessage(){
   var email = document.myForm.elements['entry.**********'].value; // メールアドレスのname属性
   if(email !== ''){
       document.myForm.reset();
       document.getElementById('formWrapper').style.display = 'none';
       document.getElementById('thxMessage').style.display = 'block';
   }
}
</script>

<div id="formWrapper">
    <form action="https://docs.google.com/forms/**********" method="post" name="myForm" target="dummyIframe">
        <label>メールアドレス</label>
        <input type="email" name="entry.**********" value="" required pattern="^(([-\w\d]+)(\.[-\w\d]+)*@([-\w\d]+)(\.[-\w\d]+)*(\.([a-zA-Z]{2,5}|[\d]{1,3})){1,2})$" />
        <label>お問い合わせ内容</label>
        <textarea required name="entry.**********"></textarea>
        <input type="hidden" name="entry.**********" value="dummy">
        <input type="submit" value="送信">

        <iframe name="dummyIframe" style="display:none;" onload="showThxMessage();"></iframe>
    </form>
</div>
<div id="thxMessage" style="display:none;">
    お問い合わせありがとうございました。<br /><br />
    などの送信後のメッセージ
</div>


これで送信後に Googleフォームに遷移せずブログ内の表示に変わります。


reCaptcha を導入する

スパム対応として Google reCaptcha を導入します。


reCaptcha にサイトを登録する

メニューの Admin Console をクリックしますと「新しいサイトを登録する」ページになります。


f:id:ausnichts:20200815201310j:plain

必要事項を入力します。v3 がありますが、現時点ではどういったものか把握していませんので v2 を選んでいます。


f:id:ausnichts:20200815201458j:plain

保存しますとサイトキーとシークレットキーが表示されます。


HTML に reCaptcha を導入する

reCaptcha を導入するには、https://www.google.com/recaptcha/api.js を読み込み、フォーム内に <div class="g-recaptcha" data-sitekey="your_site_key"></div> の htmlコードを入れるだけです。ただし、はてなブログの場合はデフォルトで api.js が読み込まれていますので htmlコードを挿入すればそれだけで reCaptcha ウィジェットがレンダリングされます。


<script>
function checkRecaptcha() {
  var response = grecaptcha.getResponse();
  if(response.length == 0) { 
    //reCaptcha 未認証
    document.getElementById('recaptcha-error').innerHTML = "reCaptchaにチェックを入れてください";
    return false;
  }
  else { 
    //reCaptch 認証
    document.myForm.elements['entry.**********'].value = response; // recaptchaのname属性
    document.myForm.submit();
  }
}

function showThxMessage(){
   var email = document.myForm.elements['entry.**********'].value; // メールアドレスのname属性
   if(email !== ''){
       document.myForm.reset();
       document.getElementById('formWrapper').style.display = 'none';
       document.getElementById('thxMessage').style.display = 'block';
   }
}
</script>
<div id="formWrapper">
    <form action="https://docs.google.com/forms/**********" method="post" name="myForm" target="dummyIframe" onsubmit="return checkRecaptcha();">
        <label>メールアドレス</label>
        <input type="email" name="entry.**********" value="" required pattern="^(([-\w\d]+)(\.[-\w\d]+)*@([-\w\d]+)(\.[-\w\d]+)*(\.([a-zA-Z]{2,5}|[\d]{1,3})){1,2})$" />
        <label>お問い合わせ内容</label>
        <textarea required name="entry.**********"></textarea>
        <div class="g-recaptcha" data-sitekey="サイトキー"></div>
        <div id="recaptcha-error"></div>
        <input type="hidden" name="entry.**********" value="">
        <input type="submit" value="送信">

        <iframe name="dummyIframe" style="display:none;" onload="showThxMessage();"></iframe>
    </form>
</div>
<div id="thxMessage" style="display:none;">
    お問い合わせありがとうございました。<br /><br />
    などの送信後のメッセージ
</div>


f:id:ausnichts:20200815203607j:plain

reCaptchaウィジェットが表示されています。 送信しますと javascript で reCaptcha がチェックされているかどうかを確認し、されていれば作成されたトークンを recaptcha要素に入れてフォームデータをサーバサイドに送ります。


f:id:ausnichts:20200815203821j:plain

Googleフォームの回答を見ていますと確かにトークンが送られています。


Google Apps Script で reCaptcha を認証する

送られたトークンの認証は Google Apps Script(GAS)で行います。


f:id:ausnichts:20200815202602j:plain

スクリプトエディタをクリックし GAS に移動し次のコードを入力します。

ただし、GAS で行う認証は Googleフォーム登録後に呼び出されますので、送られてきたトークン(recaptcha の値)が正しいものでなくてもフォームデータは登録されてしまいます。

これを避けるためには、フォームの設定の「メールアドレスを収集する」を利用した reCaptcha を使用する方法がありますが、今のところその方法で導入する方法を思いつけていません。また、recaptcha 要素に正規表現で入力制限をかけることができますのでそれでも何か方法がありそうな気がしています。


このコードは、reCaptcha で認証された場合はフォームメール送信者に確認メールとサイト管理者にお知らせメールを送るものです。

※自由に使っていただいて構いませんが、転載はしないでください。

//Secret key
var secret = 'reCaptchaのシークレットキー';

function verifyCaptcha(arg){
  var payload = {
    'secret' : secret,
    'response': arg
  }
  var url = 'https://www.google.com/recaptcha/api/siteverify';
  var resp = UrlFetchApp.fetch(url, {
    payload : payload,
    method : 'POST'
  }).getContentText();
  return JSON.parse(resp).success;
}

function submitForm(e){
  
  var itemResponses = e.response.getItemResponses();
  for (var i = 0; i < itemResponses.length; i++) {
    var question = itemResponses[i].getItem().getTitle();
    var answer = itemResponses[i].getResponse();
    if (question == 'メールアドレス') var userMail = answer;
    if (question == 'お問い合わせ内容') var userMessage = answer;
    if (question == 'recaptcha') var recaptcha = answer;
  }
  
  var resp = verifyCaptcha(recaptcha);

  if(resp)
  {
  // 自動返信メール
  var subject = 'お問い合わせ内容確認';
  var body = 'お問い合わせありがとうございました。\n'
    + 'このメールは自動返信メールです。\n'
    + '後ほど ' + userMail + ' あてにご連絡いたします。\n\n'
    + '-- お問い合わせ内容 ------------------------------------\n\n'
    + userMessage + '\n\n'
    + '----------------------------------------------------------------\n\n'
    + '--\n'
    + 'サイト名\n'
    + 'サイトurlなど\n\n';
  GmailApp.sendEmail(userMail, subject, body, {name: '送信元'});

  // 通知メール
  subject = 'お問い合わせがありました';
  body = 'お問い合わせがありました。\n'
    + userMail + ' あてに返信してください。\n\n'
    + '-- お問い合わせ内容 ------------------------------------\n\n'
    + userMessage + '\n\n'
    + '----------------------------------------------------------------\n\n'
    + '--\n'
    + 'サイト名\n'
    + 'サイトurlなど\n\n';
  GmailApp.sendEmail('送信先(自分のメールアドレス)', subject, body, {name: '送信元'});
  }
}


f:id:ausnichts:20200816115025j:plain

コードを保存し「現在のプロジェクトのトリガー」をクリックし、トリガーのページの右下「トリガーを追加」をクリックします。


f:id:ausnichts:20200816115404j:plain

上の画像のように選択し保存します。


f:id:ausnichts:20200816115700j:plain

アカウントを選択し、


f:id:ausnichts:20200816115902j:plain

詳細をクリックし、


f:id:ausnichts:20200816120016j:plain

認証だったか、保存だったか、OK系のものをクリックします。


f:id:ausnichts:20200816120116j:plain

トリガーが設定されました。

これでフォームメールが送信され reCaptcha 認証を通りますと確認メールとお知らせメールが送られます。


お問い合わせ等

当記事内のスクリプト等によるいかなる損害についても責任を負いかねますので自己責任でご使用ください。

お問い合わせ、バグの報告、仕様変更のご要望等は Contact Us までお願いします。