<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:media="http://search.yahoo.com/mrss/"><channel><title>GoogleAppsScript on IT 空間</title><link>https://blog.jiatool.com/tags/GoogleAppsScript/</link><description>Recent content in GoogleAppsScript on IT 空間</description><generator>Hugo -- gohugo.io</generator><language>zh</language><managingEditor>jia@jiatool.com (Jia)</managingEditor><webMaster>jia@jiatool.com (Jia)</webMaster><copyright>&amp;copy;{year}, Jia All Rights Reserved</copyright><lastBuildDate>Fri, 30 Dec 2022 21:00:00 +0800</lastBuildDate><atom:link href="https://blog.jiatool.com/tags/GoogleAppsScript/index.xml" rel="self" type="application/rss+xml"/><item><title>「TDX 運輸資料流通服務平台」Google Apps Script 範例，PTX 平台的升級版~</title><link>https://blog.jiatool.com/posts/tdx_google_apps_script/</link><pubDate>Fri, 30 Dec 2022 21:00:00 +0800</pubDate><author>jia@jiatool.com (Jia)</author><atom:modified>Fri, 30 Dec 2022 21:00:00 +0800</atom:modified><guid>https://blog.jiatool.com/posts/tdx_google_apps_script/</guid><description>前言 先預祝 2023 元旦快樂~🎉🎉🎉 恭喜各位又老一歲了(? 之前介紹的「PTX 公共運輸資訊平台 」已經確定在今年 2022/12/01 正式落日，所以後來我有寫一篇將取代 PTX 平</description><content:encoded>&lt;h2 id="前言">前言&lt;/h2>
&lt;p>先預祝 2023 元旦快樂~🎉🎉🎉 恭喜各位又老一歲了(?&lt;/p>
&lt;br/>
&lt;p>之前介紹的「&lt;a href="https://blog.jiatool.com/posts/ptx_intro" target="_blank" rel="noopener">
PTX 公共運輸資訊平台
&lt;/a>」已經確定在今年 2022/12/01 正式落日，所以後來我有寫一篇將取代 PTX 平台的「TDX 運輸資訊整合流通服務平台」介紹，並示範如何使用 Python 來串接。&lt;/p>
&lt;figure >
&lt;img data-src="https://res.cloudinary.com/jiablog/tdx_python/ptx_close2.jpg" alt="PTX 平台關閉" data-caption="PTX 平台關閉" src="data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='850px' height='' viewBox='0 0 24 24'%3E%3Cpath fill='none' d='M0 0h24v24H0V0z'/%3E%3Cpath fill='%23aaa' d='M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-1 16H6c-.55 0-1-.45-1-1V6c0-.55.45-1 1-1h12c.55 0 1 .45 1 1v12c0 .55-.45 1-1 1zm-4.44-6.19l-2.35 3.02-1.56-1.88c-.2-.25-.58-.24-.78.01l-1.74 2.23c-.26.33-.02.81.39.81h8.98c.41 0 .65-.47.4-.8l-2.55-3.39c-.19-.26-.59-.26-.79 0z'/%3E%3C/svg%3E" class="lazyload" style="width:850px;height:;"/>
&lt;figcaption style="text-align: center;">
PTX 平台關閉
&lt;/figcaption>
&lt;/figure>
&lt;br/>
&lt;p>因為剛好有網友詢問，所以本篇也會使用 Google Apps Script 來取得 TDX 的交通資料，兩者在 API 認證授權機制差異不小。&lt;br />
關於 TDX 平台的介紹、如何註冊並取得API金鑰等資訊&lt;a href="https://blog.jiatool.com/posts/ptx_intro" target="_blank" rel="noopener">
之前這篇文章
&lt;/a>都講解過了，這篇我們就直接進入 Google Apps Script 程式主題。&lt;/p>
&lt;br/>
&lt;figure >
&lt;img data-src="https://res.cloudinary.com/jiablog/tdx_python/tdx_home.jpg" alt="TDX 運輸資訊整合流通服務平台" data-caption="TDX 運輸資訊整合流通服務平台" src="data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='700px' height='' viewBox='0 0 24 24'%3E%3Cpath fill='none' d='M0 0h24v24H0V0z'/%3E%3Cpath fill='%23aaa' d='M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-1 16H6c-.55 0-1-.45-1-1V6c0-.55.45-1 1-1h12c.55 0 1 .45 1 1v12c0 .55-.45 1-1 1zm-4.44-6.19l-2.35 3.02-1.56-1.88c-.2-.25-.58-.24-.78.01l-1.74 2.23c-.26.33-.02.81.39.81h8.98c.41 0 .65-.47.4-.8l-2.55-3.39c-.19-.26-.59-.26-.79 0z'/%3E%3C/svg%3E" class="lazyload" style="width:700px;height:;"/>
&lt;figcaption style="text-align: center;">
TDX 運輸資訊整合流通服務平台
&lt;/figcaption>
&lt;/figure>
&lt;br/>
&lt;!--adsense-->
&lt;br/>
&lt;h2 id="取得api金鑰">取得API金鑰&lt;/h2>
&lt;blockquote>
&lt;ol>
&lt;li>TDX 平臺現階段開放每位會員最多建立三把API金鑰，每把API金鑰由一組 Client Id 和 Client Secret 所組成。&lt;/li>
&lt;li>每個呼叫來源端 IP 呼叫次數限制為 50 次/秒。&lt;/li>
&lt;li>TDX 平臺 API 採 OIDC Client Credential 機制進行身分驗證，程式介接範例可參考範例程式碼。&lt;/li>
&lt;/ol>
&lt;/blockquote>
&lt;p>登入 TDX 平台後，前往 會員中心 &amp;gt; (左邊) &amp;gt; 資料服務 &amp;gt; API金鑰 頁面，點擊 &amp;quot;編輯&amp;quot; 即可查看 &amp;quot;Client Id&amp;quot; 和 &amp;quot;Client Secret&amp;quot;，將這兩組字串記錄下來，call API 時需要帶上。&lt;/p>
&lt;figure >
&lt;img data-src="https://res.cloudinary.com/jiablog/tdx_python/tdx_api_key.png" alt="API金鑰 頁面" data-caption="API金鑰 頁面" src="data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='800px' height='' viewBox='0 0 24 24'%3E%3Cpath fill='none' d='M0 0h24v24H0V0z'/%3E%3Cpath fill='%23aaa' d='M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-1 16H6c-.55 0-1-.45-1-1V6c0-.55.45-1 1-1h12c.55 0 1 .45 1 1v12c0 .55-.45 1-1 1zm-4.44-6.19l-2.35 3.02-1.56-1.88c-.2-.25-.58-.24-.78.01l-1.74 2.23c-.26.33-.02.81.39.81h8.98c.41 0 .65-.47.4-.8l-2.55-3.39c-.19-.26-.59-.26-.79 0z'/%3E%3C/svg%3E" class="lazyload" style="width:800px;height:;"/>
&lt;figcaption style="text-align: center;">
API金鑰 頁面
&lt;/figcaption>
&lt;/figure>
&lt;br/>
&lt;p>有關 API 金鑰服務使用流程說明，請參考&lt;a href="https://tdx.transportdata.tw/about/service" target="_blank" rel="noopener">
官方說明
&lt;/a>。&lt;/p>
&lt;br/>
&lt;h2 id="api-認證授權機制">API 認證授權機制&lt;/h2>
&lt;p>詳細步驟說明如下:&lt;/p>
&lt;ol>
&lt;li>取得 Access Token&lt;/li>
&lt;/ol>
&lt;p>依照以下格式發出請求：&lt;/p>
&lt;pre>&lt;code>Request URL: https://tdx.transportdata.tw/auth/realms/TDXConnect/protocol/openid-connect/token
Request Method: POST
Request Headers:
content-type: application/x-www-form-urlencoded
data:
grant_type: client_credentials
client_id: &amp;lt;your_client_id&amp;gt;
client_secret: &amp;lt;your_client_secret&amp;gt;
&lt;/code>&lt;/pre>&lt;p>data 參數說明如下:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Key&lt;/th>
&lt;th>Value&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>grant_type&lt;/code>&lt;/td>
&lt;td>固定使用 &lt;code>client_credentials&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>client_id&lt;/code>&lt;/td>
&lt;td>您的 Client Id，從 TDX 會員中心取得&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>client_secret&lt;/code>&lt;/td>
&lt;td>您的 Client Secret，從 TDX 會員中心取得&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>回傳資料是 JSON 格式，其中包含 &lt;code>access_token&lt;/code> 參數，就是我們要的 Access Token。&lt;/p>
&lt;br/>
&lt;p>對應的 Google Apps Script code：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre class="chroma">&lt;code class="language-JavaScript" data-lang="JavaScript">&lt;span class="kd">function&lt;/span> &lt;span class="nx">GetAuthorizationToken&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;span class="kd">var&lt;/span> &lt;span class="nx">token_url&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;https://tdx.transportdata.tw/auth/realms/TDXConnect/protocol/openid-connect/token&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="kd">var&lt;/span> &lt;span class="nx">options&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;span class="s2">&amp;#34;method&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;post&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;span class="s2">&amp;#34;headers&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;span class="s2">&amp;#34;content-type&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;application/x-www-form-urlencoded&amp;#34;&lt;/span>
&lt;span class="p">},&lt;/span>
&lt;span class="s2">&amp;#34;payload&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;span class="s2">&amp;#34;grant_type&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;client_credentials&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;span class="s2">&amp;#34;client_id&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;&amp;lt;YOUR CLIENT ID&amp;gt;&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;span class="s2">&amp;#34;client_secret&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;&amp;lt;YOUR CLIENT SECRET&amp;gt;&amp;#39;&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="p">};&lt;/span>
&lt;span class="kd">var&lt;/span> &lt;span class="nx">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">UrlFetchApp&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">fetch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">token_url&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">options&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="nx">outData&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">JSON&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">parse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getContentText&lt;/span>&lt;span class="p">());&lt;/span>
&lt;span class="c1">// console.log(outData);
&lt;/span>&lt;span class="c1">&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="nx">outData&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;access_token&amp;#39;&lt;/span>&lt;span class="p">];&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;br/>
&lt;ol start="2">
&lt;li>呼叫 TDX API 服務，取得數據資料&lt;/li>
&lt;/ol>
&lt;pre>&lt;code>Request URL: TDX_API_URI
Request Method: GET
Request Headers:
authorization: Bearer &amp;lt;Access_Token&amp;gt;
&lt;/code>&lt;/pre>&lt;p>* 若 Access Token 時間超過有效期限(第一步驟收到回應中的 expires_in 參數)，則再重新透過第一步驟取得即可。&lt;/p>
&lt;br/>
&lt;p>對應的 Google Apps Script code：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre class="chroma">&lt;code class="language-JavaScript" data-lang="JavaScript">&lt;span class="kd">function&lt;/span> &lt;span class="nx">doGet&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">e&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;span class="kd">var&lt;/span> &lt;span class="nx">url&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;https://tdx.transportdata.tw/api/basic/v2/Rail/TRA/LiveBoard/Station/1000?$filter=Direction eq 1&amp;amp;$format=JSON&amp;#39;&lt;/span>
&lt;span class="kd">var&lt;/span> &lt;span class="nx">options&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;span class="s2">&amp;#34;method&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;get&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;span class="s2">&amp;#34;headers&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="s2">&amp;#34;authorization&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;Bearer &amp;#34;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">GetAuthorizationToken&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">}&lt;/span>
&lt;span class="p">};&lt;/span>
&lt;span class="kd">var&lt;/span> &lt;span class="nx">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">UrlFetchApp&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">fetch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">url&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">options&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="c1">// console.log(response.getResponseCode());
&lt;/span>&lt;span class="c1">&lt;/span> &lt;span class="kd">var&lt;/span> &lt;span class="nx">outData&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">JSON&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">parse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getContentText&lt;/span>&lt;span class="p">());&lt;/span>
&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">outData&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="k">return&lt;/span> &lt;span class="nx">ContentService&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">createTextOutput&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">JSON&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">stringify&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">outData&lt;/span>&lt;span class="p">)).&lt;/span>&lt;span class="nx">setMimeType&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">ContentService&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">MimeType&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">JSON&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;br/>
&lt;p>* 官方的說明文件： &lt;a href="https://github.com/tdxmotc/SampleCode">https://github.com/tdxmotc/SampleCode&lt;/a>&lt;/p>
&lt;br/>
&lt;h2 id="完整-google-apps-script-程式範例">完整 Google Apps Script 程式範例&lt;/h2>
&lt;p>以同樣條件當範例查詢：台鐵&amp;quot;台北&amp;quot;車站即時的列車到離站看板資訊，並且只要&amp;quot;逆行&amp;quot;的列車&lt;/p>
&lt;p>查詢的 URL 會長得像這樣：&lt;br />
&lt;code>https://tdx.transportdata.tw/api/basic/v2/Rail/TRA/LiveBoard/Station/1000?$filter=Direction eq 1&amp;amp;$format=JSON&lt;/code>&lt;/p>
&lt;br/>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre class="chroma">&lt;code class="language-JavaScript" data-lang="JavaScript">&lt;span class="kd">var&lt;/span> &lt;span class="nx">CLIENT_ID&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;&amp;lt;YOUR CLIENT ID&amp;gt;&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="kd">var&lt;/span> &lt;span class="nx">CLIENT_SECRET&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;&amp;lt;YOUR CLIENT SECRET&amp;gt;&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="kd">function&lt;/span> &lt;span class="nx">doGet&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">e&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;span class="kd">var&lt;/span> &lt;span class="nx">url&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;https://tdx.transportdata.tw/api/basic/v2/Rail/TRA/LiveBoard/Station/1000?$filter=Direction eq 1&amp;amp;$format=JSON&amp;#39;&lt;/span>
&lt;span class="kd">var&lt;/span> &lt;span class="nx">options&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;span class="s2">&amp;#34;method&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;get&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;span class="s2">&amp;#34;headers&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="s2">&amp;#34;authorization&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;Bearer &amp;#34;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">GetAuthorizationToken&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">}&lt;/span>
&lt;span class="p">};&lt;/span>
&lt;span class="kd">var&lt;/span> &lt;span class="nx">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">UrlFetchApp&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">fetch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">url&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">options&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="c1">// console.log(response.getResponseCode());
&lt;/span>&lt;span class="c1">&lt;/span> &lt;span class="kd">var&lt;/span> &lt;span class="nx">outData&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">JSON&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">parse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getContentText&lt;/span>&lt;span class="p">());&lt;/span>
&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">outData&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="k">return&lt;/span> &lt;span class="nx">ContentService&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">createTextOutput&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">JSON&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">stringify&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">outData&lt;/span>&lt;span class="p">)).&lt;/span>&lt;span class="nx">setMimeType&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">ContentService&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">MimeType&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">JSON&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="kd">function&lt;/span> &lt;span class="nx">GetAuthorizationToken&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;span class="kd">var&lt;/span> &lt;span class="nx">token_url&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;https://tdx.transportdata.tw/auth/realms/TDXConnect/protocol/openid-connect/token&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="kd">var&lt;/span> &lt;span class="nx">options&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;span class="s2">&amp;#34;method&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;post&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;span class="s2">&amp;#34;headers&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;span class="s2">&amp;#34;content-type&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;application/x-www-form-urlencoded&amp;#34;&lt;/span>
&lt;span class="p">},&lt;/span>
&lt;span class="s2">&amp;#34;payload&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;span class="s2">&amp;#34;grant_type&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;client_credentials&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;span class="s2">&amp;#34;client_id&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">CLIENT_ID&lt;/span>&lt;span class="p">,&lt;/span>
&lt;span class="s2">&amp;#34;client_secret&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">CLIENT_SECRET&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="p">};&lt;/span>
&lt;span class="kd">var&lt;/span> &lt;span class="nx">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">UrlFetchApp&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">fetch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">token_url&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">options&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="nx">outData&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">JSON&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">parse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getContentText&lt;/span>&lt;span class="p">());&lt;/span>
&lt;span class="c1">// console.log(outData);
&lt;/span>&lt;span class="c1">&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="nx">outData&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;access_token&amp;#39;&lt;/span>&lt;span class="p">];&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>透過上方的 &amp;quot;執行&amp;quot; 確認沒問題後，可參考&lt;a href="https://blog.jiatool.com/posts/ptx_google_apps_script" target="_blank" rel="noopener">
這篇的教學
&lt;/a>將程式部署起來。&lt;/p>
&lt;br/>
&lt;h2 id="其他程式範例">其他程式範例&lt;/h2>
&lt;p>除了參考我上面的 Google Apps Script 範例，與我上次整理的 &lt;a href="https://blog.jiatool.com/posts/tdx_python" target="_blank" rel="noopener">
Python 教學
&lt;/a>，TDX 官方有提供 C#、JavaScript、Java、PHP、R 程式語言的範例程式碼，需要的可前往參考 (&lt;a href="https://github.com/tdxmotc/SampleCode" target="_blank" rel="noopener">
GitHub
&lt;/a>)。&lt;/p>
&lt;br/>
&lt;!--adsense-->
&lt;h2 id="結語">結語&lt;/h2>
&lt;p>如果你想做的應用需要「公共運輸」相關資料，那應該就是串接 TDX 平台，資料齊全、方便很多~&lt;/p>
&lt;br/>
&lt;p>歡迎追蹤『&lt;a href="https://www.facebook.com/jiatool" target="_blank" rel="noopener">
IT空間
&lt;/a>』FB 粉專，取得最新發文通知🔔&lt;/p>
&lt;br/>
&lt;br/>
&lt;hr />
&lt;p>參考：&lt;br />
&lt;a href="https://tdx.transportdata.tw/" target="_blank" rel="noopener">
TDX 運輸資料流通服務平台 | 官網
&lt;/a>&lt;br />
&lt;a href="https://tdx.transportdata.tw/api-service/swagger" target="_blank" rel="noopener">
TDX API 說明 | Swagger 文件工具
&lt;/a>&lt;br />
&lt;a href="https://github.com/tdxmotc/SampleCode" target="_blank" rel="noopener">
TDX 官方介接說明與範例程式碼 | GitHub
&lt;/a>&lt;br />
&lt;a href="https://tdx.transportdata.tw/about/faq" target="_blank" rel="noopener">
TDX 平台常見問題 | 官網
&lt;/a>&lt;/p>
&lt;br/>
&lt;blockquote>
&lt;p>與其埋怨暗路，不如自己點燈。&lt;/p>
&lt;/blockquote></content:encoded><dc:creator>Jia</dc:creator><media:content url="https://blog.jiatool.comimages/cover/tdx_google_apps_script.jpg" medium="image"><media:title type="html">featured image</media:title></media:content><media:content url="https://blog.jiatool.comimages/posts/tdx_google_apps_script_meta.jpg" medium="image"><media:title type="html">meta image</media:title></media:content><category>TDX</category><category>API</category><category>交通</category><category>公共運輸</category><category>GoogleAppsScript</category><category>分享</category></item><item><title>「PTX 公共運輸資訊平台」Google Apps Script 範例程式</title><link>https://blog.jiatool.com/posts/ptx_google_apps_script/</link><pubDate>Sat, 14 May 2022 20:45:00 +0800</pubDate><author>jia@jiatool.com (Jia)</author><atom:modified>Thu, 03 Nov 2022 21:50:00 +0800</atom:modified><guid>https://blog.jiatool.com/posts/ptx_google_apps_script/</guid><description>因部內目前已收攏資料於TDX運輸資訊整合流通服務平台，本平台預計將於2022/12/1落日，屆時您的會員金鑰將於2022/12/1起停用。 為</description><content:encoded>&lt;br/>
&lt;div class="notices warning" data-title="P t x 平台將停止使用">
因部內目前已收攏資料於TDX運輸資訊整合流通服務平台，本平台預計將於2022/12/1落日，屆時您的會員金鑰將於2022/12/1起停用。&lt;br />
為避免您短時間內需移轉之困擾，即日起本平台不再受理審核會員註冊。&lt;br />
建議您依據身分類型至TDX申請會員，非常感謝您的支持。
&lt;/div>
&lt;h2 id="前言">前言&lt;/h2>
&lt;p>在之前的兩篇文章裡，我們分別介紹 &lt;a href="https://ptx.transportdata.tw/PTX/" target="_blank" rel="noopener">
PTX 公共運輸整合資訊流通服務平台
&lt;/a> 整合多項大眾運輸的資料、說明 OData（Open Data Protocol）格式如何使用，與實際使用 Python 來串接 PTX 平台。&lt;/p>
&lt;p>本篇文章將帶你換成使用 Google Apps Script 來串接 PTX 平台，取得這些相關數據資料 💾，還可以做出自己的簡易 API。&lt;br />
(Google Apps Script 其實是基於 JavaScript 來的，所以基本的語法大致都相同。)&lt;/p>
&lt;br/>
&lt;p>關於 PTX 平台我寫了三篇文章來介紹與程式教學：&lt;/p>
&lt;ol>
&lt;li>&lt;a href="https://blog.jiatool.com/posts/ptx_intro" target="_blank" rel="noopener">
「PTX 公共運輸資訊平台」API 介紹 (含 Odata 說明)
&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://blog.jiatool.com/posts/ptx_python" target="_blank" rel="noopener">
「PTX 公共運輸資訊平台」Python 範例
&lt;/a>&lt;/li>
&lt;li>「PTX 公共運輸資訊平台」Google Apps Script 範例 &amp;lt;&amp;ndash; 本篇&lt;/li>
&lt;/ol>
&lt;br/>
&lt;figure >
&lt;img data-src="https://res.cloudinary.com/jiablog/ptx_intro/ptx_home.jpg" alt="PTX 公共運輸整合資訊流通服務平台" data-caption="PTX 公共運輸整合資訊流通服務平台" src="data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='650px' height='' viewBox='0 0 24 24'%3E%3Cpath fill='none' d='M0 0h24v24H0V0z'/%3E%3Cpath fill='%23aaa' d='M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-1 16H6c-.55 0-1-.45-1-1V6c0-.55.45-1 1-1h12c.55 0 1 .45 1 1v12c0 .55-.45 1-1 1zm-4.44-6.19l-2.35 3.02-1.56-1.88c-.2-.25-.58-.24-.78.01l-1.74 2.23c-.26.33-.02.81.39.81h8.98c.41 0 .65-.47.4-.8l-2.55-3.39c-.19-.26-.59-.26-.79 0z'/%3E%3C/svg%3E" class="lazyload" style="width:650px;height:;"/>
&lt;figcaption style="text-align: center;">
PTX 公共運輸整合資訊流通服務平台
&lt;/figcaption>
&lt;/figure>
&lt;br/>
&lt;p>後來發現 PTX 竟然只服務到今年(2022年)底，之後會改以 TDX 運輸資訊整合流通服務平台，因此又特別寫一篇文章帶大家快速了解一下 TDX 平台：&lt;br />
&lt;a href="https://blog.jiatool.com/posts/tdx_python" target="_blank" rel="noopener">
「TDX 運輸資料流通服務平臺」含 Python 範例程式，PTX 平台的升級版~
&lt;/a>&lt;/p>
&lt;br/>
&lt;!--adsense-->
&lt;h2 id="創建-google-apps-script-專案">創建 Google Apps Script 專案&lt;/h2>
&lt;p>可能有些人沒接觸過 Google Apps Script，我這邊先說明兩種創建的方式：&lt;/p>
&lt;p>第一種到 &lt;a href="https://script.google.com/home/start" target="_blank" rel="noopener">
Apps Script 頁面
&lt;/a> 創建專案，預設會儲存在你 Google 雲端硬碟的根目錄。&lt;/p>
&lt;figure >
&lt;img data-src="https://res.cloudinary.com/jiablog/ptx_intro/create_gas1.jpg" alt="創建 Google Apps Script 專案 -1" data-caption="創建 Google Apps Script 專案 -1" src="data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='800px' height='' viewBox='0 0 24 24'%3E%3Cpath fill='none' d='M0 0h24v24H0V0z'/%3E%3Cpath fill='%23aaa' d='M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-1 16H6c-.55 0-1-.45-1-1V6c0-.55.45-1 1-1h12c.55 0 1 .45 1 1v12c0 .55-.45 1-1 1zm-4.44-6.19l-2.35 3.02-1.56-1.88c-.2-.25-.58-.24-.78.01l-1.74 2.23c-.26.33-.02.81.39.81h8.98c.41 0 .65-.47.4-.8l-2.55-3.39c-.19-.26-.59-.26-.79 0z'/%3E%3C/svg%3E" class="lazyload" style="width:800px;height:;"/>
&lt;figcaption style="text-align: center;">
創建 Google Apps Script 專案 -1
&lt;/figcaption>
&lt;/figure>
&lt;p>第二種在 Google 雲端硬碟裡新增 Google Apps Script 檔案 (如果選單內沒看到，可以點擊 &amp;quot;連結更多應用程式&amp;quot; 去尋找&amp;amp;加入)。&lt;/p>
&lt;figure >
&lt;img data-src="https://res.cloudinary.com/jiablog/ptx_intro/create_gas2.jpg" alt="創建 Google Apps Script 專案 -2" data-caption="創建 Google Apps Script 專案 -2" src="data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='500px' height='' viewBox='0 0 24 24'%3E%3Cpath fill='none' d='M0 0h24v24H0V0z'/%3E%3Cpath fill='%23aaa' d='M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-1 16H6c-.55 0-1-.45-1-1V6c0-.55.45-1 1-1h12c.55 0 1 .45 1 1v12c0 .55-.45 1-1 1zm-4.44-6.19l-2.35 3.02-1.56-1.88c-.2-.25-.58-.24-.78.01l-1.74 2.23c-.26.33-.02.81.39.81h8.98c.41 0 .65-.47.4-.8l-2.55-3.39c-.19-.26-.59-.26-.79 0z'/%3E%3C/svg%3E" class="lazyload" style="width:500px;height:;"/>
&lt;figcaption style="text-align: center;">
創建 Google Apps Script 專案 -2
&lt;/figcaption>
&lt;/figure>
&lt;p>選擇一種你順手的方式創建專案即可，好了我們就開始吧 🚌&lt;/p>
&lt;br/>
&lt;h2 id="api-認證授權機制">API 認證授權機制&lt;/h2>
&lt;p>一樣先撰寫放在 Header 裡的 HMAC 認證授權 🔓，關於目前 PTX 平台採用 HMAC 認證授權機制說明，可以參考&lt;a href="https://blog.jiatool.com/posts/ptx_python" target="_blank" rel="noopener">
上一篇文章
&lt;/a>。&lt;/p>
&lt;p>參數如下：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Key&lt;/th>
&lt;th>Value&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>Authorization&lt;/code>&lt;/td>
&lt;td>&lt;code>hmac username=&amp;quot;APP ID&amp;quot;, algorithm=&amp;quot;hmac-sha1&amp;quot;, headers=&amp;quot;x-date&amp;quot;, signature=&amp;quot;Base64(HMAC-SHA1(&amp;quot;x-date: &amp;quot; + x-date , APP Key))&amp;quot;&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>x-date&lt;/code>&lt;/td>
&lt;td>&lt;code>Wed, 19 Apr 2017 08:37:50 GMT&lt;/code>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>※ 建議於每次請求 API 服務當下建立新的 signature，簽章時效性為 5 分鐘。&lt;/p>
&lt;br/>
&lt;p>如果 HMAC 認證有問題、未符合身份驗證，它會回覆下列訊息：&lt;/p>
&lt;ul>
&lt;li>HTTP Status Code 403：
&lt;ul>
&lt;li>(1) HMAC signature cannot be verified, a valid date or x-date header is required for HMAC Authentication （x-date 的間隔時間超過定義的 clock skew 秒數）&lt;/li>
&lt;li>(2) HMAC signature does not match （日期格式正確，但簽章演算法有問題）&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>HTTP Status Code 401：
&lt;ul>
&lt;li>(1) Unauthorized （未帶簽章，未經授權）&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;br/>
&lt;br/>
&lt;p>依照上方說明，使用 Google Apps Script 語法把這個 HMAC 編碼撰寫出來：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre class="chroma">&lt;code class="language-JavaScript" data-lang="JavaScript">&lt;span class="kd">function&lt;/span> &lt;span class="nx">GetAuthorizationHeader&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;span class="kd">var&lt;/span> &lt;span class="nx">AppID&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;&amp;lt;YOUR APP ID&amp;gt;&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="kd">var&lt;/span> &lt;span class="nx">AppKey&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;&amp;lt;YOUR APP KEY&amp;gt;&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="kd">var&lt;/span> &lt;span class="nx">xdate&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nb">Date&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="nx">toGMTString&lt;/span>&lt;span class="p">();&lt;/span>
&lt;span class="c1">//HMAC-SHA1 運算
&lt;/span>&lt;span class="c1">&lt;/span> &lt;span class="kd">var&lt;/span> &lt;span class="nx">signature&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">Utilities&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">computeHmacSignature&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">Utilities&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">MacAlgorithm&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">HMAC_SHA_1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;x-date: &amp;#34;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">xdate&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">AppKey&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="c1">//轉成Base64
&lt;/span>&lt;span class="c1">&lt;/span> &lt;span class="kd">var&lt;/span> &lt;span class="nx">HMAC&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">Utilities&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">base64Encode&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">signature&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="kd">var&lt;/span> &lt;span class="nx">Authorization&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;hmac username=\&amp;#34;&amp;#39;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">AppID&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="s1">&amp;#39;\&amp;#34;, algorithm=\&amp;#34;hmac-sha1\&amp;#34;, headers=\&amp;#34;x-date\&amp;#34;, signature=\&amp;#34;&amp;#39;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">HMAC&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="s1">&amp;#39;\&amp;#34;&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="k">return&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="s1">&amp;#39;Authorization&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">Authorization&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;x-date&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">xdate&lt;/span> &lt;span class="p">,&lt;/span>&lt;span class="s1">&amp;#39;Accept-Encoding&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;gzip&amp;#39;&lt;/span>&lt;span class="p">};&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;br/>
&lt;h2 id="google-apps-script-程式範例">Google Apps Script 程式範例&lt;/h2>
&lt;p>我們一樣使用上次的例子：&lt;/p>
&lt;p>台鐵&amp;quot;台北&amp;quot;車站即時的列車到離站看板資訊，並且只要&amp;quot;逆行&amp;quot;的列車&lt;/p>
&lt;br/>
&lt;p>先進到 PTX 平台的 Swagger 文件，找到 &amp;quot;&lt;a href="https://ptx.transportdata.tw/MOTC/?urls.primaryName=%E8%BB%8C%E9%81%93V2#/TRA/TRAApi_LiveBoard_2153_1" target="_blank" rel="noopener">
取得指定[車站]列車即時到離站電子看板
&lt;/a>&amp;quot;：&lt;/p>
&lt;figure >
&lt;img data-src="https://res.cloudinary.com/jiablog/ptx_intro/LiveBoard.png" alt="列車即時到離站電子看板" data-caption="列車即時到離站電子看板" src="data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='900px' height='' viewBox='0 0 24 24'%3E%3Cpath fill='none' d='M0 0h24v24H0V0z'/%3E%3Cpath fill='%23aaa' d='M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-1 16H6c-.55 0-1-.45-1-1V6c0-.55.45-1 1-1h12c.55 0 1 .45 1 1v12c0 .55-.45 1-1 1zm-4.44-6.19l-2.35 3.02-1.56-1.88c-.2-.25-.58-.24-.78.01l-1.74 2.23c-.26.33-.02.81.39.81h8.98c.41 0 .65-.47.4-.8l-2.55-3.39c-.19-.26-.59-.26-.79 0z'/%3E%3C/svg%3E" class="lazyload" style="width:900px;height:;"/>
&lt;figcaption style="text-align: center;">
列車即時到離站電子看板
&lt;/figcaption>
&lt;/figure>
&lt;p>填入 &amp;quot;台北車站&amp;quot; 代碼 &lt;code>1000&lt;/code> (&lt;a href="https://tip.railway.gov.tw/tra-tip-web/tip/tip001/tip111/view" target="_blank" rel="noopener">
車站代碼表
&lt;/a>)，並依照下方 Responses 欄位說明 &amp;quot;Direction&amp;quot; 代表順逆行(0:'順行', 1:'逆行')，在 $filter 填入 &lt;code>Direction eq 1&lt;/code>，指定 &lt;code>JSON&lt;/code> 格式，最後點擊藍色 &amp;quot;Execute&amp;quot; 按鈕，看看回傳結果是不是我們想要的~&lt;/p>
&lt;p>(關於這些 Odata 的寫法，我在&lt;a href="https://blog.jiatool.com/posts/ptx_intro" target="_blank" rel="noopener">
第一篇文章
&lt;/a>有說明，忘記的可以回去看看)&lt;/p>
&lt;br/>
&lt;p>它顯示的 &amp;quot;Request URL&amp;quot; 長得像這樣：&lt;br />
&lt;code>https://ptx.transportdata.tw/MOTC/v2/Rail/TRA/LiveBoard/Station/1000?%24filter=Direction%20eq%201&amp;amp;%24format=JSON&lt;/code>&lt;/p>
&lt;p>這是以下這句經過 URL 編碼後的結果：&lt;br />
(程式中 requests 會自動幫我們編碼，因此輸入底下這句也可以)&lt;br />
&lt;code>https://ptx.transportdata.tw/MOTC/v2/Rail/TRA/LiveBoard/Station/1000?$filter=Direction eq 1&amp;amp;$format=JSON&lt;/code>&lt;/p>
&lt;br/>
&lt;p>接下來將這些代入程式中：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre class="chroma">&lt;code class="language-JavaScript" data-lang="JavaScript">&lt;span class="kd">function&lt;/span> &lt;span class="nx">doGet&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;span class="kd">var&lt;/span> &lt;span class="nx">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">UrlFetchApp&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">fetch&lt;/span>&lt;span class="p">(&lt;/span>
&lt;span class="s2">&amp;#34;https://ptx.transportdata.tw/MOTC/v2/Rail/TRA/LiveBoard/Station/1000?$filter=Direction eq 1&amp;amp;$format=JSON&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;span class="p">{&lt;/span>&lt;span class="nx">method&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;GET&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;headers&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">GetAuthorizationHeader&lt;/span>&lt;span class="p">()}&lt;/span>
&lt;span class="p">);&lt;/span>
&lt;span class="c1">// console.log(response.getResponseCode());
&lt;/span>&lt;span class="c1">&lt;/span> &lt;span class="nx">outData&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">JSON&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">parse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getContentText&lt;/span>&lt;span class="p">());&lt;/span>
&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">outData&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="k">return&lt;/span> &lt;span class="nx">ContentService&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">createTextOutput&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">JSON&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">stringify&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">outData&lt;/span>&lt;span class="p">)).&lt;/span>&lt;span class="nx">setMimeType&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">ContentService&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">MimeType&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">JSON&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>點擊上方的 &amp;quot;執行&amp;quot;，程式執行後會自動開啟 &amp;quot;執行記錄&amp;quot;，程式碼中 &lt;code>console.log();&lt;/code> 的部分會在此顯示出來。&lt;/p>
&lt;p>如此即可順利取得資料 🎉🎉🎉&lt;/p>
&lt;figure >
&lt;img data-src="https://res.cloudinary.com/jiablog/ptx_intro/gas_run.jpg" alt="執行 GAS 程式" data-caption="&amp;#34;執行&amp;#34; GAS 程式" src="data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='650px' height='' viewBox='0 0 24 24'%3E%3Cpath fill='none' d='M0 0h24v24H0V0z'/%3E%3Cpath fill='%23aaa' d='M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-1 16H6c-.55 0-1-.45-1-1V6c0-.55.45-1 1-1h12c.55 0 1 .45 1 1v12c0 .55-.45 1-1 1zm-4.44-6.19l-2.35 3.02-1.56-1.88c-.2-.25-.58-.24-.78.01l-1.74 2.23c-.26.33-.02.81.39.81h8.98c.41 0 .65-.47.4-.8l-2.55-3.39c-.19-.26-.59-.26-.79 0z'/%3E%3C/svg%3E" class="lazyload" style="width:650px;height:;"/>
&lt;figcaption style="text-align: center;">
&amp;#34;執行&amp;#34; GAS 程式
&lt;/figcaption>
&lt;/figure>
&lt;br/>
&lt;h2 id="完整-google-apps-script-程式範例">完整 Google Apps Script 程式範例&lt;/h2>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre class="chroma">&lt;code class="language-JavaScript" data-lang="JavaScript">&lt;span class="kd">function&lt;/span> &lt;span class="nx">doGet&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;span class="kd">var&lt;/span> &lt;span class="nx">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">UrlFetchApp&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">fetch&lt;/span>&lt;span class="p">(&lt;/span>
&lt;span class="s2">&amp;#34;https://ptx.transportdata.tw/MOTC/v2/Rail/TRA/LiveBoard/Station/1000?$filter=Direction eq 1&amp;amp;$format=JSON&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;span class="p">{&lt;/span>&lt;span class="nx">method&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;GET&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;headers&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">GetAuthorizationHeader&lt;/span>&lt;span class="p">()}&lt;/span>
&lt;span class="p">);&lt;/span>
&lt;span class="c1">// console.log(response.getResponseCode());
&lt;/span>&lt;span class="c1">&lt;/span> &lt;span class="nx">outData&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">JSON&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">parse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getContentText&lt;/span>&lt;span class="p">());&lt;/span>
&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">outData&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="k">return&lt;/span> &lt;span class="nx">ContentService&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">createTextOutput&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">JSON&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">stringify&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">outData&lt;/span>&lt;span class="p">)).&lt;/span>&lt;span class="nx">setMimeType&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">ContentService&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">MimeType&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">JSON&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="kd">function&lt;/span> &lt;span class="nx">GetAuthorizationHeader&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;span class="kd">var&lt;/span> &lt;span class="nx">AppID&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;&amp;lt;YOUR APP ID&amp;gt;&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="kd">var&lt;/span> &lt;span class="nx">AppKey&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;&amp;lt;YOUR APP KEY&amp;gt;&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="kd">var&lt;/span> &lt;span class="nx">xdate&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nb">Date&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="nx">toGMTString&lt;/span>&lt;span class="p">();&lt;/span>
&lt;span class="c1">//HMAC-SHA1 運算
&lt;/span>&lt;span class="c1">&lt;/span> &lt;span class="kd">var&lt;/span> &lt;span class="nx">signature&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">Utilities&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">computeHmacSignature&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">Utilities&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">MacAlgorithm&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">HMAC_SHA_1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;x-date: &amp;#34;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">xdate&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">AppKey&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="c1">//轉成Base64
&lt;/span>&lt;span class="c1">&lt;/span> &lt;span class="kd">var&lt;/span> &lt;span class="nx">HMAC&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">Utilities&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">base64Encode&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">signature&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="kd">var&lt;/span> &lt;span class="nx">Authorization&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;hmac username=\&amp;#34;&amp;#39;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">AppID&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="s1">&amp;#39;\&amp;#34;, algorithm=\&amp;#34;hmac-sha1\&amp;#34;, headers=\&amp;#34;x-date\&amp;#34;, signature=\&amp;#34;&amp;#39;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">HMAC&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="s1">&amp;#39;\&amp;#34;&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="k">return&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="s1">&amp;#39;Authorization&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">Authorization&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;x-date&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">xdate&lt;/span> &lt;span class="p">,&lt;/span>&lt;span class="s1">&amp;#39;Accept-Encoding&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;gzip&amp;#39;&lt;/span>&lt;span class="p">};&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;br/>
&lt;h2 id="部署">部署&lt;/h2>
&lt;p>Google Apps Script 特別的地方就在於可直接把程式部署起來，不需要另外找伺服器、線上服務來處理，非常方便。&lt;/p>
&lt;br/>
&lt;p>像是上面範例的 &lt;code>function doGet()&lt;/code>，就是 Google Apps Script 內定的函式，當我們發佈成 &amp;quot;網頁應用程式&amp;quot; 後，對網址發送 GET 請求(就是在瀏覽器上前往此網址)，就會執行此函式。&lt;br />
另外也有 &lt;code>function doPost()&lt;/code> 對應 POST 請求。&lt;/p>
&lt;p>如果想在網址後方帶入參數，像是這樣：&lt;br />
&lt;code>https://webdomain.com?stationId=1000&lt;/code>&lt;/p>
&lt;p>就使用這種寫法，即可取到對應參數後方的字串：&lt;br />
&lt;code>var stationId = e.parameter.stationId;&lt;/code>&lt;/p>
&lt;figure >
&lt;img data-src="https://res.cloudinary.com/jiablog/ptx_intro/gas_parameter.png" alt="帶入參數" data-caption="帶入參數" src="data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='400px' height='' viewBox='0 0 24 24'%3E%3Cpath fill='none' d='M0 0h24v24H0V0z'/%3E%3Cpath fill='%23aaa' d='M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-1 16H6c-.55 0-1-.45-1-1V6c0-.55.45-1 1-1h12c.55 0 1 .45 1 1v12c0 .55-.45 1-1 1zm-4.44-6.19l-2.35 3.02-1.56-1.88c-.2-.25-.58-.24-.78.01l-1.74 2.23c-.26.33-.02.81.39.81h8.98c.41 0 .65-.47.4-.8l-2.55-3.39c-.19-.26-.59-.26-.79 0z'/%3E%3C/svg%3E" class="lazyload" style="width:400px;height:;"/>
&lt;figcaption style="text-align: center;">
帶入參數
&lt;/figcaption>
&lt;/figure>
&lt;br/>
&lt;br/>
&lt;p>那我們開始來 &amp;quot;部署&amp;quot; 吧~&lt;/p>
&lt;p>確認檔案存檔後(重要！！)，點擊右上角的 &amp;quot;部署&amp;quot; &amp;gt; &amp;quot;新增部署作業&amp;quot; 按鈕。&lt;/p>
&lt;figure >
&lt;img data-src="https://res.cloudinary.com/jiablog/ptx_intro/new_deploy.png" alt="選擇新增部署作業" data-caption="選擇 &amp;#34;部署&amp;#34; &amp;gt; &amp;#34;新增部署作業&amp;#34;" src="data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='350px' height='' viewBox='0 0 24 24'%3E%3Cpath fill='none' d='M0 0h24v24H0V0z'/%3E%3Cpath fill='%23aaa' d='M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-1 16H6c-.55 0-1-.45-1-1V6c0-.55.45-1 1-1h12c.55 0 1 .45 1 1v12c0 .55-.45 1-1 1zm-4.44-6.19l-2.35 3.02-1.56-1.88c-.2-.25-.58-.24-.78.01l-1.74 2.23c-.26.33-.02.81.39.81h8.98c.41 0 .65-.47.4-.8l-2.55-3.39c-.19-.26-.59-.26-.79 0z'/%3E%3C/svg%3E" class="lazyload" style="width:350px;height:;"/>
&lt;figcaption style="text-align: center;">
選擇 &amp;#34;部署&amp;#34; &amp;gt; &amp;#34;新增部署作業&amp;#34;
&lt;/figcaption>
&lt;/figure>
&lt;p>左邊齒輪形狀的設定打開，選擇 &amp;quot;網頁應用程式&amp;quot;。&lt;/p>
&lt;figure >
&lt;img data-src="https://res.cloudinary.com/jiablog/ptx_intro/select_web_app.png" alt="選擇網頁應用程式" data-caption="選擇 &amp;#34;網頁應用程式&amp;#34;" src="data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='550px' height='' viewBox='0 0 24 24'%3E%3Cpath fill='none' d='M0 0h24v24H0V0z'/%3E%3Cpath fill='%23aaa' d='M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-1 16H6c-.55 0-1-.45-1-1V6c0-.55.45-1 1-1h12c.55 0 1 .45 1 1v12c0 .55-.45 1-1 1zm-4.44-6.19l-2.35 3.02-1.56-1.88c-.2-.25-.58-.24-.78.01l-1.74 2.23c-.26.33-.02.81.39.81h8.98c.41 0 .65-.47.4-.8l-2.55-3.39c-.19-.26-.59-.26-.79 0z'/%3E%3C/svg%3E" class="lazyload" style="width:550px;height:;"/>
&lt;figcaption style="text-align: center;">
選擇 &amp;#34;網頁應用程式&amp;#34;
&lt;/figcaption>
&lt;/figure>
&lt;p>&amp;quot;執行身分&amp;quot; 和 &amp;quot;誰可以存取&amp;quot; 可以自己看需求更改，執行 &amp;quot;部署&amp;quot;。&lt;/p>
&lt;figure >
&lt;img data-src="https://res.cloudinary.com/jiablog/ptx_intro/deploy.png" alt="執行部署" data-caption="執行部署" src="data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='550px' height='' viewBox='0 0 24 24'%3E%3Cpath fill='none' d='M0 0h24v24H0V0z'/%3E%3Cpath fill='%23aaa' d='M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-1 16H6c-.55 0-1-.45-1-1V6c0-.55.45-1 1-1h12c.55 0 1 .45 1 1v12c0 .55-.45 1-1 1zm-4.44-6.19l-2.35 3.02-1.56-1.88c-.2-.25-.58-.24-.78.01l-1.74 2.23c-.26.33-.02.81.39.81h8.98c.41 0 .65-.47.4-.8l-2.55-3.39c-.19-.26-.59-.26-.79 0z'/%3E%3C/svg%3E" class="lazyload" style="width:550px;height:;"/>
&lt;figcaption style="text-align: center;">
執行部署
&lt;/figcaption>
&lt;/figure>
&lt;p>用這邊產生的網址，到瀏覽器貼上，就可以取得剛剛的資料了~&lt;/p>
&lt;figure >
&lt;img data-src="https://res.cloudinary.com/jiablog/ptx_intro/web_app_url.png" alt="網頁應用程式 網址" data-caption="網頁應用程式 網址" src="data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='550px' height='' viewBox='0 0 24 24'%3E%3Cpath fill='none' d='M0 0h24v24H0V0z'/%3E%3Cpath fill='%23aaa' d='M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-1 16H6c-.55 0-1-.45-1-1V6c0-.55.45-1 1-1h12c.55 0 1 .45 1 1v12c0 .55-.45 1-1 1zm-4.44-6.19l-2.35 3.02-1.56-1.88c-.2-.25-.58-.24-.78.01l-1.74 2.23c-.26.33-.02.81.39.81h8.98c.41 0 .65-.47.4-.8l-2.55-3.39c-.19-.26-.59-.26-.79 0z'/%3E%3C/svg%3E" class="lazyload" style="width:550px;height:;"/>
&lt;figcaption style="text-align: center;">
網頁應用程式 網址
&lt;/figcaption>
&lt;/figure>
&lt;p>* 如果有編輯，需要存檔後再 &amp;quot;部署&amp;quot; 一次，而且網址會更改。但舊版本還會存在，需要到 &amp;quot;管理部署作業&amp;quot; 將其封存。&lt;/p>
&lt;br/>
&lt;h2 id="部署成網頁">部署成網頁&lt;/h2>
&lt;p>除了像上述的方法當成一支 API，也可以回傳 HTML，顯示成網頁喔~&lt;/p>
&lt;p>左邊檔案新增一個 HTML。&lt;/p>
&lt;figure >
&lt;img data-src="https://res.cloudinary.com/jiablog/ptx_intro/create_html.jpg" alt="新增 HTML 檔案" data-caption="新增 HTML 檔案" src="data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='400px' height='' viewBox='0 0 24 24'%3E%3Cpath fill='none' d='M0 0h24v24H0V0z'/%3E%3Cpath fill='%23aaa' d='M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-1 16H6c-.55 0-1-.45-1-1V6c0-.55.45-1 1-1h12c.55 0 1 .45 1 1v12c0 .55-.45 1-1 1zm-4.44-6.19l-2.35 3.02-1.56-1.88c-.2-.25-.58-.24-.78.01l-1.74 2.23c-.26.33-.02.81.39.81h8.98c.41 0 .65-.47.4-.8l-2.55-3.39c-.19-.26-.59-.26-.79 0z'/%3E%3C/svg%3E" class="lazyload" style="width:400px;height:;"/>
&lt;figcaption style="text-align: center;">
新增 HTML 檔案
&lt;/figcaption>
&lt;/figure>
&lt;p>HTML 檔案內容撰寫完成後，在原先的程式碼 doGet() 中，回傳這個 HTML 檔案即可。&lt;br />
(&lt;code>index&lt;/code> 是 HTML 的檔名)&lt;/p>
&lt;figure >
&lt;img data-src="https://res.cloudinary.com/jiablog/ptx_intro/return_html.png" alt="回傳 HTML" data-caption="回傳 HTML" src="data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='650px' height='' viewBox='0 0 24 24'%3E%3Cpath fill='none' d='M0 0h24v24H0V0z'/%3E%3Cpath fill='%23aaa' d='M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-1 16H6c-.55 0-1-.45-1-1V6c0-.55.45-1 1-1h12c.55 0 1 .45 1 1v12c0 .55-.45 1-1 1zm-4.44-6.19l-2.35 3.02-1.56-1.88c-.2-.25-.58-.24-.78.01l-1.74 2.23c-.26.33-.02.81.39.81h8.98c.41 0 .65-.47.4-.8l-2.55-3.39c-.19-.26-.59-.26-.79 0z'/%3E%3C/svg%3E" class="lazyload" style="width:650px;height:;"/>
&lt;figcaption style="text-align: center;">
回傳 HTML
&lt;/figcaption>
&lt;/figure>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre class="chroma">&lt;code class="language-JavaScript" data-lang="JavaScript">&lt;span class="kd">function&lt;/span> &lt;span class="nx">doGet&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;span class="k">return&lt;/span> &lt;span class="nx">HtmlService&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">createHtmlOutputFromFile&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;index&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>因此你可以在 Google Apps Script 裡，取得 PTX 平台大眾運輸資料後，顯示到自製的網頁上，嘗試做一個🚅查詢時刻表的網頁服務吧~&lt;/p>
&lt;br/>
&lt;h2 id="其他程式範例">其他程式範例&lt;/h2>
&lt;p>除了參考我上面的 Google Apps Script 範例，PTX 官方也提供不同程式語言的範例程式碼提供開發者下載 (&lt;a href="https://github.com/ptxmotc/Sample-code" target="_blank" rel="noopener">
GitHub
&lt;/a>)。&lt;/p>
&lt;ul>
&lt;li>平台提供
&lt;ul>
&lt;li>ASP.NET&lt;/li>
&lt;li>Java&lt;/li>
&lt;li>JavaScript&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>網友提供
&lt;ul>
&lt;li>Bourne Shell&lt;/li>
&lt;li>C# 與 .NET Standard 2.0&lt;/li>
&lt;li>Go&lt;/li>
&lt;li>Go Client SDK - Code generated by go-swagger&lt;/li>
&lt;li>Node.js&lt;/li>
&lt;li>python&lt;/li>
&lt;li>Ruby&lt;/li>
&lt;li>Swift&lt;/li>
&lt;li>PHP&lt;/li>
&lt;li>Postman&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;br/>
&lt;!--adsense-->
&lt;h2 id="結語">結語&lt;/h2>
&lt;p>有關更多 Google Apps Script 語法說明，歡迎參考 &lt;a href="https://developers.google.com/apps-script/guides/web" target="_blank" rel="noopener">
Google Apps Script 官方文件
&lt;/a>。&lt;/p>
&lt;br/>
&lt;p>歡迎追蹤『&lt;a href="https://www.facebook.com/jiatool" target="_blank" rel="noopener">
IT空間
&lt;/a>』FB 粉專，取得最新發文通知🔔&lt;/p>
&lt;br/>
&lt;br/>
&lt;hr />
&lt;p>參考：&lt;br />
&lt;a href="https://ptx.transportdata.tw/PTX/" target="_blank" rel="noopener">
公共運輸整合資訊流通服務平台 | 官網
&lt;/a>&lt;br />
&lt;a href="https://gist.github.com/ptxmotc/383118204ecf7192bdf96bc0197bb981" target="_blank" rel="noopener">
PTX 資料服務使用注意事項 | GitHub
&lt;/a>&lt;br />
&lt;a href="https://github.com/ptxmotc/Sample-code" target="_blank" rel="noopener">
PTX 官方範例程式碼 | GitHub
&lt;/a>&lt;br />
&lt;a href="https://developers.google.com/apps-script/guides/web" target="_blank" rel="noopener">
Google Apps Script 官方文件
&lt;/a>&lt;/p>
&lt;br/>
&lt;blockquote>
&lt;p>我不是神童，只是堅持打好每一顆球。&lt;/p>
&lt;p align="right">—— 林昀儒 (台灣桌球國手)&lt;/p>
&lt;/blockquote></content:encoded><dc:creator>Jia</dc:creator><media:content url="https://blog.jiatool.comimages/cover/ptx_google_apps_script.jpg" medium="image"><media:title type="html">featured image</media:title></media:content><media:content url="https://blog.jiatool.comimages/posts/ptx_google_apps_script_meta.jpg" medium="image"><media:title type="html">meta image</media:title></media:content><category>PTX</category><category>API</category><category>交通</category><category>公共運輸</category><category>GoogleAppsScript</category><category>分享</category></item></channel></rss>