[從資料分析到資料產品]:DAY4 中文貼標及詞頻計算 Flask API實做-旅遊文章景點貼標

Hector Haung
7 min readMay 26, 2020

--

本章節實做的API範例是旅遊文章貼標系統,這邊會用到自然語言處理(NLP)的概念,使用python 的Jieba套件對旅遊文章做斷字斷詞,並以Flask 製作一隻以POST呼叫的API,讓使用者可以把台灣國內旅遊的文章做拆解,並回傳以下項目:

  1. 文章中包含的旅遊景點、縣市、區域標籤,以及出現的次數
  2. 文章中有意義的字詞標籤以及詞頻(term frequency)

有了這兩樣東西,就可以進行文章的貼標,而若未來想進一步進行文章相似性的計算或做推薦,詞頻也是很重要的文章前處理工程。總之,做一隻文章拆解及貼標API,可以讓後續其他相關的工作變得更容易也更彈性,本章節主要步驟可分為以下部分:

  1. 取得景點資料及製作自訂辭庫
  2. 資料前處理
  3. 撰寫app.py
  4. 在VM上部署API,並以POST的方式呼叫

第一步:取得景點資料及製作自訂辭庫

在這裡我們用python的Jieba套件來處理中文的斷字斷詞,Jieba本身有內建常用字詞的詞庫,但當要對特定領域的文章進行拆解時,由於各領域通常都會有不同的專有字詞,內建詞庫常常會拆不出這些字,所以在實際進行前,我們會需要找到一組旅遊景點字詞庫,用來補足內建詞庫的不足。本章節的拆字標的是景點,故會需要使用額外的景點詞庫來增加斷字斷詞的精準度,這裡選擇的是台灣觀光局的公開景點資料庫,直接提供數千筆台灣景點詳細資料的json檔,相當方便,我們可以直接讀取並將所有景點名稱另存成txt辭庫。

首先先匯入會使用到的套件:

import pandas as pd
import jieba
from flask import Flask, Response, request
import json
from collections import Counter
from zhon.hanzi import punctuation
import requests

接著抓取景點json檔,並存成txt辭庫檔:

# 抓取景點json檔
url='https://gis.taiwan.net.tw/XMLReleaseALL_public/scenic_spot_C_f.json'
data_json=requests.get(url).json()
# 把json檔轉成dataframe,並留下景點名稱、縣市、城鎮三個欄位
data=pd.DataFrame(data_json['XML_Head']['Infos']['Info'])
data=data[['Name','Region','Town']]
# 存成景點txt辭庫檔
f=open('poi.txt','w',encoding = 'utf8')
s1='\n'.join(list(data['Name']))
f.write(s1)
f.close()

第二步:資料前處理

我們希望最後的產出不只有斷詞結果,還希望從斷出來的詞中找出景點以及景點所屬的縣市跟城鎮,故這邊先產生比對用的景點列表、景點/縣市/城鎮對照字典,以便之後進行比對及對照:

# 產生比對用的景點列表
l=list(data['Name'])
# 產生景點/縣市對照dict
region_dict={}
for i, j in zip(list(data['Name']),
list(data['Region'])):
region_dict[i]=j

# 產生景點/城鎮對照dict
town_dict={}
for i, j in zip(list(data['Name']),
list(data['Town'])):
town_dict[i]=j

第三步:撰寫app.py

這邊會寫的比較長,詳細的步驟請見下面程式碼的註解,我這裡整理一下幾個重點:

  1. 拆解特定領域文字內容時,一定要製作並匯入自訂辭庫,否則專有名詞會拆不出來,在旅遊領域尤其如此。舉例來說,假如有個景點
  2. 要先刪除特殊字元,否則拆出來的keyword會有一大堆標點符號。
# 創一個flask的app
app = Flask(__name__)
@app.route("/tagging", methods=['POST'])# 設定api網址格式,設定為POST
def tagging():
# 載入剛剛做出來的景點詞庫
jieba.load_userdict('poi.txt')
# 讀取user端傳回來的json
data_json = request.get_json(force=True)

# 從讀回來的json取得要拆解的字串
sentence=(data_json['sentence'])

# 刪除特殊字元及標點符號
for ch in '!"#$%^&*()+_-,./:;<>?@[]{}\|~\,!?。"#$%&'\
()*+-/:\;<=>@[\]^_`{|}~⦅⦆「」、、〃》「」『』\
【】〔〕〖〗〘〙〚〛〜〝〞〟〰〾〿–—‘…‧﹏':
sentence=sentence.replace(ch,"")

# 用Jieba斷詞
words_list = jieba.lcut(sentence, cut_all=False, HMM=True)

# 把景點從斷詞的結果中挑出來
poi_list = [w for w in words_list if w in l]

# 找出景點所屬縣市
region_list= [ region_dict.get(item,item) for item in poi_list ]

# 找出景點所屬城鎮
town_list= [ town_dict.get(item,item) for item in poi_list ]

# 接著設定產出規格
output={}
for i,j in zip([poi_list,region_list,town_list, words_list],
['tag_poi','tag_region','tag_town','word']):
# 計算詞頻(每個詞、景點、縣市、城鎮)
item_add=list(Counter(i).items())
# 依詞頻由高至低排序
item_add.sort(key=lambda x:x[1],reverse=True)
# 加到產出json裡
output[j]=item_add
# 回傳成json格式
return Response(json.dumps(output), mimetype='application/json')

最後設定port=8080:

if __name__ == '__main__':
app.run(host='0.0.0.0', port=8080)

第四步:在VM上部署API,並以POST的方式呼叫

在VM上部署的方式請見[從資料分析到資料產品]:DAY 3 資料產品實做-Content Based Recommendation Flask API-相似NBA球員推薦,當然可以依自己的需求改port,避免共用的問題。

部屬完成後可以試著用Python以POST的方式,丟一串文章內容試著取看看資料:

import requests 
import json
#輸入自己的IP位置及PORT
url = 'http://xx.xxx.xx.xxx:8080/tagging'
#輸入文字內容
sentence='石梯坪很漂亮,但我更喜歡長虹橋,蛇頭山我沒去過,天台山這個周末我想去'
#組成json
my_data={'sentence':sentence}
#取得資料
r = requests.post(url,data=json.dumps(my_data)).json()
#印出來看看!
print (r)

現在我們就有一隻以POST呼叫、能對旅遊文章做斷字斷詞及計算詞頻的API了!

--

--