840回 2行を書換える
2019年4月から JavaScriptを扱う様になり、約5年ほどツールの紹介記事を書いて来ました。それらの本文中で、ほぼ定型の下の2行を何度も使って来ました。
〔コピー方法〕 右サイドバーの マークのボタンを1度押してください。
コード枠内の右クリック ➔ コード全体の選択 ➔ コピー操作 が可能になります。
これは、記事中のコードを簡単にコピーできる様にする「右サイドバーの特別なボタン」の説明です。
しかし、最近のフリースペースの仕様変更で「 ボタン」のコピー機能が使えなくなり、上の2行の説明を下の説明に書換える事になりました。
〔コピー方法〕 軽量シンプルなツール「PreBox Button 」を使うと
コード枠内を「Ctrl+左Click」➔「Copy code 」を「左Click」
の操作で、掲載コードのコピーが可能になります。
"〔コピー方法〕" の文字列でブログ内を検索すると 840件ほどありました。 それだけの記事を編集・修正するのはしんどい話です。
変形されにくい文字列なら自動化できる
例えば「東京都渋谷区道玄坂二丁目」といった定形のシンプルな文字列なら、書換えは自動処理ツールに任せられます。 この様な定形文字列は変形が加えられ難いので、処理漏れとなる可能性が低いからです。
下は「文字列の検索」を自動処理するツールですが、このツールが各記事を開いて行う「検索」処理を、「書換」+「文書保存」に置き換えれば、文字列の書換え処理を自動化できます。
今回の2行には「FontAwesome」の絵文字が含まれ、置換える3行には文字リンクが含まれているので、この処理は「HTMLの検索・置換処理」が不可欠です。 そして、実際に処理を始めて判ったのですが、2行の文字列には、改行やスペースの変形パターンの複数種が出来ていて、2割ほどが自動化から漏れてしまうのです。
半自動化の処理
こうなると、記事を開いて修正箇所を書換える作業が必要です。 しかし、件数からしてそれは困難。 そこで、以下の処理を行う半自動化のスクリプトを作りました。
❶ 編集画面を開いた時に2行を検索できた場合は、自動で書換える。
更に、自動処理の結果をチェックできる様に、編集対象の位置までスクロール。
◎ 編集対象は、記事の最後の「h3」までスクロールすると表示されます。
❷ 編集画面を開いた時に2行を検索できない場合は、「対象が見つからない」のアラートを表示。 この場合は、手作業での修正を行う。
◎「replace()」メソッドで ❶ は自動化できるが、❷ は出来ません。 問題の2行を見つけて削除(手作業)し、3行を書き込みます。 書込みは以下のツールを使えば、ショートカットのキー操作だけで出来ます。(3行のツールへの登録が必要です)
❸ 編集画面のバグで、編集を行うとたいてい文末に空白行が1行増えるので、それを修正。 この操作を素早くするのに、文末移動「Ctrl+End」を使う。
最後に、文書の投稿をマウス操作で行うのは手間なので、「Ctrl+Shift+Enter」で「投稿する」を押せる様にする。
❹ 投稿の結果で「投稿確認画面」と「記事の編集削除」のタブウインドウが生成されるが、これを毎回閉じる操作は大変なので、自動で閉じる様にする。
ツールを使用した結果
実際の処理をしながらコードを練ったので、コードが最後の形になった時には、全ての1/3程度まで進んだ頃でした。 それでも、このツールが無かったらとても1日では作業が終わらなかったと思います。
ブログ内の検索結果のリストから直に編集画面を開くには、下のツールも、無くてはならないものでした。
このリストの 840件を編集したわけですが、8割は編集画面を開くと「置換」え済みの問題箇所が表示されます。 それを確認して文末に移動し、文末の行を修正して「投稿」という操作の繰り返しになります。
大量の処理になると、マウス操作とキーボード操作の持ち替えがスムーズかそうでないかで、ずいぶん能率が違って来ます。 そういう事にも対処しながら、このツールは出来上がったのです。
「Auto Worker 💢💢 Rewrite」
このツールは『変形パターンが多い定形文字列を、半分は自動、半分は手作業で別の文字列に置き換える』ためのツールです。
いわば、置換え作業のサポートツールですが、実際の処理をしながら頻繁に見つかる「変形パターン」については、1種だけですが ❶ の自動化を可能にしています。
過去記事の全域を修正する作業は、一般のユーザーには余り必要はないと思います。 しかし、同様の必要が生じた時には、このツールが参考になると思います。
カスタマイズと使用上の注意
◎ 以下のソースコードは、この記事の検索対象の「2行」(2パターンに対応)と置換の文字列「3行」の「HTMLコード」が記入されています。 この文字列を書換える事で、任意の文字列の「検索・書換」に利用できます。 対象文字列のカスタマイズ箇所は「🟦⬜🟦」の表示を置いています。
◎「置換え処理後の自動スクロール 🟦⬜🟦」の関数は、最後の「h3」を表示するか、「h3」がなければ文末に移動する動作をします。「h3」表示は今回の作業に特化した動作で、以下に変更すれば「文末に移動」へ変更が出来ます。
◎ このツールは文字列のHTML書換処理の専用で、通常時には正常な編集作業を阻害します。 目的の作業の終了後は速やかに「OFF」にする必要があります。
◎ JavaScriptの処理について理解が浅い状態で、このツールをカスタマイズして扱うと、既存の記事の内容を想定外に壊したり、失ったりする可能性があります。 カスタマイズして利用される場合は、失っても良いコピーをした記事などで厳密な処理テストをした上で利用してください。 結果は自己責任です。
「Auto Worker 💢💢 Rewrite」を利用するには
このツールは Chrome / Edge の 拡張機能「Tampermonkey」に以下のソースコードをコピーして登録する事で利用できます。 Firefoxではテストをしていません。
◎「Tampermonkey」の「+」マークの「新規スクリプト」タブを開きます。
◎「新規スクリプト」には、最初からテンプレートが記入されています。 これは全て削除して、完全に空白の編集枠に 下のコードをコピー&ペーストします。
〔コピー方法〕 軽量シンプルなツール「PreBox Button 」を使うと
コード枠内を「Ctrl+左Click」➔「Copy code 」を「左Click」
の操作で、掲載コードのコピーが可能になります。
◎ 最後に「ファイル」メニューの「保存」を押すと、ツールが使用可能になります。
◎ 前項の「カスタマイズと使用上の注意」に従って、登録したソースコードをカスタマイズした上で使用してください。
「Auto Worker 💢💢 Rewrite」 ver.0.1
// ==UserScript== // @name Auto Worker 💢💢 Rewrite // @namespace http://tampermonkey.net/ // @version 0.1 // @description ブログ記事の自動編集(検索と置換)+「Ctrl+Shift+Enter」で投稿を実行 // @author Ameba Blog User // @match https://blog.ameba.jp/ucs/entry/srventryupdate* // @match https://blog.ameba.jp/ucs/entry/srventrylist.do?* // @run-at document-start // @grant none // ==/UserScript== let search_word1= // 検索候補1 HTML 🟦⬜🟦 '<p>〔コピー方法〕 右サイドバーの <i class="fa-regular fa-clone" style="color: #2196f3; '+ 'padding: 3px; border: 1px solid #2196f3; border-radius: 4px;"> </i> マークのボタンを1度押してください。</p>'+ '<p> コード枠内の右クリック ➔ コード全体の選択 ➔ コピー操作 が可能になります。</p>'; let search_worde1=escapeRegExp(search_word1); let search_word2= // 検索候補2 HTML 🟦⬜🟦 '<p>〔コピー方法〕 右サイドバーの <i class="fa-regular fa-clone" style="color: #2196f3; '+ 'padding: 3px; border: 1px solid #2196f3; border-radius: 4px;"> </i> マークのボタンを1度押してください。</p>'+ '<div><p> コード枠内の右クリック ➔ コード全体の選択 ➔ コピー操作 が可能になります。</p></div>'; let search_worde2=escapeRegExp(search_word2); let rewrite_word= // 置換え文字列 HTML 🟦⬜🟦 '<p>〔コピー方法〕 軽量シンプルなツール「<a href="https://ameblo.jp/personwritep/entry-12818954389.html" '+ 'rel="noopener noreferrer" target="_blank"><b style="font-weight:bold;">PreBox Button</b> '+ '<i class="fas fa-external-link-alt fa-sm"> </i> </a>」を使うと</p>'+ '<p> コード枠内を「Ctrl+左Click」➔「Copy code <i class="fa-regular fa-clone"> </i>」を「左Click」</p>'+ '<p> の操作で、掲載コードのコピーが可能になります。</p>'; let interval=setInterval(find_iframe, 50); // iframe 読込み待機 function find_iframe(){ let editor_iframe=document.querySelector('.cke_wysiwyg_frame'); if(editor_iframe){ let iframe_doc=editor_iframe.contentWindow.document; if(iframe_doc){ clearInterval(interval); task(iframe_doc); }}} function task(iframe_doc){ // ブログ本文のHTML検索・置換 let interval=setInterval(find_doc, 50); // iframe 読込み待機 function find_doc(){ let body=iframe_doc.querySelector('.cke_editable'); if(body){ clearInterval(interval); setTimeout(()=>{ rewrite(body); }, 800); }} function rewrite(body){ let cke_text=body.innerHTML; if(cke_text){ let regex1=new RegExp(search_worde1); let result1=regex1.test(cke_text); // 検索候補1 の検索結果 if(result1==true){ body.innerHTML=cke_text.replace(search_worde1, rewrite_word); to_h3(iframe_doc); } else { let regex2=new RegExp(search_worde2); let result2=regex2.test(cke_text); // 検索候補2 の検索結果 if(result2==true){ body.innerHTML=cke_text.replace(search_worde2, rewrite_word); to_h3(iframe_doc); } else{ alert("Not Found"); }} }} fast_publish(iframe_doc); //「Ctrl+Shift+Enter」で記事投稿 } // task() function escapeRegExp(string){ // エスケープ文字の処理 let reRegExp=/[\\^$.*+?()[\]{}|]/g; let reHasRegExp=new RegExp(reRegExp.source); return (string && reHasRegExp.test(string)) ? string.replace(reRegExp, '\\$&') : string; } function to_h3(iframe_doc){ // 置換え処理後の自動スクロール 🟦⬜🟦 let h3s=iframe_doc.querySelectorAll('h3'); if(h3s.length>0){ let h3=h3s[h3s.length-1]; h3.scrollIntoView({ block: "start" }); } else{ let html=iframe_doc.documentElement; html.scrollTop=html.scrollHeight; }} function fast_publish(iframe_doc){ iframe_doc.addEventListener('keydown', check_key); function check_key(event){ if(event.ctrlKey && event.shiftKey && event.keyCode==13){ let publish=document.querySelector('button.js-submitButton[publishflg="0"]'); if(publish){ publish.click(); }}}} if(location.pathname.includes('/entry/srventrylist.do')){ // 記事の編集・削除 setTimeout(()=>{ window.close(); }, 1000); } if(location.pathname.includes('/entry/srventryupdateend.do')){ // 投稿確認画面 setTimeout(()=>{ window.close(); }, 1000); }