<?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>公共運輸 on IT 空間</title><link>https://blog.jiatool.com/tags/%E5%85%AC%E5%85%B1%E9%81%8B%E8%BC%B8/</link><description>Recent content in 公共運輸 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/%E5%85%AC%E5%85%B1%E9%81%8B%E8%BC%B8/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>「TDX 運輸資料流通服務平台」含 Python 範例程式，PTX 平台的升級版~</title><link>https://blog.jiatool.com/posts/tdx_python/</link><pubDate>Sun, 19 Jun 2022 21:25:00 +0800</pubDate><author>jia@jiatool.com (Jia)</author><atom:modified>Sat, 31 Dec 2022 21:00:00 +0800</atom:modified><guid>https://blog.jiatool.com/posts/tdx_python/</guid><description>前言 在之前文章介紹 PTX 公共運輸整合資訊流通服務平台 ，它整合多項公共運輸的資料服務 API，供我們串接取得相關數據。 「PTX 公共運輸資訊平台」AP</description><content:encoded>&lt;h2 id="前言">前言&lt;/h2>
&lt;p>在之前文章介紹 &lt;a href="https://ptx.transportdata.tw/PTX/" target="_blank" rel="noopener">
PTX 公共運輸整合資訊流通服務平台
&lt;/a>，它整合多項公共運輸的資料服務 API，供我們串接取得相關數據。&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>&lt;a href="https://blog.jiatool.com/posts/ptx_google_apps_script" target="_blank" rel="noopener">
「PTX 公共運輸資訊平台」Google Apps Script 範例
&lt;/a>&lt;/li>
&lt;/ol>
&lt;br/>
&lt;p>但經網友提醒，才發現 PTX 竟然只服務到今年(2022年)底，之後會改以「TDX 運輸資訊整合流通服務平台」代替，因此本篇文章將帶大家快速了解一下 TDX 平台，以及實際使用 Python 來串接，取得相關數據資料。&lt;/p>
&lt;figure >
&lt;img data-src="https://res.cloudinary.com/jiablog/tdx_python/ptx_close.jpg" alt="PTX 平台關閉" data-caption="PTX 平台關閉" src="data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='600px' 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:600px;height:;"/>
&lt;figcaption style="text-align: center;">
PTX 平台關閉
&lt;/figcaption>
&lt;/figure>
&lt;br/>
&lt;p>後來我有寫一篇 &lt;a href="%28/posts/tdx_google_apps_script%29" target="_blank" rel="noopener">
Google Apps Script 範例程式
&lt;/a> 可以參考哦~&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;h2 id="tdx-平台說明">TDX 平台說明&lt;/h2>
&lt;p>按照官網的說法，TDX 整合交通部運輸數據五大平台(PTX、Traffic、GIS-T、TICP、LINK)，並使用全新的認證授權方式管控資源存取權限，提供單一服務查詢與介接入口。短時間內五大平台與 TDX 平台將持續並行運作，但未來會以 TDX 為主要的數據流通平台。&lt;br />
(參考：&lt;a href="https://tdx.transportdata.tw/about/faq" target="_blank" rel="noopener">
TDX 平台常見問題
&lt;/a>)&lt;/p>
&lt;figure >
&lt;img data-src="https://tdx.transportdata.tw/backend/images/Uploads/4c57b4tdxapi2.PNG" alt="TDX 整合五大平台 (圖片來源：TDX官網)" data-caption="TDX 整合五大平台 (圖片來源：TDX官網)" src="data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='750px' 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:750px;height:;"/>
&lt;figcaption style="text-align: center;">
TDX 整合五大平台 (圖片來源：TDX官網)
&lt;/figcaption>
&lt;/figure>
&lt;p>實際使用其實「TDX 運輸資訊整合流通服務平台」服務及概念是跟「PTX 公共運輸整合資訊流通服務平台」差不多的，只是相較於 PTX 平台，TDX 平台又多了許多種類的資料，像是 &amp;quot;即時路況&amp;quot;、&amp;quot;停車資訊&amp;quot;、&amp;quot;GIS圖資&amp;quot;、&amp;quot;道路編碼&amp;quot; 等等也把它整合進來。&lt;/p>
&lt;figure >
&lt;img data-src="https://res.cloudinary.com/jiablog/tdx_python/tdx_service_scope.png" alt="TDX 平台服務範疇" data-caption="TDX 平台服務範疇" 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;">
TDX 平台服務範疇
&lt;/figcaption>
&lt;/figure>
&lt;br/>
&lt;p>TDX 平台也有個&lt;a href="https://tdx.transportdata.tw/data-service/basic" target="_blank" rel="noopener">
詳細各項服務查詢頁面
&lt;/a>，可依照 &amp;quot;服務類型&amp;quot;、&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/tdx_python/tdx_service.jpg" alt="TDX 各項服務查詢" data-caption="TDX 各項服務查詢" 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;">
TDX 各項服務查詢
&lt;/figcaption>
&lt;/figure>
&lt;br/>
&lt;h2 id="註冊會員--取得api金鑰">註冊會員 &amp;amp; 取得API金鑰&lt;/h2>
&lt;p>同樣為了系統資源使用之公平性與資通訊安全考量，要先註冊會員來取得 API 金鑰(&lt;a href="https://tdx.transportdata.tw/register" target="_blank" rel="noopener">
TDX 註冊頁面
&lt;/a>)，但不知道是不是因為平台還在試營運階段的關係，審核完成並沒有寄 Email 通知，是我主動登入平台才發覺已審核通過&amp;hellip;&amp;hellip;&lt;/p>
&lt;figure >
&lt;img data-src="https://res.cloudinary.com/jiablog/tdx_python/signup_account.png" alt="註冊 TDX 平台會員" data-caption="註冊 TDX 平台會員" 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;">
註冊 TDX 平台會員
&lt;/figcaption>
&lt;/figure>
&lt;p>登入 TDX 平台後，前往 會員中心 &amp;gt; (左邊) &amp;gt; 資料服務 &amp;gt; API金鑰 頁面，對 預設的API Key (或建立新的金鑰) 點擊 &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;/p>
&lt;ul>
&lt;li>使用 API 金鑰呼叫，每個呼叫來源端 IP 呼叫次數限制為 50 次/秒 (無每日上限)。&lt;/li>
&lt;li>不使用 API 金鑰呼叫，則僅能透過瀏覽器呼叫 API，且每個呼叫來源端 IP 的上限為每日 50 次。&lt;/li>
&lt;/ul>
&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="swagger-工具">Swagger 工具&lt;/h2>
&lt;p>從上方導航欄 開發指引 &amp;gt; API說明，前往 TDX 平台的 &lt;a href="https://tdx.transportdata.tw/api-service/swagger" target="_blank" rel="noopener">
Swagger 工具
&lt;/a>，能查看有提供哪些 API，與各自的請求參數與回傳欄位說明。&lt;br />
詳細使用說明可以查看之前我寫的 &lt;a href="https://blog.jiatool.com/posts/ptx_intro" target="_blank" rel="noopener">
PTX 介紹 (含 Odata 說明)
&lt;/a> 文章。&lt;/p>
&lt;figure >
&lt;img data-src="https://res.cloudinary.com/jiablog/tdx_python/tdx_api_swagger.jpg" alt="TDX 平台 Swagger 工具" data-caption="TDX 平台 Swagger 工具" 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;">
TDX 平台 Swagger 工具
&lt;/figcaption>
&lt;/figure>
&lt;p>右方有 Authorize 按鈕，填入 &amp;quot;Client Id&amp;quot; 與 &amp;quot;Client Secret&amp;quot; 驗證，即可使用你的 API 金鑰來呼叫。不使用 API 金鑰呼叫，有每日 50 次的上限。&lt;/p>
&lt;br/>
&lt;h2 id="api-認證授權機制">API 認證授權機制&lt;/h2>
&lt;p>API 認證授權機制與 PTX 平台有蠻大的差異，簡單來說每次請求前要先身份認證取得 Access Token，才能使用 Access Token 來取得 TDX API 服務的數據資料。&lt;br />
(或使用尚未過期的 Access Token，有效期限預設為 1 天)&lt;/p>
&lt;br/>
&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;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>* 官方的說明文件： &lt;a href="https://github.com/tdxmotc/SampleCode">https://github.com/tdxmotc/SampleCode&lt;/a>&lt;/p>
&lt;br/>
&lt;h2 id="完整-python-程式範例">完整 Python 程式範例&lt;/h2>
&lt;p>因為 TDX 平台的 API 一樣使用 OData (Open Data Protocol) 標準介面，所以這邊我就不再寫一次了，還不太懂的可前往 &lt;a href="https://blog.jiatool.com/posts/ptx_intro" target="_blank" rel="noopener">
「PTX 平台」API 介紹 (含 Odata 說明)
&lt;/a> 了解。&lt;/p>
&lt;p>同樣以下方條件當範例查詢：&lt;/p>
&lt;p>台鐵&amp;quot;台北&amp;quot;車站即時的列車到離站看板資訊，並且只要&amp;quot;逆行&amp;quot;的列車&lt;/p>
&lt;br/>
&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;p>會發現與 PTX 平台根本一樣，只差在網址前方的部分。&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;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre class="chroma">&lt;code class="language-Python" data-lang="Python">&lt;span class="kn">import&lt;/span> &lt;span class="nn">requests&lt;/span>
&lt;span class="n">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="n">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="k">class&lt;/span> &lt;span class="nc">TDX&lt;/span>&lt;span class="p">():&lt;/span>
&lt;span class="k">def&lt;/span> &lt;span class="fm">__init__&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">client_id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">client_secret&lt;/span>&lt;span class="p">):&lt;/span>
&lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">client_id&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">client_id&lt;/span>
&lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">client_secret&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">client_secret&lt;/span>
&lt;span class="k">def&lt;/span> &lt;span class="nf">get_token&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">):&lt;/span>
&lt;span class="n">token_url&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;https://tdx.transportdata.tw/auth/realms/TDXConnect/protocol/openid-connect/token&amp;#39;&lt;/span>
&lt;span class="n">headers&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s1">&amp;#39;content-type&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;application/x-www-form-urlencoded&amp;#39;&lt;/span>&lt;span class="p">}&lt;/span>
&lt;span class="n">data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;span class="s1">&amp;#39;grant_type&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;client_credentials&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;span class="s1">&amp;#39;client_id&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">client_id&lt;/span>&lt;span class="p">,&lt;/span>
&lt;span class="s1">&amp;#39;client_secret&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">client_secret&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="n">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">requests&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">post&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">token_url&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">headers&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">headers&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">data&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="c1"># print(response.status_code)&lt;/span>
&lt;span class="c1"># print(response.json())&lt;/span>
&lt;span class="k">return&lt;/span> &lt;span class="n">response&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">json&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="k">def&lt;/span> &lt;span class="nf">get_response&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">url&lt;/span>&lt;span class="p">):&lt;/span>
&lt;span class="n">headers&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s1">&amp;#39;authorization&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">f&lt;/span>&lt;span class="s1">&amp;#39;Bearer {self.get_token()}&amp;#39;&lt;/span>&lt;span class="p">}&lt;/span>
&lt;span class="n">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">requests&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">url&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">headers&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">headers&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="k">return&lt;/span> &lt;span class="n">response&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">json&lt;/span>&lt;span class="p">()&lt;/span>
&lt;span class="k">if&lt;/span> &lt;span class="vm">__name__&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s1">&amp;#39;__main__&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;span class="n">tdx&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">TDX&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">client_id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">client_secret&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="c1"># url = &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="n">base_url&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;https://tdx.transportdata.tw/api&amp;#34;&lt;/span>
&lt;span class="c1"># 取得指定[車站]列車即時到離站電子看板(動態前後30分鐘的車次)&lt;/span>
&lt;span class="n">endpoint&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;/basic/v2/Rail/TRA/LiveBoard/Station/1000&amp;#34;&lt;/span>
&lt;span class="nb">filter&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;Direction eq 1&amp;#34;&lt;/span> &lt;span class="c1"># 順逆行: [0:&amp;#39;順行&amp;#39;, 1:&amp;#39;逆行&amp;#39;]&lt;/span>
&lt;span class="n">url&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">f&lt;/span>&lt;span class="s2">&amp;#34;{base_url}{endpoint}?$filter={filter}&amp;amp;$format=JSON&amp;#34;&lt;/span>
&lt;span class="n">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">tdx&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get_response&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">url&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="k">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">response&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>如此應該就能順利取得資料 🎉🎉🎉&lt;/p>
&lt;br/>
&lt;h2 id="其他程式範例">其他程式範例&lt;/h2>
&lt;p>除了參考我上面的 Python 範例，TDX 官方有提供 JavaScript 與 C# 程式語言的範例程式碼，需要的可前往參考 (&lt;a href="https://github.com/tdxmotc/SampleCode" target="_blank" rel="noopener">
GitHub
&lt;/a>)。&lt;/p>
&lt;br/>
&lt;p>我有寫一篇 &lt;a href="%28/posts/tdx_google_apps_script%29" target="_blank" rel="noopener">
Google Apps Script 範例程式
&lt;/a> 可以參考。&lt;/p>
&lt;p>也感謝熱心網友 meebox，提供 &amp;quot;適用在嵌入式系統的 MicroPython 環境、token 有效期判斷&amp;quot; 的版本：&lt;br />
&lt;a href="https://github.com/codemee/tdx">https://github.com/codemee/tdx&lt;/a>&lt;/p>
&lt;br/>
&lt;!--adsense-->
&lt;h2 id="結語">結語&lt;/h2>
&lt;p>這次快速(?)帶大家了解「TDX 運輸資料流通服務平台」，看起來就是 PTX 平台的升級版，加入更多不同服務的資料。&lt;br />
如果你之前已經用 PTX 做些作品出來，不妨試著將其改串接至 TDX，畢竟可能明年 PTX 平台就無法使用了。&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;br />
因此，你必須相信，這些小事一定會和你的未來產生關聯。&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/tdx_python.jpg" medium="image"><media:title type="html">featured image</media:title></media:content><media:content url="https://blog.jiatool.comimages/posts/tdx_python_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>Python</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><item><title>「PTX 公共運輸資訊平台」Python 範例程式</title><link>https://blog.jiatool.com/posts/ptx_python/</link><pubDate>Sat, 30 Apr 2022 20:55: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_python/</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>，它整合公車、臺鐵、高鐵、捷運等多項公共運輸的資料服務 API，以及說明 OData（Open Data Protocol）標準介面格式如何使用。&lt;/p>
&lt;p>本篇文章將帶你實際使用 Python 來串接 PTX 平台，取得這些相關數據資料 💾。&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>「PTX 公共運輸資訊平台」Python 範例 &amp;lt;&amp;ndash; 本篇&lt;/li>
&lt;li>&lt;a href="https://blog.jiatool.com/posts/ptx_google_apps_script" target="_blank" rel="noopener">
「PTX 公共運輸資訊平台」Google Apps Script 範例
&lt;/a>&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="api-認證授權機制">API 認證授權機制&lt;/h2>
&lt;p>目前 PTX 平台採用 HMAC 認證授權機制，在送出請求時 headers 需代上規定的資料，它會依照 HTTP header 資訊來判別用戶是否有授權身份 🔓。&lt;/p>
&lt;p>官方說明如下：&lt;/p>
&lt;blockquote>
&lt;p>HMAC 機制：以 HMAC 簽章驗證使用者的身份，用戶在請求 API 服務時，將 APP Key 與當下時間(格式請使用GMT時間) 做 HMAC-SHA1 運算後轉成 Base64 格式，帶入 signature 屬性欄位，服務器端將驗證用戶請求時的 header 欄位，驗證使用者的身份及請求服務的時效性。&lt;/p>
&lt;p>HMAC Signature 簽章時效性：於 MOTC Helper 該網頁測試時，請在最上方輸入 API Key 與 API ID (請再次確認是否有把 APP Key 跟 ID 填寫正確，若欄位資訊相反會無法執行)。點選 Explore ，每次請求下方 API 時，會於 header 帶入 Authorization 及 x-date ，依照請求當下的時間 &amp;amp; API Key 製作簽章。&lt;/p>
&lt;/blockquote>
&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>依照上方說明，使用 Python 把這個 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;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;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre class="chroma">&lt;code class="language-Python" data-lang="Python">&lt;span class="k">class&lt;/span> &lt;span class="nc">Auth&lt;/span>&lt;span class="p">():&lt;/span>
&lt;span class="k">def&lt;/span> &lt;span class="fm">__init__&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">app_id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">app_key&lt;/span>&lt;span class="p">):&lt;/span>
&lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">app_id&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">app_id&lt;/span>
&lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">app_key&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">app_key&lt;/span>
&lt;span class="k">def&lt;/span> &lt;span class="nf">get_auth_header&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">):&lt;/span>
&lt;span class="n">xdate&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">datetime&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">utcnow&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">strftime&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;%a, &lt;/span>&lt;span class="si">%d&lt;/span>&lt;span class="s2"> %b %Y %H:%M:%S GMT&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="n">hashed&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">hmac&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">new&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">app_key&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">encode&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;utf8&amp;#39;&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">f&lt;/span>&lt;span class="s1">&amp;#39;x-date: {xdate}&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">encode&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;utf8&amp;#39;&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">sha1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="n">signature&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">base64&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">b64encode&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">hashed&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">digest&lt;/span>&lt;span class="p">())&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">decode&lt;/span>&lt;span class="p">()&lt;/span>
&lt;span class="n">authorization&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">f&lt;/span>&lt;span class="s1">&amp;#39;hmac username=&amp;#34;{self.app_id}&amp;#34;, algorithm=&amp;#34;hmac-sha1&amp;#34;&amp;#39;&lt;/span> &lt;span class="o">+&lt;/span>
&lt;span class="n">f&lt;/span>&lt;span class="s1">&amp;#39;, headers=&amp;#34;x-date&amp;#34;, signature=&amp;#34;{signature}&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="p">:&lt;/span> &lt;span class="n">authorization&lt;/span>&lt;span class="p">,&lt;/span>
&lt;span class="s1">&amp;#39;x-date&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">xdate&lt;/span>&lt;span class="p">,&lt;/span>
&lt;span class="s1">&amp;#39;Accept-Encoding&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;gzip&amp;#39;&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="python-程式範例">Python 程式範例&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;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre class="chroma">&lt;code class="language-Python" data-lang="Python">&lt;span class="n">auth&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Auth&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">app_id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">app_key&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="n">url&lt;/span> &lt;span class="o">=&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="n">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">requests&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">url&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">headers&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">auth&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get_auth_header&lt;/span>&lt;span class="p">())&lt;/span>
&lt;span class="k">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">response&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">json&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>如此應該就能順利取得資料 🎉🎉🎉&lt;/p>
&lt;br/>
&lt;h2 id="完整-python-程式範例">完整 Python 程式範例&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;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;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre class="chroma">&lt;code class="language-Python" data-lang="Python">&lt;span class="kn">import&lt;/span> &lt;span class="nn">hmac&lt;/span>
&lt;span class="kn">import&lt;/span> &lt;span class="nn">base64&lt;/span>
&lt;span class="kn">import&lt;/span> &lt;span class="nn">requests&lt;/span>
&lt;span class="kn">from&lt;/span> &lt;span class="nn">datetime&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">datetime&lt;/span>
&lt;span class="kn">from&lt;/span> &lt;span class="nn">hashlib&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">sha1&lt;/span>
&lt;span class="n">app_id&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="n">app_key&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="k">class&lt;/span> &lt;span class="nc">Auth&lt;/span>&lt;span class="p">():&lt;/span>
&lt;span class="k">def&lt;/span> &lt;span class="fm">__init__&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">app_id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">app_key&lt;/span>&lt;span class="p">):&lt;/span>
&lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">app_id&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">app_id&lt;/span>
&lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">app_key&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">app_key&lt;/span>
&lt;span class="k">def&lt;/span> &lt;span class="nf">get_auth_header&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">):&lt;/span>
&lt;span class="n">xdate&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">datetime&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">utcnow&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">strftime&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;%a, &lt;/span>&lt;span class="si">%d&lt;/span>&lt;span class="s2"> %b %Y %H:%M:%S GMT&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="n">hashed&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">hmac&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">new&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">app_key&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">encode&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;utf8&amp;#39;&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">f&lt;/span>&lt;span class="s1">&amp;#39;x-date: {xdate}&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">encode&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;utf8&amp;#39;&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">sha1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="n">signature&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">base64&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">b64encode&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">hashed&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">digest&lt;/span>&lt;span class="p">())&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">decode&lt;/span>&lt;span class="p">()&lt;/span>
&lt;span class="n">authorization&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">f&lt;/span>&lt;span class="s1">&amp;#39;hmac username=&amp;#34;{self.app_id}&amp;#34;, algorithm=&amp;#34;hmac-sha1&amp;#34;&amp;#39;&lt;/span> &lt;span class="o">+&lt;/span>
&lt;span class="n">f&lt;/span>&lt;span class="s1">&amp;#39;, headers=&amp;#34;x-date&amp;#34;, signature=&amp;#34;{signature}&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="p">:&lt;/span> &lt;span class="n">authorization&lt;/span>&lt;span class="p">,&lt;/span>
&lt;span class="s1">&amp;#39;x-date&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">xdate&lt;/span>&lt;span class="p">,&lt;/span>
&lt;span class="s1">&amp;#39;Accept-Encoding&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;gzip&amp;#39;&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="k">if&lt;/span> &lt;span class="vm">__name__&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s1">&amp;#39;__main__&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;span class="n">auth&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Auth&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">app_id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">app_key&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="n">base_url&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;https://ptx.transportdata.tw&amp;#34;&lt;/span>
&lt;span class="c1"># 取得指定[車站]列車即時到離站電子看板(動態前後30分鐘的車次)&lt;/span>
&lt;span class="n">endpoint&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;/MOTC/v2/Rail/TRA/LiveBoard/Station/1000&amp;#34;&lt;/span>
&lt;span class="nb">filter&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;Direction eq 1&amp;#34;&lt;/span> &lt;span class="c1"># 順逆行: [0:&amp;#39;順行&amp;#39;, 1:&amp;#39;逆行&amp;#39;]&lt;/span>
&lt;span class="n">url&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">f&lt;/span>&lt;span class="s2">&amp;#34;{base_url}{endpoint}?$filter={filter}&amp;amp;$format=JSON&amp;#34;&lt;/span>
&lt;span class="n">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">requests&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">url&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">headers&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">auth&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get_auth_header&lt;/span>&lt;span class="p">())&lt;/span>
&lt;span class="k">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">response&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">json&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>除了參考我上面的 Python 範例，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;p>另外，在下一篇文章裡，我會分享 &lt;a href="https://blog.jiatool.com/posts/ptx_google_apps_script" target="_blank" rel="noopener">
Google Apps Script 的範例程式
&lt;/a>，它跟一般 JavaScript 有一些些不同，Google Apps Script 中預設應該是不能使用 jQuery，還有編碼的寫法也有些不同。&lt;/p>
&lt;br/>
&lt;!--adsense-->
&lt;h2 id="結語">結語&lt;/h2>
&lt;p>除了以上文章的解說，也可以看看&lt;a href="https://gist.github.com/ptxmotc/383118204ecf7192bdf96bc0197bb981" target="_blank" rel="noopener">
官方的文件說明
&lt;/a>。&lt;br />
有針對 &amp;quot;航空&amp;quot;、&amp;quot;公車&amp;quot;、&amp;quot;雙鐵(台鐵/高鐵)&amp;quot; 資料做進一步的說明，如果有使用到此 API 服務的人，建議進入先了解。&lt;br />
* 不過我看此文件上次更新時間 2018/04/12，可能有些資料不是最新的。&lt;/p>
&lt;br/>
&lt;p>上一篇了解 &lt;a href="https://blog.jiatool.com/posts/ptx_intro" target="_blank" rel="noopener">
PTX 平台的說明與 OData 介面
&lt;/a>，本篇則教你如何透過 Python 程式來發出請求、獲取資料，快將你有趣的 idea 來動手做實現它吧~&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;/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_python.jpg" medium="image"><media:title type="html">featured image</media:title></media:content><media:content url="https://blog.jiatool.comimages/posts/ptx_python_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>Python</category><category>分享</category></item><item><title>「PTX 公共運輸資訊平台」API 介紹 (含 Odata 說明)</title><link>https://blog.jiatool.com/posts/ptx_intro/</link><pubDate>Sat, 23 Apr 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_intro/</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>你手機裡很可能有安裝臺鐵、高鐵、捷運、公車等等公共運輸的 APP，或者使用過查詢時刻表、動態相關網站(&lt;a href="https://ptx.transportdata.tw/PTX/DemoApp/Example" target="_blank" rel="noopener">
示範應用列表
&lt;/a>)，但他們是怎麼取得這些資料的呢？&lt;/p>
&lt;p>原來有個政府的平台 — &lt;a href="https://ptx.transportdata.tw/PTX/" target="_blank" rel="noopener">
公共運輸整合資訊流通服務平台
&lt;/a>，它整合並提供這些數據資料，以 Web API 的形式提供我們串接。&lt;/p>
&lt;br/>
&lt;p>關於 PTX 平台我寫了三篇文章來介紹與程式教學：&lt;/p>
&lt;ol>
&lt;li>「PTX 公共運輸資訊平台」API 介紹 (含 Odata 說明) &amp;lt;&amp;ndash; 本篇&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>&lt;a href="https://blog.jiatool.com/posts/ptx_google_apps_script" target="_blank" rel="noopener">
「PTX 公共運輸資訊平台」Google Apps Script 範例
&lt;/a>&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="ptx-簡介">PTX 簡介&lt;/h2>
&lt;p>為了推動公共運輸整合資訊開放 (Open Data) 、統整各機關(構)的多元公共運輸資訊，交通部推出了這個 PTX 平台，包含了臺灣的公車、臺鐵、高鐵、捷運、航空、自行車、航運(海運)、觀光景點等公共運輸資料服務 API，非常的多樣，幾乎在臺灣你想的到的大眾交通工具都包在裡頭。&lt;br />
像是：臺鐵票價資料、高雄輕軌時刻表、臺北捷運即時通阻事件、機場即時入境航班、公車即時位置、公共自行車剩餘數量、觀光景點&amp;hellip;&amp;hellip;等等。&lt;/p>
&lt;p>PTX 網站有個「&lt;a href="https://ptx.transportdata.tw/PTX/Service" target="_blank" rel="noopener">
各項服務查詢
&lt;/a>」供檢視平台提供的服務，能依照 &amp;quot;領域類型&amp;quot;、&amp;quot;資料類型&amp;quot;、&amp;quot;業管機關&amp;quot; 去做篩選，每個服務還可以快速點開 API 說明及網址範例，另外還有整理成表格顯示：&lt;a href="https://ptx.transportdata.tw/PTX/Static/PDF_SupplyStatus.html" target="_blank" rel="noopener">
目前各單位資料供應現況表
&lt;/a>。&lt;/p>
&lt;figure >
&lt;img data-src="https://res.cloudinary.com/jiablog/ptx_intro/ptx_service.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;h2 id="加入會員--取得-app-id-與-app-key">加入會員 &amp;amp; 取得 APP ID 與 APP Key&lt;/h2>
&lt;p>為了提供穩定的服務、加強資訊安全管理，要使用此平台的 API 前需要先加入會員。&lt;/p>
&lt;h3 id="加入會員">加入會員&lt;/h3>
&lt;p>進入 &lt;a href="https://ptx.transportdata.tw/PTX/Management/AccountApply" target="_blank" rel="noopener">
加入會員
&lt;/a> 頁面，在此選擇「一般會員」即可，「進階會員」、「專案用戶」有更多的呼叫次數及其他權益，但那還要提供額外的證明，一般會員的 API 呼叫次數上限 20,000 次/日已經很夠我們自己玩玩，或做個小應用了。&lt;br />
(因為我以前註冊過，這邊就不演示了，按照網頁流程操作應該不會有什麼問題。)&lt;/p>
&lt;figure >
&lt;img data-src="https://res.cloudinary.com/jiablog/ptx_intro/account_apply.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;h3 id="取得-app-id-與-app-key">取得 APP ID 與 APP Key&lt;/h3>
&lt;p>(APP ID 與 APP Key 下一篇文章使用 Python 實際操作時才會用到)&lt;/p>
&lt;p>註冊完成並等待三個工作日審核後，會收到一封 Email，之後我們會使用到「基礎資料服務(L1)」的「APP ID」與「APP Key」。&lt;br />
而目前提供的資料服務多屬 L1，L2 的服務目前僅有場站空氣品質服務 (不知道現在 L2 是否有加入其他的服務)。&lt;/p>
&lt;br/>
&lt;p>或者也可登入後到 &lt;a href="https://ptx.transportdata.tw/PTX/APIMember/ApplyRecord" target="_blank" rel="noopener">
API金鑰申請
&lt;/a> 取得，如果忘記 APP Key 可點擊「忘記APP Key」即可，但要注意舊的 APP Key 就會失效，所以如果你 APP Key 不小心公開了也可由此來重新產生，避免額度被其他人使用。&lt;/p>
&lt;figure >
&lt;img data-src="https://res.cloudinary.com/jiablog/ptx_intro/ptx_api_key.jpg" alt="取得 APP ID 與 APP Key" data-caption="取得 APP ID 與 APP Key" 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;">
取得 APP ID 與 APP Key
&lt;/figcaption>
&lt;/figure>
&lt;br/>
&lt;!--adsense-->
&lt;h2 id="api-說明">API 說明&lt;/h2>
&lt;p>可以參考官方提供的「&lt;a href="https://motc-ptx-api-documentation.gitbook.io/motc-ptx-api-documentation/" target="_blank" rel="noopener">
資料使用葵花寶典
&lt;/a>」，此手冊分成以下四點作介紹說明&lt;/p>
&lt;ul>
&lt;li>API 會員：各會員層級進行說明並提供會員申請流程。&lt;/li>
&lt;li>API 使用說明：API 呼叫方法及注意事項進行說明。&lt;/li>
&lt;li>API 特色說明：API 獨特的使用技巧進行說明。&lt;/li>
&lt;li>API 資料使用注意事項：各運具資料面注意事項進行說明。&lt;/li>
&lt;/ul>
&lt;br/>
&lt;p>PTX 的 API 是以 OData（Open Data Protocol）標準介面格式對外開放供使用。&lt;/p>
&lt;br/>
&lt;h3 id="uri-設計概念">URI 設計概念&lt;/h3>
&lt;p>先來看看 URI 的設計概念。&lt;/p>
&lt;p>這邊以臺鐵 &amp;quot;取得指定[日期],[車站]的站別時刻表資料&amp;quot; 為例：&lt;/p>
&lt;figure >
&lt;img data-src="https://res.cloudinary.com/jiablog/ptx_intro/uri_convention.png" alt="臺鐵 &amp;#34;取得指定[日期],[車站]的站別時刻表資料&amp;#34;" data-caption="臺鐵 &amp;#34;取得指定[日期],[車站]的站別時刻表資料&amp;#34;" 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;">
臺鐵 &amp;#34;取得指定[日期],[車站]的站別時刻表資料&amp;#34;
&lt;/figcaption>
&lt;/figure>
&lt;p>從這張圖可以清楚的看出來，最前方 &amp;quot;基本網址&amp;quot; 大家都一樣，再來是 &amp;quot;API版本&amp;quot;、&amp;quot;鐵道&amp;quot; 中的 &amp;quot;臺鐵&amp;quot;，我們想抓的 &amp;quot;站別時刻表&amp;quot;，以及最後方帶入的參數 &amp;quot;車站代碼&amp;quot; 與 &amp;quot;日期&amp;quot;。&lt;br />
(車站代碼可以從另外一個 &amp;quot;車站基本資料&amp;quot; API 取得，或臺鐵官網的&lt;a href="https://tip.railway.gov.tw/tra-tip-web/tip/tip001/tip111/view" target="_blank" rel="noopener">
車站代碼表
&lt;/a>)&lt;/p>
&lt;p>雖然每項服務會有些不同，但其網址結構都是差不多的，這邊稍微知道就好，詳細說明請參考官方提供的「&lt;a href="https://docs.google.com/viewer?url=https://github.com/ptxmotc/PTX_Web/blob/master/Swagger%E6%9C%8D%E5%8B%99%E8%AA%AA%E6%98%8E%E4%B8%8A%E5%82%B3%E5%8F%83%E8%80%83%E6%AA%94%E6%A1%88/API_URI_Convention%E6%96%87%E4%BB%B6_v1.pdf?raw=true" target="_blank" rel="noopener">
API URI Convention文件
&lt;/a>」。&lt;/p>
&lt;br/>
&lt;h3 id="swagger-文件工具">Swagger 文件工具&lt;/h3>
&lt;p>至於各項服務 API 說明可以從&lt;a href="https://ptx.transportdata.tw/MOTC/" target="_blank" rel="noopener">
這裡
&lt;/a>進入，或到「&lt;a href="https://ptx.transportdata.tw/PTX/Service" target="_blank" rel="noopener">
PTX 各項服務查詢
&lt;/a>」點擊對應服務右方的&amp;quot;服務說明&amp;quot;，它是透過 Swagger 這個文件工具來呈現。&lt;/p>
&lt;br/>
&lt;p>這邊先講個 Swagger 基本使用。&lt;/p>
&lt;p>找一個感興趣的資源(Resource)，並打開其 API 說明。&lt;/p>
&lt;figure >
&lt;img data-src="https://res.cloudinary.com/jiablog/ptx_intro/ptx_swagger01.png" alt="API 介面說明" data-caption="API 說明" 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;">
API 說明
&lt;/figcaption>
&lt;/figure>
&lt;br/>
&lt;p>大致分為幾個部分，最上方針對此資源簡短描述，再來 &amp;quot;Parameters&amp;quot; 是此資源可附加上的參數，像是我想看哪一個捷運系統的車站資料，就是在這邊帶上參數，除了標示 required 紅字為必填，其餘我們先暫時不必理會。最後 &amp;quot;Responses&amp;quot; 會寫到當回應哪個 HTTP Status Code 對應是哪個意思，其回應資料格式又是長什麼樣子。&lt;/p>
&lt;figure >
&lt;img data-src="https://res.cloudinary.com/jiablog/ptx_intro/ptx_swagger02.jpg" alt="API 介面說明" data-caption="API 說明" 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;">
API 說明
&lt;/figcaption>
&lt;/figure>
&lt;p>回應資料有些欄位可能看不出它代表意思，就可以到這邊找找說明。&lt;/p>
&lt;figure >
&lt;img data-src="https://res.cloudinary.com/jiablog/ptx_intro/ptx_swagger03.png" alt="API 介面說明" data-caption="API 說明" 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;">
API 說明
&lt;/figcaption>
&lt;/figure>
&lt;br/>
&lt;p>在 Parameters 與 Responses 中間有個大大的藍色 &amp;quot;Execute&amp;quot; 按鈕，我們給他用力按下去。&lt;/p>
&lt;p>噹🌟！發現在底下 &amp;quot;Responses&amp;quot; 區塊跑出好幾個新東西：&lt;/p>
&lt;ul>
&lt;li>&amp;quot;Request URL&amp;quot; 代表依照你上方設定的參數，其請求網址會長怎樣。&lt;/li>
&lt;li>&amp;quot;Server response&amp;quot; 顯示回應 HTTP Status Code 及回應內容(Response body)，回應內容就是我們想要的資料啦~&lt;/li>
&lt;/ul>
&lt;figure >
&lt;img data-src="https://res.cloudinary.com/jiablog/ptx_intro/responses.png" alt="API 回應說明" data-caption="API 回應說明" 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;">
API 回應說明
&lt;/figcaption>
&lt;/figure>
&lt;p>這個 Swagger 工具大致上的用法就是這樣。&lt;/p>
&lt;br/>
&lt;p>到這邊，都可以如實拿到想要的資料了。&lt;/p>
&lt;p>但可能它每次回傳的資料量很大，我只是需要其中的一部分資料而已，這樣會浪費頻寬、增加回應時間、接收到資料後還要費力處理，那有沒有能在送出請求時，就告訴 Server 我們想要那些資料呢？&lt;br />
有的！！接下來就要講到 Odata 這個介面格式厲害的地方了😮&lt;/p>
&lt;br/>
&lt;h3 id="odata-查詢語法">Odata 查詢語法&lt;/h3>
&lt;p>通過 Odata 協定制定的用法，我們可以去做多樣的查詢、過濾、排序，甚至有更進階的函數語法。&lt;/p>
&lt;p>我會依序介紹以下六種 Odata 的查詢方法：&lt;/p>
&lt;ul>
&lt;li>&lt;code>$format&lt;/code> 指定來源格式&lt;/li>
&lt;li>&lt;code>$orderby&lt;/code> 排序&lt;/li>
&lt;li>&lt;code>$top&lt;/code> 取前幾筆&lt;/li>
&lt;li>&lt;code>$skip&lt;/code> 跳過前幾筆&lt;/li>
&lt;li>&lt;code>$select&lt;/code> 挑選&lt;/li>
&lt;li>&lt;code>$filter&lt;/code> 過濾&lt;/li>
&lt;/ul>
&lt;p>這邊我是依照比較好理解的順序介紹說明，最後再舉幾個複合範例給各位參考。&lt;/p>
&lt;br/>
&lt;h4 id="format-指定來源格式">$format 指定來源格式&lt;/h4>
&lt;p>指定回傳資料要以什麼格式。&lt;/p>
&lt;p>PTX 提供 JSON 與 XML 兩種格式供選擇。&lt;/p>
&lt;ul>
&lt;li>&lt;code>$format=json&lt;/code>&lt;/li>
&lt;li>&lt;code>$format=xml&lt;/code>&lt;/li>
&lt;/ul>
&lt;h4 id="orderby-排序">$orderby 排序&lt;/h4>
&lt;p>指定回傳資料的排序。&lt;/p>
&lt;p>升冪：由小到大；降冪：由大到小。&lt;/p>
&lt;ul>
&lt;li>針對欄位1作升冪 (預設)&lt;br />
&lt;code>$orderby=Field1&lt;/code>&lt;br />
&lt;code>$orderby=Field1 asc&lt;/code>&lt;/li>
&lt;li>針對欄位1作降冪&lt;br />
&lt;code>$orderby=Field1 desc&lt;/code>&lt;/li>
&lt;li>針對欄位1作升冪，欄位2降冪&lt;br />
&lt;code>$orderby=Field1 asc,Field2 desc&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>各 API 服務有不同的回傳資料，例如我在查詢公共自行車即時車位時，希望資料依照 &amp;quot;可租借數量&amp;quot; 由多至少排列：&lt;br />
&lt;code>$orderby=AvailableRentBikes desc&lt;/code>&lt;/p>
&lt;h4 id="top-skip-取前幾筆">$top $skip 取前幾筆&lt;/h4>
&lt;p>這兩個是類似的概念。&lt;/p>
&lt;ul>
&lt;li>&lt;code>$top&lt;/code>：取前幾筆資料。&lt;/li>
&lt;li>&lt;code>$skip&lt;/code> 跳過前幾筆資料。&lt;/li>
&lt;/ul>
&lt;p>像是我想拿取前 11~30 筆資料：&lt;br />
&lt;code>$top=20&amp;amp;$skip=10&lt;/code>&lt;/p>
&lt;h4 id="select-挑選">$select 挑選&lt;/h4>
&lt;p>指定只需要回傳那些欄位，多個欄位可用逗號(&lt;code>,&lt;/code>)隔開，未指定則回傳全部欄位。&lt;br />
(目前只支援第一層欄位)&lt;/p>
&lt;ul>
&lt;li>只回傳欄位1&lt;br />
&lt;code>$select=Field1&lt;/code>&lt;/li>
&lt;li>回傳多個欄位，欄位1和欄位2&lt;br />
&lt;code>$select=Field1,Field2&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>不過它有但書：&lt;/p>
&lt;ul>
&lt;li>回傳資料指定為 json 時，只會回傳被 select 的欄位，除此之外，若其他欄位為非 nullable，也會回傳系統預設值。&lt;/li>
&lt;li>回傳資料指定為 xml，沒有被指定的屬性若為 class 或是 string，不會回傳該欄位，但若是其他屬性(int,bool,enum..)，還是會回傳該欄位，其值為系統預設值。&lt;/li>
&lt;/ul>
&lt;h4 id="filter-過濾">$filter 過濾&lt;/h4>
&lt;p>(來到最複雜、最多變化的一個語法了😅)&lt;/p>
&lt;p>filter 語法是用來對資料做篩選、過濾，提供「邏輯運算子」、「算術運算子」、「規範函數」、「Lambda Operators」可使用。&lt;/p>
&lt;br/>
&lt;p>&lt;strong>邏輯運算子 (Logical Operators)&lt;/strong>&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>邏輯運算子&lt;/th>
&lt;th>意義&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>eq&lt;/td>
&lt;td>等於&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>ne&lt;/td>
&lt;td>不等於&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>gt&lt;/td>
&lt;td>大於&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>ge&lt;/td>
&lt;td>大於等於&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>lt&lt;/td>
&lt;td>小於&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>le&lt;/td>
&lt;td>小於等於&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>and&lt;/td>
&lt;td>而且、和&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>or&lt;/td>
&lt;td>或者&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>not&lt;/td>
&lt;td>否定&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;ul>
&lt;li>
&lt;p>例如想查詢公共自行車 &amp;quot;可租借數量&amp;quot; 大於 10 台的站點：&lt;br />
&lt;code>$filter=AvailableRentBikes gt 10&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>假如欄位是在第二層，則可透過 &lt;code>/&lt;/code> 連接。&lt;br />
例如想取得高雄捷運 &amp;quot;左營站 &amp;quot; 的即時到離站電子看板：&lt;br />
&lt;code>$filter=StationName/Zh_tw eq '左營'&lt;/code>&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>* 要注意後方比對的字串需使用單引號 &lt;code>'&lt;/code> 框起來，雙引號 &lt;code>&amp;quot;&lt;/code> 是不行的哦。&lt;/p>
&lt;br/>
&lt;p>&lt;strong>算術運算子 (Arithmetic Operators)&lt;/strong>&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>算數運算子&lt;/th>
&lt;th>意義&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>add&lt;/td>
&lt;td>加&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>sub&lt;/td>
&lt;td>減&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>mul&lt;/td>
&lt;td>乘&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>div&lt;/td>
&lt;td>除&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>mod&lt;/td>
&lt;td>餘數&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;ul>
&lt;li>例如我想查詢有哪台公車目前時速是奇數：&lt;br />
&lt;code>$filter=Speed mod 2 eq 1&lt;/code>&lt;br />
(知道這個好像也不能幹嘛&amp;hellip;&amp;hellip;)&lt;/li>
&lt;/ul>
&lt;br/>
&lt;p>&lt;strong>規範函數 (Canonical Functions)&lt;/strong>&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>規範函數&lt;/th>
&lt;th>意義&lt;/th>
&lt;th>&lt;/th>
&lt;th>規範函數&lt;/th>
&lt;th>意義&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>substring&lt;/td>
&lt;td>子字串&lt;/td>
&lt;td>&lt;/td>
&lt;td>year&lt;/td>
&lt;td>年份&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>startswith&lt;/td>
&lt;td>字串開頭&lt;/td>
&lt;td>&lt;/td>
&lt;td>month&lt;/td>
&lt;td>月份&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>endswith&lt;/td>
&lt;td>字串結尾&lt;/td>
&lt;td>&lt;/td>
&lt;td>day&lt;/td>
&lt;td>日&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>length&lt;/td>
&lt;td>字串長度&lt;/td>
&lt;td>&lt;/td>
&lt;td>hour&lt;/td>
&lt;td>小時&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>indexof&lt;/td>
&lt;td>指定字串出現位置&lt;/td>
&lt;td>&lt;/td>
&lt;td>minute&lt;/td>
&lt;td>分&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>tolower&lt;/td>
&lt;td>字串變小寫&lt;/td>
&lt;td>&lt;/td>
&lt;td>second&lt;/td>
&lt;td>秒&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>toupper&lt;/td>
&lt;td>字串變大寫&lt;/td>
&lt;td>&lt;/td>
&lt;td>fractionalseconds&lt;/td>
&lt;td>小數秒&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>trim&lt;/td>
&lt;td>去空白&lt;/td>
&lt;td>&lt;/td>
&lt;td>date&lt;/td>
&lt;td>日期&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>contains&lt;/td>
&lt;td>包含&lt;/td>
&lt;td>&lt;/td>
&lt;td>time&lt;/td>
&lt;td>時間&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>concat&lt;/td>
&lt;td>串接&lt;/td>
&lt;td>&lt;/td>
&lt;td>&lt;/td>
&lt;td>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;/td>
&lt;td>&lt;/td>
&lt;td>&lt;/td>
&lt;td>round&lt;/td>
&lt;td>四捨五入&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;/td>
&lt;td>&lt;/td>
&lt;td>&lt;/td>
&lt;td>floor&lt;/td>
&lt;td>無條件捨去&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;/td>
&lt;td>&lt;/td>
&lt;td>&lt;/td>
&lt;td>ceiling&lt;/td>
&lt;td>無條件進位&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;/td>
&lt;td>&lt;/td>
&lt;td>&lt;/td>
&lt;td>cast&lt;/td>
&lt;td>轉型&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;ul>
&lt;li>
&lt;p>例如取得公車動態中車牌號碼開頭是 &amp;quot;001&amp;quot; 的資料：&lt;br />
&lt;code>$filter=startswith(PlateNumb, '001')&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>或者想知道車牌號碼長度為 7 個字的公車：&lt;br />
&lt;code>$filter=length(PlateNumb) eq 7&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>去台南旅遊，想找出店名包含 &amp;quot;豆花&amp;quot; 的店家：&lt;br />
&lt;code>$filter=contains(RestaurantName,'豆花')&lt;/code>&lt;/p>
&lt;/li>
&lt;/ul>
&lt;br/>
&lt;p>&lt;strong>Lambda Operators&lt;/strong>&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Lambda Operators&lt;/th>
&lt;th>意義&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>all&lt;/td>
&lt;td>所有項目都要符合&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>any&lt;/td>
&lt;td>其中一項符合&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;ul>
&lt;li>例如我想知道今天有哪幾班高鐵會停 &amp;quot;雲林&amp;quot;：&lt;br />
&lt;code>$filter=StopTimes/any(d:d/StationName/Zh_tw eq '雲林')&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>這部分可能一開始有些難懂，主要是用在判斷陣列中的值。&lt;br />
* 其中的 &lt;code>d&lt;/code> 是可以隨意換其他名稱的。&lt;/p>
&lt;br/>
&lt;br/>
&lt;p>&lt;strong>多種複合用法&lt;/strong>&lt;/p>
&lt;p>為了讓大家更容易理解，這邊我再舉幾個多種語法所組合的用法：&lt;/p>
&lt;ul>
&lt;li>
&lt;p>想看看高鐵在今年(2022年) 3 月之後有發布什麼關於 &amp;quot;左營&amp;quot; 的最新消息：&lt;br />
&lt;code>$filter=year(PublishTime) eq 2022 and month(PublishTime) ge 3 and contains(Title,'左營')&lt;/code>&lt;br />
(啊&amp;hellip;4/1 水泥施工事故導致電力異常，台南-左營站間停駛)&lt;/p>
&lt;/li>
&lt;li>
&lt;p>台南市公車中，路線是 &amp;quot;紅&amp;quot; 字開頭，並且目前 &amp;quot;時速超過 30 公里&amp;quot;，依照時速由大排到小的前五名：&lt;br />
&lt;code>$filter=startswith(SubRouteName/Zh_tw,'紅') and Speed gt 30&amp;amp;$orderby=Speed desc&amp;amp;$top=5&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>因為提著大包小包，想知道台北捷運 &amp;quot;古亭站&amp;quot; 有哪個出口有 &amp;quot;電扶梯&amp;quot; 或 &amp;quot;電梯&amp;quot;，且只回傳 &amp;quot;出口&amp;quot; 和 &amp;quot;地址&amp;quot;：&lt;br />
(備註：Escalator 是否有電扶梯(0:沒有,1:雙向,2:出站,3:入站))&lt;br />
&lt;code>$select=ExitName,LocationDescription&amp;amp;$filter=StationName/Zh_tw eq '古亭' and (Escalator ne 0 or Elevator eq true)&lt;/code>&lt;/p>
&lt;/li>
&lt;/ul>
&lt;br/>
&lt;br/>
&lt;p>有關如何使用 Odata 查詢 PTX API 資料，官方有整裡個簡報—「&lt;a href="https://docs.google.com/viewer?url=https://github.com/ptxmotc/PTX_Web/blob/master/Swagger%E6%9C%8D%E5%8B%99%E8%AA%AA%E6%98%8E%E4%B8%8A%E5%82%B3%E5%8F%83%E8%80%83%E6%AA%94%E6%A1%88/%E5%85%AC%E5%85%B1%E9%81%8B%E8%BC%B8%E6%95%B4%E5%90%88%E8%B3%87%E8%A8%8A%E5%B9%B3%E5%8F%B0%E8%B3%87%E6%96%99%E6%9C%8D%E5%8B%99%E9%96%8B%E7%99%BC%E5%AF%A6%E4%BD%9C.pdf?raw=true" target="_blank" rel="noopener">
PTX APIs開發技術說明(含Odata)
&lt;/a>」，裡頭有更詳細地介紹 Odata，以及其查詢選項、函數語法。&lt;/p>
&lt;p>當然，如果你覺得這些篩選語法太麻煩，也是可以全部抓下來後，再使用程式去處理。&lt;/p>
&lt;br/>
&lt;h2 id="api-呼叫統計">API 呼叫統計&lt;/h2>
&lt;p>PTX 平台也提供「&lt;a href="https://ptx.transportdata.tw/PTX/APIMember/APICallTime" target="_blank" rel="noopener">
服務呼叫統計
&lt;/a>」網頁，可查看自己目前的資料數據傳輸量、呼叫次數以及虛擬點數等等統計數據，確認還有多少額度可以使用，甚至也針對各項服務做統計。&lt;/p>
&lt;figure >
&lt;img data-src="https://res.cloudinary.com/jiablog/ptx_intro/api_call_time01.jpg" alt="PTX 服務呼叫統計" data-caption="PTX 服務呼叫統計" src="data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='750px' 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:750px;height:;"/>
&lt;figcaption style="text-align: center;">
PTX 服務呼叫統計
&lt;/figcaption>
&lt;/figure>
&lt;figure >
&lt;img data-src="https://res.cloudinary.com/jiablog/ptx_intro/api_call_time02.jpg" alt="PTX 服務呼叫統計" data-caption="PTX 服務呼叫統計" src="data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='750px' 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:750px;height:;"/>
&lt;figcaption style="text-align: center;">
PTX 服務呼叫統計
&lt;/figcaption>
&lt;/figure>
&lt;br/>
&lt;!--adsense-->
&lt;h2 id="結語">結語&lt;/h2>
&lt;p>有了這個 PTX 平台後，想要取得各大眾公共運輸的資料變得相當容易，除了做成應用程式自用外，也可以公開讓大家使用。&lt;/p>
&lt;p>官方也有整理一些 &lt;a href="https://ptx.transportdata.tw/PTX/Common/FAQ" target="_blank" rel="noopener">
常見問題
&lt;/a> 供參考。&lt;/p>
&lt;br/>
&lt;p>如何？看完介紹後你腦中是否產生有趣的 idea 了呢😙&lt;br />
別急，下一篇文章會來教你該&lt;a href="https://blog.jiatool.com/posts/ptx_python" target="_blank" rel="noopener">
如何使用 Python 來串接 PTX API
&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://motc-ptx-api-documentation.gitbook.io/motc-ptx-api-documentation/" target="_blank" rel="noopener">
PTX 資料使用葵花寶典
&lt;/a>&lt;br />
&lt;a href="https://ptx.transportdata.tw/MOTC/" target="_blank" rel="noopener">
PTX API 說明 | Swagger
&lt;/a>&lt;br />
&lt;a href="https://docs.google.com/viewer?url=https://github.com/ptxmotc/PTX_Web/blob/master/Swagger%E6%9C%8D%E5%8B%99%E8%AA%AA%E6%98%8E%E4%B8%8A%E5%82%B3%E5%8F%83%E8%80%83%E6%AA%94%E6%A1%88/%E5%85%AC%E5%85%B1%E9%81%8B%E8%BC%B8%E6%95%B4%E5%90%88%E8%B3%87%E8%A8%8A%E5%B9%B3%E5%8F%B0%E8%B3%87%E6%96%99%E6%9C%8D%E5%8B%99%E9%96%8B%E7%99%BC%E5%AF%A6%E4%BD%9C.pdf?raw=true" target="_blank" rel="noopener">
PTX APIs開發技術說明(含Odata)
&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_intro.jpg" medium="image"><media:title type="html">featured image</media:title></media:content><media:content url="https://blog.jiatool.comimages/posts/ptx_intro_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>分享</category></item></channel></rss>