# 第四週 大數據分析實務&商業智慧與巨量資料分析 > ==**將先前爬出來的多篇類別新聞進行詞彙分析、建置輿情分析平台、從0開始建置Django網站**==[color=#EA0000] ###### tags: `大數據分析實務` `商業智慧與巨量資料分析` `碩士` `複習用` `高科大` >:::spoiler 文章目錄 >[TOC] >::: {%hackmd @chiaoshin369/bigdata_url_temple %} ## 第四週內容 > **時間:2025/3/11(二)、2025/3/13(四)** [color=#ffe260] ### 說明 1. 從0開始用Django製作輿情分析網站 2. 回家作業使用word檔,並截圖說明,最後匯出pdf檔 3. 將 `news_topkey_with_category_via_token_pos` 匯入 4. 執行 <font color="#EA0000">**Get hot keywords and draw chart**</font> 5. 建置Django專案,瞭解其MVC網站內容架構 ## 下載本週課程檔案 >[!Note] >選取 w04 所有檔案 download。 ![image](https://hackmd.io/_uploads/BJL6-_W2Jg.png) 如果 **無法使用** 或 **檔案連結已被老師變更**,請至 `Github倉庫` 下載 >[!Important] >[bigdata/2025/class4/drive-download-20250314T090347Z-001.zip at main · chiaoshin/bigdata](https://github.com/chiaoshin/bigdata/blob/main/2025/class4/drive-download-20250314T090347Z-001.zip) >![bigdata_download](https://hackmd.io/_uploads/SkkCsBa9Jx.png) ## 抓取熱門關鍵字,並繪製圖表 > Get hot keywords and draw chart ```python=1 import pandas as pd df_data = pd.read_csv('blow_news_topkey_with_category_via_token_pos.csv',sep=',') df_data.head(5) # 印出前4類(含全部類別) df_data['top_keys'][0] # 抓第1類 eval(df_data['top_keys'][0]) #將原先字串格式轉為list data={} # 字典 Dict for idx, row in df_data.iterrows(): # 逐列處理 data[row['category']]= eval(row['top_keys']) data # 印出全部字典 data['議題'] # 單獨抓 "議題" 類別資料 data['議題'][0:5] # 抓 "議題" 類別資料的前5筆 # 寫一個函式,處理成Django圖表可讀取格式 def get_category_topword(cate, topk=10): wf_pairs = data[cate][0:topk] words = [w for w, f in wf_pairs] freqs = [f for w, f in wf_pairs] chart_data = { "category": cate, "labels": words, "values": freqs} return chart_data, wf_pairs # 印出議題中前5筆資料(keyword) get_category_topword('議題',5) ``` :::info 從list、Array(陣列) 轉成 Dict(字典)格式。 變成 ==類別==、==關鍵字keyword==、==數量==的組合(key,value組合)。 ![image](https://hackmd.io/_uploads/r14u2u-n1x.png =100x200) 並 轉成 ==Django圖表== 可讀取的 Json格式。 ![image](https://hackmd.io/_uploads/ByUNhdWhke.png) ::: ### Django API ```python # POST: csrf_exempt should be used # 指定這一支程式忽略csrf驗證 from django.views.decorators.csrf import csrf_exempt @csrf_exempt def api_get_cate_topword(request): cate = request.POST.get('news_category') #cate = request.POST['news_category'] # this command is also works. topk = request.POST.get('topk') topk = int(topk) print(cate, topk) chart_data, wf_pairs = get_category_topword(cate, topk) response = {'chart_data': chart_data, 'wf_pairs': wf_pairs, } print(response) return JsonResponse(response) ``` ## 建置Django輿情分析平台 > 圖表使用 `Char.js` 繪製。 > 也可以使用其他,如:D3.js、Highcharts。[color=#ff2877] 利用Ajax將後端資料,渲染至前端。 ### Step By Step(初始化6步) 1. 建置資料夾 `website_news_analysis_v1` 2. 進入命令提示字元、終端機(cmd) 3. 執行conda環境 `conda activate ai25` 4. 建置專案配置資料夾 `website_configs` 5. 新增Django頁面 `app_top_keyword` 6. 執行網站(啟動) `python manage.py runserver 8000` >[!Important] >請先完成上方6個步驟,確認沒問題後,再執行下方步驟。 :::success ==配置頁面、網站設定== 1. `settings.py` 貼上修正 2. `urls.py` (website_configs總目錄 / app_top_keyword 頁面目錄) 3. 建立頁面資料夾 `templates`,再新增一層資料夾 `app_top_keyword` 放置 `home.html` 4. `views.py` 貼上修正 5. 在原專案頁面 `app_top_keyword` 資料集下,新增資料夾 `dataset`,並將處理好的csv資料集 `blow_news_topkey_with_category_via_token_pos.csv`放置於此 6. 修改成 **MVC架構**,將 原先的 `home.html` 拆成 `base.html` 跟 MVC渲染的 `home.html` 結構,並統一放置於 專案 `templates` 資料夾。 ::: #### 建置資料夾 ![image](https://hackmd.io/_uploads/BJqMHtZ3Jx.png) #### 右鍵 叫出 ==命令提示字元、終端機(cmd)== ![image](https://hackmd.io/_uploads/r1SGvFZ21g.png) :::danger 注意!!! 要進去資料夾,再叫出cmd,否則要先執行 `cd website_news_analysis_v1`,才會到達對的路徑(請留意為資料夾放置的路徑,不一定和圖片相同)。 ![image](https://hackmd.io/_uploads/SynXvYb2kx.png) ::: #### 執行conda環境 ```powershell=1 conda activate ai25 ``` ![image](https://hackmd.io/_uploads/Hy1RDtZhye.png) #### 建立 名為 website_config 的 ==專案配置資料夾== ```powershell=1 django-admin startproject website_configs . ``` 用來存放專案設定(配置)檔案的資料夾,名稱為 `website_configs` 通常,這類資料夾包含各種設定檔,如環境變數、伺服器配置、資料庫連線資訊等,讓專案能夠根據不同的需求進行調整。 ![image](https://hackmd.io/_uploads/rymxut-2Je.png) ![image](https://hackmd.io/_uploads/r16HdF-2Jl.png) #### 新增Django頁面 > Create an APP[color=#42cbed] ```powershell=1 django-admin startapp app_top_keyword ``` or ```powershell=1 python manage.py startapp app_top_keyword ``` 新增一個 Django頁面,名為 `app_top_keyword`。 ![image](https://hackmd.io/_uploads/H1kIYtW3kl.png) ![image](https://hackmd.io/_uploads/S1aIFFZ3Jx.png) #### 執行網站(啟動) ```cmd=1 python manage.py runserver 8000 ``` 確定顯示火箭,Django環境與專案則建置成功。 ![image](https://hackmd.io/_uploads/HkrjYYW2Je.png) ![image](https://hackmd.io/_uploads/SJHpFYW3kg.png) #### ==初始資料夾結構== 瞭解 > `website_news_analysis_v1` 專案資料夾 ![image](https://hackmd.io/_uploads/ryEvOyOnJe.png =300x150) > `website_configs` 專案總設定檔資料夾 ![image](https://hackmd.io/_uploads/rJ-KOJO3Jg.png =200x200) > `app_top_keyword` 單一頁面(app)資料夾 ![image](https://hackmd.io/_uploads/rkXcdyOhJx.png =200x200) >[!Tip] >瞭解 MVC框架架構。 >基本控制: `manage.py` >基本檔案: `views.py`、`urls.py`、`settings.py` >其餘資料夾與MVC架構: >dataset、templates、`base.html`、`home.html` #### 執行 Hello world 頁面 > **==views.py==**[color=#42cbed] ```python=1 from django.shortcuts import render from django.http import HttpResponse def hello( request ): return HttpResponse('Hello World!') # or 二選一 def hello( request ): return HttpResponse('<h2>Hello World!</h2>') ``` > **==urls.py==**[color=#42cbed] ```python=1 from django.contrib import admin from django.urls import path from app_hello import views urlpatterns = [ path('admin/', admin.site.urls), path('hello/', views.hello), ] ``` #### 執行並創建Django各類新聞熱門關鍵詞網站 > **Top Keywords Display and Charting** ![image](https://hackmd.io/_uploads/HJJEiy_n1l.png =500x400) ##### 完整專案資料夾結構 ![image](https://hackmd.io/_uploads/SJMnCuO21g.png =250x400) ##### 開始建立 STEP by STEP (HW回家作業) ![image](https://hackmd.io/_uploads/SJIbPO_2Jx.png) 將 `settings.py` 進行處理,修改 `ALLOWED_HOSTS`、`INSTALLED_APPS`、`TEMPLATES`。 > **==settings.py==**[color=#42cbed] ```python=1 from pathlib import Path import os # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = 'django-insecure-1j=m)#r))l4hz(gqvc65$@v8my(#!fijflbl@#ii61gq+xzvy!' # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True # 修改1 ALLOWED_HOSTS = [] # or '*', 'localhost', '127.0.0.1',"your public ip" # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent # Application definition # 修改2 INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'app_top_keyword', ] MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] ROOT_URLCONF = 'website_configs.urls' # 修改3 TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'templates')], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] WSGI_APPLICATION = 'website_configs.wsgi.application' # Database # https://docs.djangoproject.com/en/5.1/ref/settings/#databases DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': BASE_DIR / 'db.sqlite3', } } # Password validation # https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators AUTH_PASSWORD_VALIDATORS = [ { 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', }, { 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', }, { 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', }, { 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', }, ] # Internationalization # https://docs.djangoproject.com/en/5.1/topics/i18n/ LANGUAGE_CODE = 'en-us' TIME_ZONE = 'UTC' USE_I18N = True USE_TZ = True # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/5.1/howto/static-files/ STATIC_URL = 'static/' # Default primary key field type # https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' ``` 新建一個頁面的路徑(create app router)。 > **==`app_top_keyword` urls.py==** ```python=1 from django.urls import path from app_top_keyword import views # Declare a namespace for this APP app_name = 'app_top_keyword' urlpatterns = [ # For home path('', views.home, name='home'), # For Ajax (當csv資料集新增進去,Ajax呼叫,需要指引路徑) # 留意指定 home.html的ajax url,至 views.py的api_get_cate_topword方法 path('api_get_cate_topword/', views.api_get_cate_topword), ] ``` 建立一個網站的總路徑(總目錄),並引入前面 **各類新聞熱門關鍵詞(app)** 頁面 的 `urls`。 > **==`website_configs` urls.py==**[color=#42cbed] ```python=1 from xml.etree.ElementInclude import include from django.contrib import admin from django.urls import path,include urlpatterns = [ # path('admin/', admin.site.urls), # top keywords path('topword/', include('app_top_keyword.urls')), ] ``` 利用 `views.py` 將前端畫面進行渲染。 並且在頁面 `app_top_keyword` 建立資料夾 `dataset`,放置先前已處理好的資料集 `blow_news_topkey_with_category_via_token_pos.csv` ![image](https://hackmd.io/_uploads/B1S-X_OnJl.png) :::danger 注意!!! 放入csv資料集後,請重新執行,並載入畫面。 `python manage.py runserver 8000` ::: > **==views.py==**[color=#42cbed] ```python=1 from django.shortcuts import render from django.http import JsonResponse import pandas as pd # render渲染網頁 def home(request): return render(request, 'app_top_keyword/home.html') # read df 讀取csv資料集 df_topkey = pd.read_csv('app_top_keyword/dataset/blow_news_topkey_with_category_via_token_pos.csv', sep=',') # prepare data 預處理 - 清洗資料 data={} for idx, row in df_topkey.iterrows(): data[row['category']] = eval(row['top_keys']) # We don't use it anymore, so delete it to save memory. del df_topkey # POST: csrf_exempt should be used # 指定這一支程式忽略csrf驗證 from django.views.decorators.csrf import csrf_exempt @csrf_exempt def api_get_cate_topword(request): cate = request.POST.get('news_category') #cate = request.GET['news_category'] # this command also works. topk = request.POST.get('topk') topk = int(topk) print(cate, topk) chart_data, wf_pairs = get_category_topword(cate, topk) response = {'chart_data': chart_data, 'wf_pairs': wf_pairs, } print(response) return JsonResponse(response) def get_category_topword(cate, topk=10): wf_pairs = data[cate][0:topk] words = [w for w, f in wf_pairs] freqs = [f for w, f in wf_pairs] chart_data = { "category": cate, "labels": words, "values": freqs} return chart_data, wf_pairs print("app_top_keywords--類別熱門關鍵字載入成功!") ``` 在 `app_top_keyword` 資料夾內,新增一個資料夾 `templates`,在 `templates` 資料夾下,繼續新增一個資料夾 `app_top_keyword`,並將 `home.html` 放進去。 ![image](https://hackmd.io/_uploads/Syo1cwd31g.png) >[!Tip] >資料夾名稱,建議用 **複製**,不要用打的,容易因為一個空白,或者字不一樣,導致路徑錯誤。 再來複製 `home.html`,即可看到即時渲染畫面。 :::info ==Django Template Language (DTL)== {% url 'app_top_keyword:home' %} 定義在 `website_configs` 裡 `urls.py` path(總目錄) 中的 name 類似於 `Jinja` ::: > **==home.html==**[color=#42cbed] ```html=1 <!DOCTYPE html> <html lang="zh-TW"> <head> <title>輿情分析平台</title> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <!-- Bootstrap 5 CSS --> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" /> </head> <body> <div class="container"> <div class="row"> <!-- Navigation Bar --> <div class="col-lg-12 mb-2"> <nav class="navbar navbar-expand-lg navbar-light" style="background-color: #e3f2fd"> <div class="container-fluid"> <a class="navbar-brand" href="#">輿情大數據</a> <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button> <div class="collapse navbar-collapse" id="navbarSupportedContent"> <ul class="navbar-nav me-auto mb-2 mb-lg-0"> <li class="nav-item"> <a class="nav-link" href="#">政治人物聲量排行榜</a> </li> <li class="nav-item"> <a class="nav-link" href="#">政黨聲量排行榜</a> </li> <li class="nav-item"> <a class="nav-link" href="{% url 'app_top_keyword:home' %}">熱門關鍵詞分析</a> </li> <li class="nav-item"> <a class="nav-link" href="#">昨日熱門關鍵字</a> </li> <li class="nav-item"> <a class="nav-link" href="#">熱門人物排行分析</a> </li> <li class="nav-item"> <a class="nav-link" href="#">NER熱門分析</a> </li> <li class="nav-item"> <a class="nav-link" href="#">你的關鍵詞熱門度分析</a> </li> <li class="nav-item"> <a class="nav-link" href="#">全文檢索與關聯新聞分析</a> </li> <li class="nav-item"> <a class="nav-link" href="#">你的關鍵詞情緒分析</a> </li> <li class="nav-item"> <a class="nav-link" href="#">今日新聞瀏覽與新聞推薦</a> </li> <li class="nav-item"> <a class="nav-link" href="#">新聞或文章情緒分類</a> </li> <li class="nav-item"> <a class="nav-link" href="#">新聞分類新聞分類</a> </li> <li class="nav-item"> <a class="nav-link" href="#">課程介紹網頁</a> </li> <li class="nav-item dropdown"> <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">會員獨享功能</a> <ul class="dropdown-menu" aria-labelledby="navbarDropdown"> <li> <a class="dropdown-item" href="#">長時間(超過3個月)監控分析(收費標準)</a> </li> <li> <a class="dropdown-item" href="#">自訂分析功能(收費標準)</a> </li> <li> <a class="dropdown-item" href="#">更多資料庫分析(收費標準)</a> </li> <li> <hr class="dropdown-divider" /> </li> <li> <a class="dropdown-item" href="#">為你訂製輿情分析任務(報價)</a> </li> </ul> </li> </ul> </div> </div> </nav> </div> <!-- End of Navigation Bar --> <!-- Main Content --> <div class="col-lg-12"> <h1>各類新聞最熱門的關鍵詞</h1> <p>熱門度分析:可以了解新聞關注那些重要的東東</p> </div> <!-- 新聞類別選單--- --> <div class="col-lg-6 mb-2"> <div class="card"> <div class="card-header"> <h3 class="h6 text-uppercase mb-0">熱門關鍵字瀏覽與繪圖(資料週期:資料截止時間的前4周)</h3> </div> <div class="card-body"> <!-- 新聞類別選單 form group --> <div class="mb-3 row"> <label class="col-sm-3 form-label">新聞類別</label> <div class="col-md-9"> <select id="cate-selected" name="news_category" class="form-select"> <!-- <option>請選擇</option> --> <option>全部</option> <option>人物</option> <option>議題</option> <option>新聞</option> <option>雜吹</option> </select> <div class="form-text">請選擇新聞類別</div> </div> </div> <!-- form group --> <!-- 熱門詞多少個?form group --> <div class="mb-3 row"> <label class="col-md-3 form-label">多少個熱門詞?</label> <div class="col-md-9"> <input id="topk-selected" name="topk" value="10" class="form-control" /> <div class="form-text">內定值為10</div> </div> </div> <!-- form group --> <!-- submit按鈕form group --> <div class="mb-3 row"> <div class="col-md-9 ms-auto"> <button type="button" id="btn-ok" class="btn btn-primary">查詢</button> </div> </div> <!-- form group --> </div> <!-- card body --> </div> <!-- column --> </div> <!-- 區塊結束 --> <!-- 繪圖區塊--- --> <div class="col-lg-6 mb-5"> <div class="card"> <div class="card-header"> <h3 class="h6 text-uppercase mb-0">熱門關鍵字繪圖</h3> </div> <div class="card-body"> <canvas id="mychart"></canvas> </div> </div> </div> <!-- 區塊結束 --> <!-- 熱門關鍵字區塊--- --> <div class="col-lg-6 mb-5"> <div class="card"> <div class="card-header"> <h3 class="h6 text-uppercase mb-0">熱門關鍵字</h3> </div> <div class="card-body"> <ul id="topkeys"></ul> </div> </div> </div> <!-- 區塊結束 --> </div> </div> <!-- container --> <!-- Bootstrap 5 JS Bundle with Popper --> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> <!-- Additional Scripts --> <!-- chartjs圖js --> <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.3/Chart.min.js"></script> <!-- 程式碼區 --> <script> // Show default top words bar chart when the page is loaded. // Invoke callAjax() after the function is defined, unless callAjax() is defined with "hoisting" callAjax() //*新聞類別選單select被選中值有改變時,執行以下事件 //$('#cate-selected').on('change', callAjax() ); $('#cate-selected').on('change', function () { callAjax() }) //event function //**按鈕事件 $('#btn-ok').on('click', function () { callAjax() }) //event function // ** draw chart using Ajax 畫圖 // There are two ways to define a function: // one is expression, and the other is definition with "hoisting" // function with hoisting: function callAjax() {} // normal function expression: let callAjax = function() {} // Define callAjax function with hoisting // callAjax()這樣定義可以在被定義前就被使用 跟我們在Java裡面的函數用法一樣! //let callAjax = function() { function callAjax() { let cate = $('#cate-selected').val() //console.log(cate); // $ 可改成 jQuery let topk = $('#topk-selected').val() //console.log(topk); $.ajax({ type: 'POST', //url: "/topword/api_get_cate_topword/", url: 'http://127.0.0.1:8000/topword/api_get_cate_topword/', //url: "http://163.18.23.21:8000/topword/api_get_cate_topword/", //url: "api_get_cate_topword/", //Not recommended! data: { news_category: cate, topk: topk }, success: function (received) { //console.log(received); let chart_data = received.chart_data let wf_pairs = received.wf_pairs console.log(wf_pairs) showTopKeys(wf_pairs) showChart(chart_data) } //success function }) //ajax } //callAjax //* 顯示關鍵詞資料函數 function showTopKeys(items) { //先清除前一次的資料 $('#topkeys').empty() //將內容加上li標籤附加起來,顯示在顯示區"topkeys" for (let i = 0; i < items.length; i++) { let item_li = '<li>' + items[i] + '</li>' $('#topkeys').append(item_li) } } //function //**繪圖函數showChart() function showChart(chart_data) { // 畫圖需要的數據資料 let values = chart_data.values let labels = chart_data.labels let category = chart_data.category //第1個變數: 餵給chart的資料 let data = { labels: labels, datasets: [ { label: category, data: values, backgroundColor: randomColors(values.length), borderColor: randomColors(values.length), borderWidth: 1 } ] } //第2個變數: chart的選項 指定y坐標軸從零開始顯示 let options = { scales: { y: { beginAtZero: true } } } //取得在前面html區域欲顯示的圖代號 let canvas_mychrat = document.getElementById('mychart') //**先清除前一個圖 再繪新圖 // 可以印出barchart物件是否存在 // console.log(window.barchart); //先清除前一個圖 再繪新圖 if 有以下兩種寫法皆可 // if (window.barchart) //若存在則為true // if (typeof (barchart) != "undefined"){ if (window.barchart) { barchart.destroy() } //**繪圖(產生一個圖物件變數名稱為barchart) // 必須全域變數--注意:前面不要有let, var, const等修飾詞 // 理由: 我們要讓它存在於網頁全域變數, // 這樣我們才方便判斷是否有前一次的圖,如果存在有,要刪除之,否則,很多張圖會疊在一起 barchart = new Chart(canvas_mychrat, { type: 'bar', data: data, options: options }) //** 產生隨機顏色 function randomColors(num_colors) { let colors = [] for (i = 0; i < num_colors; i++) { let r = Math.floor(Math.random() * 255) let g = Math.floor(Math.random() * 255) let b = Math.floor(Math.random() * 255) let rgb = `rgba(${r},${g},${b},0.5)` // (red, green, blue, alfa) alfa透明度 colors.push(rgb) } return colors } } //show chart function // document就是這個網頁HTML所有的元素 // window就是這個網頁的全域變數global variables:有一大堆,我們自己定義的有callAjax, showChart, barchart等 // 把document, window印出來看看就能理解它們是甚麼 //console.log(document); //console.log(window); </script> </body> </html> ``` 上方為所有檔案的呈現(html+css+js+api_dataset)。 但不可能每一頁都要撰寫重複的meun bar (header),所以要把它拆開來撰寫,同時把檔案結構分乾淨、清楚,才會符合框架的 **MVC架構**。 在 `website_news_analysis_v1` 資料夾內,新增一個資料夾 `templates`,並將 `base.html` 放進去。 >[!Tip] >資料夾名稱,建議用 **複製**,不要用打的,容易因為一個空白,或者字不一樣,導致路徑錯誤。 > >他是吃 `settings.py` 的 `TEMPLATES` >`'DIRS': [os.path.join(BASE_DIR, 'templates')],` 去讀取 > **==base.html==**[color=#42cbed] ```html=1 <!DOCTYPE html> {% load static %} <html lang="zh-TW"> <head> <title> {% block title %} 輿情分析平台 {% endblock %} </title> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <!-- Bootstrap 5 CSS --> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" /> {% block extra_css %} {% endblock %} </head> <body> <div class="container"> <div class="row"> <!-- Navigation Bar --> <div class="col-lg-12 mb-2 mt-2"> <nav class="navbar navbar-expand-lg navbar-light" style="background-color: #e3f2fd"> <div class="container-fluid"> <a class="navbar-brand" href="/">輿情大數據</a> <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button> <div class="collapse navbar-collapse" id="navbarSupportedContent"> <ul class="navbar-nav me-auto mb-2 mb-lg-0"> <!-- 基礎熱門分析 --> <div class="btn-group"> <button type="button" class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">熱門分析</button> <div class="dropdown-menu"> <a class="dropdown-item" href="{% url 'app_top_keyword:home' %}">熱門關鍵詞分析</a> <a class="dropdown-item" href="#">熱門人物排行分析</a> <a class="dropdown-item" href="#">命名實體熱門分析</a> <a class="dropdown-item" href="#" style="color: green">昨日誰最大</a> </div> </div> <!-- 進階自訂分析 --> <div class="btn-group"> <button type="button" class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">進階查詢</button> <div class="dropdown-menu"> <a class="dropdown-item" href="#">自訂關鍵詞熱門度分析</a> <a class="dropdown-item" href="#">自訂全文檢索與關聯分析</a> <a class="dropdown-item" href="#">自訂關鍵詞之情緒分析</a> </div> </div> <!-- 特色分析 --> <div class="btn-group"> <button type="button" class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">特色分析</button> <div class="dropdown-menu"> <a class="nav-link" href="#" style="color: green">時中聲量我最大</a> <div class="dropdown-divider"></div> <a class="nav-link" href="#" style="color: green">台北市長選舉聲量觀測站</a> <div class="dropdown-divider"></div> <a class="nav-link" href="#">政黨聲量排行榜</a> <a class="nav-link" href="#">政治人物聲量排行榜</a> <div class="dropdown-divider"></div> <a class="nav-link" href="#" style="color: green">事件人物聲量相關分析</a> </div> </div> <!-- 新聞推薦系統 --> <div class="btn-group"> <button type="button" class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">新聞推薦系統</button> <div class="dropdown-menu"> <a class="dropdown-item" href="#">Bert/Qwen新聞推薦-新聞查找相似新聞</a> </div> </div> <!-- NLP應用 --> <div class="btn-group"> <button type="button" class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">自然語言理解</button> <div class="dropdown-menu"> <a class="dropdown-item" href="#">語言模型Bert/Qwen情緒分類</a> <a class="dropdown-item" href="#">語言模型Bert/Qwen新聞分類</a> </div> </div> <!-- 使用資料庫 --> <div class="btn-group"> <button type="button" class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">使用DB</button> <div class="dropdown-menu"> <a class="dropdown-item" href="#">熱門人物資料庫查詢</a> <a class="dropdown-item" href="#">昨日新聞資料庫全文檢索</a> </div> </div> <!-- 其他特色應用 --> <div class="btn-group"> <button type="button" class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">特色應用</button> <div class="dropdown-menu"> <a class="dropdown-item" href="#">高雄市市長選舉人氣分析</a> <a class="dropdown-item" href="#">珊珊市長人氣</a> <a class="dropdown-item" href="#">阿邁市長人氣</a> <div class="dropdown-divider"></div> <a class="dropdown-item" href="#">疫情發燒監視</a> <a class="dropdown-item" href="#">蘋果發燒站</a> <div class="dropdown-divider"></div> <a class="dropdown-item" href="#">人氣美食排行榜</a> <a class="dropdown-item" href="#">財經議題排行榜</a> <a class="dropdown-item" href="#">股市新聞監視站</a> <a class="dropdown-item" href="#">科技新聞熱門議題分析</a> <a class="dropdown-item" href="#">人力銀行職缺大熱門</a> <div class="dropdown-divider"></div> <a class="dropdown-item" href="#">批踢踢酸民大吐槽</a> <a class="dropdown-item" href="#">批踢踢鄉民來八卦</a> <a class="dropdown-item" href="#">滴卡大學生關心議題</a> <div class="dropdown-divider"></div> <a class="dropdown-item" href="#">新聞媒體政黨傾向調查分析</a> <a class="dropdown-item" href="#">自訂競爭大PK</a> </div> </div> <!-- 會員訂閱 --> <div class="btn-group"> <button type="button" class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">付費訂閱</button> <div class="dropdown-menu"> <a class="dropdown-item" href="#">長時間(超過3個月)監測分析(收費表)</a> <a class="dropdown-item" href="#">更多資料庫分析(收費表)</a> <a class="dropdown-item" href="#">為你訂製輿情分析任務(報價)</a> </div> </div> <!-- 關於 --> <div class="btn-group"> <button type="button" class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">關於</button> <div class="dropdown-menu"> <a class="dropdown-item" href="#">課程介紹網頁</a> <a class="dropdown-item" href="#">如何使用輿情API</a> </div> </div> </ul> </div> </div> </nav> </div> <!-- End of Navigation Bar --> <!-- Main Content --> {% block content %} {% endblock %} </div> </div> <!-- Bootstrap 5 JS Bundle with Popper --> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> <!-- Additional Scripts --> {% block extra_js %} {% endblock %} </body> </html> ``` 將舊的 `home.html` 刪除,並重新貼上,修正成 **MVC架構** 的html模板。 > **==home.html==**[color=#42cbed] ```python=1 {% extends 'base.html' %} {% block title %} 輿情分析平台 - 熱門關鍵詞分析 {% endblock %} {% block content %} <div class="col-lg-12"> <h1>各類新聞最熱門的關鍵詞</h1> <p>熱門度分析:可以了解新聞關注那些重要的東東</p> </div> <!-- 新聞類別選單 --> <div class="col-lg-6 mb-2"> <div class="card"> <div class="card-header"> <h3 class="h6 text-uppercase mb-0">熱門關鍵字瀏覽與繪圖(資料週期:資料截止時間的前4周)</h3> </div> <div class="card-body"> <!-- 新聞類別選單 form group --> <div class="mb-3 row"> <label class="col-sm-3 form-label">新聞類別</label> <div class="col-md-9"> <select id="cate-selected" name="news_category" class="form-select"> <!-- <option>請選擇</option> --> <option>全部</option> <option>人物</option> <option>議題</option> <option>新聞</option> <option>雜吹</option> </select> <div class="form-text">請選擇新聞類別</div> </div> </div> <!-- form group --> <!-- 熱門詞多少個?form group --> <div class="mb-3 row"> <label class="col-md-3 form-label">多少個熱門詞?</label> <div class="col-md-9"> <input id="topk-selected" name="topk" value="10" class="form-control" /> <div class="form-text">內定值為10</div> </div> </div> <!-- form group --> <!-- submit按鈕form group --> <div class="mb-3 row"> <div class="col-md-9 ms-auto"> <button type="button" id="btn-ok" class="btn btn-primary">查詢</button> </div> </div> <!-- form group --> </div> <!-- card body --> </div> <!-- column --> </div> <!-- 區塊結束 --> <!-- 繪圖區塊--- --> <div class="col-lg-6 mb-5"> <div class="card"> <div class="card-header"> <h3 class="h6 text-uppercase mb-0">熱門關鍵字繪圖</h3> </div> <div class="card-body"> <canvas id="mychart"></canvas> </div> </div> </div> <!-- 區塊結束 --> <!-- 熱門關鍵字區塊--- --> <div class="col-lg-6 mb-5"> <div class="card"> <div class="card-header"> <h3 class="h6 text-uppercase mb-0">熱門關鍵字</h3> </div> <div class="card-body"> <ul id="topkeys"></ul> </div> </div> </div> <!-- 區塊結束 --> {% endblock %} {% block extra_js %} <!-- chartjs圖js --> <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.3/Chart.min.js"></script> <!-- 程式碼區 --> <script> // Show default top words bar chart when the page is loaded. // Invoke callAjax() after the function is defined, unless callAjax() is defined with "hoisting" callAjax() //*新聞類別選單select被選中值有改變時,執行以下事件 //$('#cate-selected').on('change', callAjax() ); $('#cate-selected').on('change', function () { callAjax() }) //event function //**按鈕事件 $('#btn-ok').on('click', function () { callAjax() }) //event function // ** draw chart using Ajax 畫圖 // There are two ways to define a function: // one is expression, and the other is definition with "hoisting" // function with hoisting: function callAjax() {} // normal function expression: let callAjax = function() {} // Define callAjax function with hoisting // callAjax()這樣定義可以在被定義前就被使用 跟我們在Java裡面的函數用法一樣! //let callAjax = function() { function callAjax() { let cate = $('#cate-selected').val() //console.log(cate); let topk = $('#topk-selected').val() //console.log(topk); $.ajax({ type: 'POST', //url: "/topword/api_get_cate_topword/", url: 'http://127.0.0.1:8000/topword/api_get_cate_topword/', //url: "http://163.18.23.21:8000/topword/api_get_cate_topword/", //url: "api_get_cate_topword/", //Not recommended! data: { news_category: cate, topk: topk }, success: function (received) { //console.log(received); let chart_data = received.chart_data let wf_pairs = received.wf_pairs console.log(wf_pairs) showTopKeys(wf_pairs) showChart(chart_data) } //success function }) //ajax } //callAjax //* 顯示關鍵詞資料函數 function showTopKeys(items) { //先清除前一次的資料 $('#topkeys').empty() //將內容加上li標籤附加起來,顯示在顯示區"topkeys" for (let i = 0; i < items.length; i++) { let item_li = '<li>' + items[i] + '</li>' $('#topkeys').append(item_li) } } //function //**繪圖函數showChart() function showChart(chart_data) { // 畫圖需要的數據資料 let values = chart_data.values let labels = chart_data.labels let category = chart_data.category //第1個變數: 餵給chart的資料 let data = { labels: labels, datasets: [ { label: category, data: values, backgroundColor: randomColors(values.length), borderColor: randomColors(values.length), borderWidth: 1 } ] } //第2個變數: chart的選項 指定y坐標軸從零開始顯示 let options = { scales: { yAxes: [ { ticks: { beginAtZero: true } } ] } } //取得在前面html區域欲顯示的圖代號 let canvas_mychrat = document.getElementById('mychart') //**先清除前一個圖 再繪新圖 // 可以印出barchart物件是否存在 // console.log(window.barchart); //先清除前一個圖 再繪新圖 if 有以下兩種寫法皆可 // if (window.barchart) //若存在則為true // if (typeof (barchart) != "undefined"){ if (window.barchart) { barchart.destroy() } //**繪圖(產生一個圖物件變數名稱為barchart) // 必須全域變數--注意:前面不要有let, var, const等修飾詞 // 理由: 我們要讓它存在於網頁全域變數, // 這樣我們才方便判斷是否有前一次的圖,如果存在有,要刪除之,否則,很多張圖會疊在一起 barchart = new Chart(canvas_mychrat, { type: 'bar', data: data, options: options }) //** 產生隨機顏色 function randomColors(num_colors) { let colors = [] for (i = 0; i < num_colors; i++) { let r = Math.floor(Math.random() * 255) let g = Math.floor(Math.random() * 255) let b = Math.floor(Math.random() * 255) let rgb = `rgba(${r},${g},${b},0.5)` // (red, green, blue, alfa) alfa透明度 colors.push(rgb) } return colors } } //show chart function // document就是這個網頁HTML所有的元素 // window就是這個網頁的全域變數global variables:有一大堆,我們自己定義的有callAjax, showChart, barchart等 // 把document, window印出來看看就能理解它們是甚麼 //console.log(document); //console.log(window); </script> {% endblock %} ``` ##### 最後結果呈現 ![image](https://hackmd.io/_uploads/BJZ9aOO2Jx.png) ![image](https://hackmd.io/_uploads/rJVhTudhJx.png) ![image](https://hackmd.io/_uploads/B1fTTduh1g.png) ![image](https://hackmd.io/_uploads/SyRp6dOhyg.png) ![image](https://hackmd.io/_uploads/Sk41Rdunke.png) --- :::spoiler 最後更新日期 >==第一版==[time=2025 3 20 , 1:58 AM][color=#786ff7] >第二版[time=2025 3 27 , 11:24 PM][color=#ce770c] <!-- >第三版[time=2025 3 24 , 3:20 PM][color=#ce770c] --> >**最後版[time=2025 3 27 , 11:24 PM]**[color=#EA0000] :::