# AWS Comprehend # 我們要做什麼 ![1000001990](https://hackmd.io/_uploads/B11KP8Q-gx.jpg) # 簡介流程 ![image](https://hackmd.io/_uploads/rko2P2B-xe.png) # 前置步驟 ```php= /** * Plugin Name: AWS_comprehend * Author: YUE YUE * Version: 1.0.0 */ ``` * 防止檔案被直接存取 ```php= if (!defined('ABSPATH')) { exit; } ``` * 先引用等等會用到的功能 ```php= require_once dirname(__DIR__) . '/vendor/autoload.php'; use WP_REST_Response; use Aws\Comprehend\ComprehendClient; ``` * 加入定義函式 ```php= add_action('wp_enqueue_scripts', 'comprehend_load_assets'); add_shortcode('comprehend-form', 'comprehend_text_form'); add_action('wp_footer', 'comprehend_load_scripts'); add_action('rest_api_init', 'comprehend_register_rest_api'); ``` # (第一部分)前端頁面 ## function 1 建立表單 ### 加入CSS美化 javascript 提供html簡單互動 ```php= function comprehend_load_assets() { wp_enqueue_style( 'bootstrap-css', 'https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css', array(), '5.3.0', 'all' ); wp_enqueue_script('jquery'); wp_enqueue_script( 'bootstrap-js', 'https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js', array('jquery'), '5.3.0', true ); } ``` ### 設計表單 ```html= function comprehend_text_form() { ob_start(); ?> <div class="container mt-5"> <h1>AWS Comprehend Form</h1> <p> Please choose the service</p> <form id="aws-comprehend-form__form"> <div class="form-group mb-2"> <select class="form-select form-control" name="comprehend_service"> <option value="Sentiment Analysis">Sentiment Analysis(情感分析)</option> <option value="Syntax Analysis">Syntax Analysis(句法分析)</option> <option value="Entity Analysis">Entity Analysis(實體辨識)</option> </select> </div> <div class="form-group mb-2 form-floating"> <textarea class="form-control" name="text_to_be_analysed" placeholder="Leave a comment here" id="floatingTextarea" style="height: 200px"></textarea> <label for="floatingTextarea">請輸入要分析文字</label> </div> <div class="form-group"> <button class="btn btn-success btn-block" type="submit">Send</button> </div> </form> <div id="result-message" class="alert" style="display:none;"></div> </div> <?php return ob_get_clean(); } ``` 當表單被送出時,會長下面這樣 ```json { "comprehend_service": "Sentiment Analysis", "text_to_be_analysed": "這是一段要被 AWS Comprehend 分析的文字" } ``` ## function 2 用javascript偵測送出表單 >```php= >public function load_scripts(){ >//偵測表單送出,並送到後端 >} >``` 第一步一定要先偵測表單送出submit ```php= $(`#aws-comprehend-form__form`).off('submit').submit(function(event){ //處理收到表單後事情 }); ``` **當收到表單後** 首先要建立一個nonce,其作用是防止 CSRF(跨站請求偽造)攻擊,等等要在要包含在後端請求中 ``` var nonce= '<?php echo wp_create_nonce('wp_rest');?>'; ``` 接下來要將前端UI送出按鈕改變樣式並禁止送出 ```php= $('#aws-comprehend-form__form').find('button').prop('disabled', true); // 禁用提交按鈕 $('#aws-comprehend-form__form').find('button').text('處理中...'); // 更改按鈕文字 $('#result-message').removeClass('alert-success alert-danger').addClass('alert-info').text('處理中...').show(); // 顯示處理中訊息 ``` 最後要將表單訊息送到後端處理 1. 如果處理成功,如果將按鈕變回可以送出下一個表單 2. 如果處理失敗,在下方顯示錯誤 ```php= $.ajax({ method: 'post', url: '<?php echo get_rest_url(null, 'comprehend-form/v1/send-api-to-aws');?>', headers: { 'X-WP-Nonce': nonce }, data: form, success: function(response) { // 更新前端樣式與顯示回應結果 $('#aws-comprehend-form__form').find('button').prop('disabled', false); // 啟用提交按鈕 $('#aws-comprehend-form__form').find('button').text('送出'); // 恢復按鈕文字 $('#result-message').removeClass('alert-info alert-danger') .addClass('alert-success') .html(response.comprehend_output) .show(); // 顯示成功訊息 }, error: function(xhr, status, error) { // 更新錯誤顯示 $('#aws-comprehend-form__form').find('button').prop('disabled', false); // 啟用提交按鈕 $('#aws-comprehend-form__form').find('button').text('送出'); // 恢復按鈕文字 $('#result-message').removeClass('alert-info alert-success') .addClass('alert-danger') .text('❌ 發生錯誤:' + error) .show(); // 顯示錯誤訊息 console.log('AJAX Error:', error); console.log('Response:', xhr.responseText); } }); ``` >[!Important]完整程式碼 >:::spoiler comprehend_load_script() > >```php= >function comprehend_load_scripts() >{ > ?> > <script> > var nonce = '<?php echo wp_create_nonce('wp_rest'); ?>'; > (function ($) { > $('#aws-comprehend-form__form').off('submit').submit(function (event) { > event.preventDefault(); > var form = $(this).serialize(); > > $('#aws-comprehend-form__form').find('button').prop('disabled', true); > $('#aws-comprehend-form__form').find('button').text('處理中...'); > $('#result-message').removeClass('alert-success alert-danger').addClass('alert-info').text('處理中...').show(); > > $.ajax({ > method: 'post', > url: '<?php echo esc_url(get_rest_url(null, 'comprehend-form/v1/send-api-to-aws')); ?>', > headers: {'X-WP-Nonce': nonce}, > data: form, > success: function (response) { > $('#aws-comprehend-form__form').find('button').prop('disabled', false); > $('#aws-comprehend-form__form').find('button').text('送出'); > $('#result-message').removeClass('alert-info alert-danger').addClass('alert-success').html(response.comprehend_output).show(); > }, > error: function (xhr, status, error) { > $('#aws-comprehend-form__form').find('button').prop('disabled', false); > $('#aws-comprehend-form__form').find('button').text('送出'); > $('#result-message').removeClass('alert-info alert-success').addClass('alert-danger').text('❌ 發生錯誤:' + error).show(); > console.log('AJAX Error:', error); > console.log('Response:', xhr.responseText); > } > }); > }); > })(jQuery); > </script> > <?php >} >``` <!-- ?> --> # (第二部分)後端功能 ## function 3 註冊RestAPI ### 為了讓前端能送請求,透過網址comprehend-form/v1/send-api-to-aws ```php= function comprehend_register_rest_api() { register_rest_route('comprehend-form/v1', 'send-api-to-aws', array( 'methods' => 'POST', 'callback' => 'comprehend_handle_form', 'permission_callback' => function () { return true; // 可自行增加權限判斷 } )); } ``` ```php= function comprehend_handle_form(WP_REST_Request $request) { //檢查nonce是否正確 //取出分析文字及使用服務 //呼叫comprehend_call_aws($text, $service) 分析 //將結果傳回前端 } ``` :::spoiler comprehend_handle_form ```php= function comprehend_handle_form(WP_REST_Request $request) { if (!wp_verify_nonce($request->get_header('x-wp-nonce'), 'wp_rest')) { return new WP_REST_Response(['message' => '驗證失敗'], 422); } $params = $request->get_params(); $text = sanitize_text_field($params['text_to_be_analysed'] ?? ''); $service = sanitize_text_field($params['comprehend_service'] ?? ''); if (empty($text)) { return new WP_REST_Response(['message' => '請輸入文字'], 422); } try { $result = comprehend_call_aws($text, $service); } catch (Exception $e) { return new WP_REST_Response(['message' => 'AWS API錯誤: ' . $e->getMessage()], 500); } return new WP_REST_Response([ 'message' => '提交成功', 'comprehend_service' => $service, 'comprehend_output' => $result ], 200); } ``` ::: ## function 4 開始處理分析 ```php= function comprehend_call_aws($text, $service) { //取得AWS_Client,才能呼叫API //根據選擇的服務送出不同的請求 //取得結果,並轉成HTML返回 } ``` :::spoiler comprehend_call_aws ```php= function comprehend_call_aws($text, $service) { $client = comprehend_get_aws_client(); if (!$client) { throw new Exception('AWS 客戶端初始化失敗'); } switch ($service) { case 'Sentiment Analysis': $result = $client->detectSentiment([ 'LanguageCode' => 'zh-TW', 'Text' => $text, ]); return comprehend_generate_sentiment_html([ 'sentiment' => $result['Sentiment'], 'scores' => $result['SentimentScore'], ]); case 'Syntax Analysis': $result = $client->detectSyntax([ 'LanguageCode' => 'en', 'Text' => $text, ]); return comprehend_generate_syntax_html(['tokens' => $result['SyntaxTokens']]); case 'Entity Analysis': $result = $client->detectEntities([ 'LanguageCode' => 'zh-TW', 'Text' => $text, ]); return comprehend_generate_entity_html(['entities' => $result['Entities']]); default: throw new Exception('未知的服務類型'); } } ``` ::: ### 建立用戶端 ```php= function comprehend_get_aws_client() { $region = 'us-west-2'; $accessKeyId = '你的key'; $secretAccessKey = '你的secretKey'; try { $client = new ComprehendClient([ 'region' => $region, 'version' => 'latest', 'credentials' => [ 'key' => $accessKeyId, 'secret' => $secretAccessKey, ], 'http' => [ 'timeout' => 10, 'connect_timeout' => 5, ], ]); return $client; } catch (Exception $e) { error_log('AWS SDK 初始化錯誤: ' . $e->getMessage()); return null; } } ``` ## function 5 將AWS Comprehend分析結果打包成HTML :::spoiler 產生情感分析結果 HTML ```php= function comprehend_generate_sentiment_html($result) { $sentiment = esc_html($result['sentiment']); $html = '<div class="sentiment-analysis-result">'; $html .= '<h4>情感分析結果</h4>'; $html .= '<p>情感分析結果是: <strong>' . $sentiment . '</strong></p>'; if (isset($result['scores'])) { $html .= '<table class="table table-striped table-bordered">'; $html .= '<thead><tr><th>情感類型</th><th>可信度</th></tr></thead>'; $html .= '<tbody>'; foreach ($result['scores'] as $type => $score) { $html .= '<tr>'; $html .= '<td>' . esc_html($type) . '</td>'; $html .= '<td>' . round($score * 100, 1) . '%</td>'; $html .= '</tr>'; } $html .= '</tbody></table>'; } $html .= '</div>'; return $html; } ``` ::: :::spoiler 產生句法分析結果 HTML ```php= function comprehend_generate_syntax_html($result) { $tokens = $result['tokens']; $html = '<div class="syntax-analysis-result">'; $html .= '<h4>語法分析結果</h4>'; if (empty($tokens)) { $html .= '<p>未檢測到任何語法成分。</p>'; } else { $html .= '<table class="table table-striped table-bordered">'; $html .= '<thead><tr><th>詞語</th><th>詞性</th><th>可信度</th><th>起始位置</th><th>結束位置</th></tr></thead><tbody>'; foreach ($tokens as $token) { $text = esc_html($token['Text']); $pos = esc_html($token['PartOfSpeech']['Tag']); $score = isset($token['PartOfSpeech']['Score']) ? round($token['PartOfSpeech']['Score'] * 100, 1) . '%' : 'N/A'; $begin = $token['BeginOffset']; $end = $token['EndOffset']; $html .= '<tr>'; $html .= "<td>$text</td><td>$pos</td><td>$score</td><td>$begin</td><td>$end</td>"; $html .= '</tr>'; } $html .= '</tbody></table>'; } $html .= '</div>'; return $html; } ``` ::: :::spoiler 產生實體分析結果 HTML ```php= function comprehend_generate_entity_html($result) { $entities = $result['entities']; $html = '<div class="entity-analysis-result">'; $html .= '<h4>實體分析結果</h4>'; if (empty($entities)) { $html .= '<p>未檢測到任何實體。</p>'; } else { $html .= '<table class="table table-striped table-bordered">'; $html .= '<thead><tr><th>實體</th><th>類型</th><th>可信度</th><th>開始位置</th><th>結束位置</th></tr></thead><tbody>'; foreach ($entities as $entity) { $text = esc_html($entity['Text']); $type = esc_html($entity['Type']); $score = round($entity['Score'] * 100, 1) . '%'; $begin = $entity['BeginOffset']; $end = $entity['EndOffset']; $html .= '<tr>'; $html .= "<td>$text</td><td>$type</td><td>$score</td><td>$begin</td><td>$end</td>"; $html .= '</tr>'; } $html .= '</tbody></table>'; } $html .= '</div>'; return $html; } ``` ::: --- # 完整版程式碼及提醒 >[!Important] 全部Comprehend_程式碼 >:::spoiler >```php= ><?php >/** > * Plugin Name: AWS_comprehend > * Author: YUE YUE > * Version: 1.0.0 > */ >if (!defined('ABSPATH')) { > exit; >} > >require_once dirname(__DIR__) . '/vendor/autoload.php'; > >use WP_REST_Response; >use Aws\Comprehend\ComprehendClient; > >add_action('wp_enqueue_scripts', 'comprehend_load_assets'); >add_shortcode('comprehend-form', 'comprehend_text_form'); >add_action('wp_footer', 'comprehend_load_scripts'); >add_action('rest_api_init', 'comprehend_register_rest_api'); >/** > * 載入前端資源 (Bootstrap + jQuery) > */ >function comprehend_load_assets() >{ > wp_enqueue_style( > 'bootstrap-css', > 'https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css', > array(), > '5.3.0', > 'all' > ); > > wp_enqueue_script('jquery'); > > wp_enqueue_script( > 'bootstrap-js', > 'https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js', > array('jquery'), > '5.3.0', > true > ); >} > >/** > * 顯示 AWS Comprehend 表單 > */ >function comprehend_text_form() >{ > ob_start(); > ?> > <div class="container mt-5"> > <h1>AWS Comprehend Form</h1> > <p> Please choose the service</p> > <form id="aws-comprehend-form__form"> > <div class="form-group mb-2"> > <select class="form-select form-control" name="comprehend_service"> > <option value="Sentiment Analysis">Sentiment Analysis(情感分析)</option> > <option value="Syntax Analysis">Syntax Analysis(句法分析)</option> > <option value="Entity Analysis">Entity Analysis(實體辨識)</option> > </select> > </div> > <div class="form-group mb-2 form-floating"> > <textarea class="form-control" name="text_to_be_analysed" placeholder="Leave a comment here" id="floatingTextarea" style="height: 200px"></textarea> > <label for="floatingTextarea">請輸入要分析文字</label> > </div> > <div class="form-group"> > <button class="btn btn-success btn-block" type="submit">Send</button> > </div> > </form> > <div id="result-message" class="alert" style="display:none;"></div> > </div> > <?php > return ob_get_clean(); >} > >/** > * 載入前端 AJAX JS 代碼 > */ >function comprehend_load_scripts() >{ > ?> > <script> > var nonce = '<?php echo wp_create_nonce('wp_rest'); ?>'; > (function ($) { > $('#aws-comprehend-form__form').off('submit').submit(function (event) { > event.preventDefault(); > var form = $(this).serialize(); > > $('#aws-comprehend-form__form').find('button').prop('disabled', true); > $('#aws-comprehend-form__form').find('button').text('處理中...'); > $('#result-message').removeClass('alert-success alert-danger').addClass('alert-info').text('處理中...').show(); > > $.ajax({ > method: 'post', > url: '<?php echo esc_url(get_rest_url(null, 'comprehend-form/v1/send-api-to-aws')); ?>', > headers: {'X-WP-Nonce': nonce}, > data: form, > success: function (response) { > $('#aws-comprehend-form__form').find('button').prop('disabled', false); > $('#aws-comprehend-form__form').find('button').text('送出'); > $('#result-message').removeClass('alert-info alert-danger').addClass('alert-success').html(response.comprehend_output).show(); > }, > error: function (xhr, status, error) { > $('#aws-comprehend-form__form').find('button').prop('disabled', false); > $('#aws-comprehend-form__form').find('button').text('送出'); > $('#result-message').removeClass('alert-info alert-success').addClass('alert-danger').text('❌ 發生錯誤:' + error).show(); > console.log('AJAX Error:', error); > console.log('Response:', xhr.responseText); > } > }); > }); > })(jQuery); > </script> > <?php >} > >/** > * 註冊 REST API 路由 > */ >function comprehend_register_rest_api() >{ > register_rest_route('comprehend-form/v1', 'send-api-to-aws', array( > 'methods' => 'POST', > 'callback' => 'comprehend_handle_form', > 'permission_callback' => function () { > return true; // 可自行增加權限判斷 > } > )); >} > >/** > * REST API 處理函式 > */ >function comprehend_handle_form(WP_REST_Request $request) >{ > if (!wp_verify_nonce($request->get_header('x-wp-nonce'), 'wp_rest')) { > return new WP_REST_Response(['message' => '驗證失敗'], 422); > } > > $params = $request->get_params(); > $text = sanitize_text_field($params['text_to_be_analysed'] ?? ''); > $service = sanitize_text_field($params['comprehend_service'] ?? ''); > > if (empty($text)) { > return new WP_REST_Response(['message' => '請輸入文字'], 422); > } > > try { > $result = comprehend_call_aws($text, $service); > } catch (Exception $e) { > return new WP_REST_Response(['message' => 'AWS API錯誤: ' . $e->getMessage()], 500); > } > > return new WP_REST_Response([ > 'message' => '提交成功', > 'comprehend_service' => $service, > 'comprehend_output' => $result > ], 200); >} > >/** > * 呼叫 AWS Comprehend API > */ >function comprehend_call_aws($text, $service) >{ > $client = comprehend_get_aws_client(); > > if (!$client) { > throw new Exception('AWS 客戶端初始化失敗'); > } > > switch ($service) { > case 'Sentiment Analysis': > $result = $client->detectSentiment([ > 'LanguageCode' => 'zh-TW', > 'Text' => $text, > ]); > return comprehend_generate_sentiment_html([ > 'sentiment' => $result['Sentiment'], > 'scores' => $result['SentimentScore'], > ]); > > case 'Syntax Analysis': > $result = $client->detectSyntax([ > 'LanguageCode' => 'en', > 'Text' => $text, > ]); > return comprehend_generate_syntax_html(['tokens' => $result['SyntaxTokens']]); > > case 'Entity Analysis': > $result = $client->detectEntities([ > 'LanguageCode' => 'zh-TW', > 'Text' => $text, > ]); > return comprehend_generate_entity_html(['entities' => $result['Entities']]); > > default: > throw new Exception('未知的服務類型'); > } >} > >/** > * 初始化 AWS ComprehendClient > */ >function comprehend_get_aws_client() >{ > $region = 'us-west-2'; > $accessKeyId = 'AKIAWIA4HWSFHJJFRCCJ'; > $secretAccessKey = '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'; > > try { > $client = new ComprehendClient([ > 'region' => $region, > 'version' => 'latest', > 'credentials' => [ > 'key' => $accessKeyId, > 'secret' => $secretAccessKey, > ], > 'http' => [ > 'timeout' => 10, > 'connect_timeout' => 5, > ], > ]); > return $client; > } catch (Exception $e) { > error_log('AWS SDK 初始化錯誤: ' . $e->getMessage()); > return null; > } >} > >/** > * 產生情感分析結果 HTML > */ >function comprehend_generate_sentiment_html($result) >{ > $sentiment = esc_html($result['sentiment']); > $html = '<div class="sentiment-analysis-result">'; > $html .= '<h4>情感分析結果</h4>'; > $html .= '<p>情感分析結果是: <strong>' . $sentiment . '</strong></p>'; > > if (isset($result['scores'])) { > $html .= '<table class="table table-striped table-bordered">'; > $html .= '<thead><tr><th>情感類型</th><th>可信度</th></tr></thead>'; > $html .= '<tbody>'; > foreach ($result['scores'] as $type => $score) { > $html .= '<tr>'; > $html .= '<td>' . esc_html($type) . '</td>'; > $html .= '<td>' . round($score * 100, 1) . '%</td>'; > $html .= '</tr>'; > } > $html .= '</tbody></table>'; > } > $html .= '</div>'; > return $html; >} > >/** > * 產生句法分析結果 HTML > */ >function comprehend_generate_syntax_html($result) >{ > $tokens = $result['tokens']; > $html = '<div class="syntax-analysis-result">'; > $html .= '<h4>語法分析結果</h4>'; > > if (empty($tokens)) { > $html .= '<p>未檢測到任何語法成分。</p>'; > } else { > $html .= '<table class="table table-striped table-bordered">'; > $html .= '<thead><tr><th>詞語</th><th>詞性</th><th>可信度</th><th>起始位置</th><th>結束位置</th></tr></thead><tbody>'; > foreach ($tokens as $token) { > $text = esc_html($token['Text']); > $pos = esc_html($token['PartOfSpeech']['Tag']); > $score = isset($token['PartOfSpeech']['Score']) ? round($token['PartOfSpeech']['Score'] * 100, 1) . '%' : 'N/A'; > $begin = $token['BeginOffset']; > $end = $token['EndOffset']; > > $html .= '<tr>'; > $html .= "<td>$text</td><td>$pos</td><td>$score</td><td>$begin</td><td>$end</td>"; > $html .= '</tr>'; > } > $html .= '</tbody></table>'; > } > $html .= '</div>'; > return $html; >} > >/** > * 產生實體分析結果 HTML > */ >function comprehend_generate_entity_html($result) >{ > $entities = $result['entities']; > $html = '<div class="entity-analysis-result">'; > $html .= '<h4>實體分析結果</h4>'; > > if (empty($entities)) { > $html .= '<p>未檢測到任何實體。</p>'; > } else { > $html .= '<table class="table table-striped table-bordered">'; > $html .= '<thead><tr><th>實體</th><th>類型</th><th>可信度</th><th>開始位置</th><th>結束位置</th></tr></thead><tbody>'; > foreach ($entities as $entity) { > $text = esc_html($entity['Text']); > $type = esc_html($entity['Type']); > $score = round($entity['Score'] * 100, 1) . '%'; > $begin = $entity['BeginOffset']; > $end = $entity['EndOffset']; > > $html .= '<tr>'; > $html .= "<td>$text</td><td>$type</td><td>$score</td><td>$begin</td><td>$end</td>"; > $html .= '</tr>'; > } > $html .= '</tbody></table>'; > } > $html .= '</div>'; > return $html; >} >``` >::: #### short code 名稱 ``` [comprehend-form] ``` #### 在程式碼的開頭與結尾不能有任何符號,空白也不行,不然可能會出錯 #### 不會用的話,我們不會咬人,盡量問