Node.JsをつかってYouTube検索して動画のタイトルとURLをスプレッドシートに表示させるではurlで使えない文字(/や#などのurlとして特別な意味をもつ文字)で検索するとうまくいきませんでした。そこで今回はそれに対応させます。それからほかのスプレッドシートであってもGASで発行されるsebアプリのurlをおくれば誰でも検索結果を表示させることができるようにします。
Contents
encodeURI関数で変換できるか?
urlで使えない文字に該当する文字としては、< > # ” % : ? / @ ! $ & ‘ ( ) * + , ; = { } | \ ^ [ ]です。
JavaScriptでURIエンコードを行うのが encodeURI()関数ですが、どの文字にも対応しているわけではありません。実際にencodeURI関数を使ってみると
# : ? / @ ! $ & ( ) * + , ; = ‘ は変化しません。それ以外は
< %3C
> %3E
” %22
% %25
{ %7B
} %7D
| %7C
\ %5C
^ %5E
[ %5B
] %5D
半角スペース %20
全角スペース %E3%80%80
のように置き換わります。
そのうえで # : ? / @ ! $ & ( ) * + , ; = ‘ をURLエンコードすると、以下のようになります。
# %23
: %3A
? %3F
/ %2F
@ %40
! %21
$ %24
& %26
( %28
) %29
* %2A
+ %2B
, %2C
; %3B
= %3D
‘ %27
そこで先に手動でencodeURI関数の影響をうけないものを置換しておいて、そのあとencodeURI関数でエンコードします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
function ManualEncoding(str){ str = encodeURI(str); str = str.replaceAll('#', '%23'); str = str.replaceAll(':', '%3A'); str = str.replaceAll('?', '%3F'); str = str.replaceAll('/', '%2F'); str = str.replaceAll('@', '%40'); str = str.replaceAll('!', '%21'); str = str.replaceAll('$', '%24'); str = str.replaceAll('&', '%26'); str = str.replaceAll('(', '%28'); str = str.replaceAll(')', '%29'); str = str.replaceAll('*', '%2A'); str = str.replaceAll('+', '%2B'); str = str.replaceAll(',', '%2C'); str = str.replaceAll(';', '%3B'); str = str.replaceAll('=', '%3D'); str = str.replaceAll('\'', '%27'); return str; } |
デコードするのであれば
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
function ManualDecoding(str){ str = str.replaceAll('%23', '#'); str = str.replaceAll('%3A', ':'); str = str.replaceAll('%3F', '?'); str = str.replaceAll('%2F', '/'); str = str.replaceAll('%40', '@'); str = str.replaceAll('%21', '!'); str = str.replaceAll('%24', '$'); str = str.replaceAll('%26', '&'); str = str.replaceAll('%28', '('); str = str.replaceAll('%29', ')'); str = str.replaceAll('%2A', '*'); str = str.replaceAll('%2B', '+'); str = str.replaceAll('%2C', ','); str = str.replaceAll('%3B', ';'); str = str.replaceAll('%3D', '='); str = str.replaceAll('%27', '\''); str = decodeURI(str); return str; } |
replaceAll関数がサポートされていない場合
古いバージョンのNode.jsだとreplaceAll関数がサポートされていません。そこで置換元の文字列で対象の文字列を分割して、置換先の文字列をはさんで結合させます。
1 2 3 4 5 |
function ReplaceAll(str, oldstr, newstr){ let arrey = str.split(oldstr); str = arrey.join(newstr); return str; } |
これだとエンコード・デコードする処理は以下のようになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
function ManualEncoding2(str){ str = encodeURI(str); str = ReplaceAll(str, '#', '%23'); str = ReplaceAll(str, ':', '%3A'); str = ReplaceAll(str, '?', '%3F'); str = ReplaceAll(str, '/', '%2F'); str = ReplaceAll(str, '@', '%40'); str = ReplaceAll(str, '!', '%21'); str = ReplaceAll(str, '$', '%24'); str = ReplaceAll(str, '&', '%26'); str = ReplaceAll(str, '(', '%28'); str = ReplaceAll(str, ')', '%29'); str = ReplaceAll(str, '*', '%2A'); str = ReplaceAll(str, '+', '%2B'); str = ReplaceAll(str, ',', '%2C'); str = ReplaceAll(str, ';', '%3B'); str = ReplaceAll(str, '=', '%3D'); str = ReplaceAll(str, '\'', '%27'); return str; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
function ManualDecoding2(str){ str = ReplaceAll(str, '%23', '#'); str = ReplaceAll(str, '%3A', ':'); str = ReplaceAll(str, '%3F', '?'); str = ReplaceAll(str, '%2F', '/'); str = ReplaceAll(str, '%40', '@'); str = ReplaceAll(str, '%21', '!'); str = ReplaceAll(str, '%24', '$'); str = ReplaceAll(str, '%26', '&'); str = ReplaceAll(str, '%28', '('); str = ReplaceAll(str, '%29', ')'); str = ReplaceAll(str, '%2A', '*'); str = ReplaceAll(str, '%2B', '+'); str = ReplaceAll(str, '%2C', ','); str = ReplaceAll(str, '%3B', ';'); str = ReplaceAll(str, '%3D', '='); str = ReplaceAll(str, '%27', '\''); str = decodeURI(str); return str; } |
urlで使えない文字で検索したときもYouTubeの検索結果を取得できるようにする
Node.js側の処理ですが、検索結果を取得するためのurlは https://www.youtube.com/results?search_query=keywordなのでエンコードが必要です。そうでないと「C#」で検索したとき期待している結果が取得できません。
それからGASで発行されるsebアプリのurlをおくれば誰でも検索結果を表示させることができるようにします。
GAS側の処理
ボタンを作成してボタンがクリックされたらfunc1関数が実行されるようにしておきます。urlはWebアプリとしてデプロイしたときに発行されるurl、herokuAppUrlはhttps://boiling-spire-97714.herokuapp.comを指定してください。
ボタンをクリックすると、H4セルに書かれているキーワードでYouTube検索をして、H5セルに書かれている数の件数だけ結果を取得・表示します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
function func1(){ let spreadsheet = SpreadsheetApp.getActiveSpreadsheet(); let sheet = spreadsheet.getSheetByName(sheetName); let keyword = sheet.getRange(4, 8).getValue(); let max = sheet.getRange(5, 8).getValue(); if(keyword == '') return; if(isNaN(max)) return; let herokuAppUrl = 'https://boiling-spire-97714.herokuapp.com'; const url = 'https://script.google.com/macros/s/XXXXXXXXXXXXXXXXXXXXXXXXXX/exec'; let encodedKeyWord = ManualEncoding(keyword); let encodedUrl = ManualEncoding(url); let url2 = `${herokuAppUrl}/search/keyword/${encodedKeyWord}/max/${max}/url/${encodedUrl}`; console.log(url2); UrlFetchApp.fetch(url2); } |
Node.js側の処理
main関数を変更し、ManualEncoding2関数、ManualDecoding2関数、ReplaceAll関数を追加します。あとはNode.JsをつかってYouTube検索して動画のタイトルとURLをスプレッドシートに表示させると同じです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
async function main(){ app.get('/', (req, res) => { res.send('ここはトップページ<br>/search/keyword/:keyword/max/:max/url/:urlでスプレッドシートに検索結果を表示させます。'); }); app.get('/search/keyword/:keyword/max/:max/url/:url', async(req, res) => { try { let keyword = req.params.keyword; let max = req.params.max; let url = req.params.url; url = ManualDecoding2(url); console.log(url); if(keyword == undefined || isNaN(max)){ res.send('引数が不正'); console.log('引数が不正'); return; } // この段階ではエンコードされていたkeywordはデコードされている console.log(keyword + ' で ' + max + ' 件 取得します。'); // 検索結果を取得するためのurlは https://www.youtube.com/results?search_query=keyword // keywordはエンコードされていなければならない // keywordをエンコードする keyword = ManualEncoding2(keyword); // あとはそのまま let datas = await Search (keyword, max); let sendText = CreateSendText(datas); try { SaveToSpreadsheet(sendText, url); res.send(sendText); } catch(err){ res.send('SaveToSpreadsheetが失敗' + err); console.log('SaveToSpreadsheetが失敗 ' + err); } } catch(err){ res.send('引数が不正'); console.log('引数が不正 ' + err); } }); var server = app.listen(portNumber, function() { console.log("listening at port %s リッスン中!", server.address().port); }); return; } |