使用Python的http.server實現一個簡易的Web Api對外提供HanLP拼音轉換服務

因爲採集省市區鎮數據須要對地名進行拼音轉換,因爲第三方高準確度接口對IP進行了限制,處理大量數據變得異常緩慢。html

使用了一個折中的辦法,省市區 3級(3千+)用高準確度接口(幾乎沒有拼錯的地名),鎮級(4萬+)用本地HanLP提供的接口(大部分多音字還算是能拼正確)。java

Github源碼:github.com/xiangyuecn/…node

另外我提供了一個臨時測試服務器,國內的但域名備案掉了走的海外線路,僅供測試,隨時可能關閉。測試:pinyin-test.haozgz.com/pinyin?txt=…python

HanLP是一個優秀的開源天然語言處理工具,提供了頗爲準確的拼音轉換功能。開始本想使用更爲順手的nodejs來處理,但測試了Github上排第一的hotoo/pinyin庫,就是開了分詞,對多音字支持也不太理想。最後決定使用HanLPPython版來進行轉換,可是這個庫對地名的支持仍是有限,字地名不少轉換成dou囧,不過在同類型裏面算是最好的。git

新手第一次正經寫Python代碼,剛開始電腦上之前裝的Python 2.7.x,就按Python2寫了一個服務,發現字符串編碼須要轉來轉去,夠折騰的,但好歹功能沒問題。後面由於這個編碼問題,越想越以爲不舒服,一個優秀的語言哪有這麼折騰的,就改爲了3.6.x(Miniconda裏面3.7.x環境SSL有問題,pip用不了,不肯折騰了)。最終結果就是僅支持Python3,沒有了奇異的代碼。github

http.server的簡單上手

建立服務

網上有不少例子,也都很是簡單,這個玩意也很容易上手。幾行代碼就能建立一個HTTP服務功能。json

from http.server import HTTPServer, BaseHTTPRequestHandler

class HttpHandler(BaseHTTPRequestHandler):
      def do_GET(self):
            #服務功能實現
      def do_POST(self):
            #服務功能實現

httpd = HTTPServer(('127.0.0.1', 9527), HttpHandler)
httpd.serve_forever()
複製代碼

nodejshttp模塊寫出來的服務同樣簡潔。只監聽127.0.0.1省的配置防火牆,只讓本機訪問。windows

接收請求參數

一個Web Api不單單有path,還要有query string x-www-form-urlencoded請求參數支持。urllib.parse.parse_qs能輕鬆解析出請求數據。瀏覽器

class HttpHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        path,args=urllib.parse.splitquery(self.path)
        self._response(path, args)

    def do_POST(self):
        args = self.rfile.read(int(self.headers['content-length'])).decode("utf-8")
        self._response(self.path, args)
        
   def _response(self, path, args):
        if args:
            args=urllib.parse.parse_qs(args).items()
            args=dict([(k,v[0]) for k,v in args])
        else:
            args={}
        # 輕鬆就解析出了請求參數對象
複製代碼

執行實際業務邏輯

有了pathargs,就直接能夠上業務邏輯了。具體這個請求是要處理什麼功能,根據path來判斷一下便可,功能須要的參數從args裏面取。服務器

整個拼音服務完整代碼

from pyhanlp import *

import traceback
import json
import urllib
from http.server import HTTPServer, BaseHTTPRequestHandler


class HttpHandler(BaseHTTPRequestHandler):
    def _response(self, path, args):
        code=200
        rtv={'c':0,'m':'','v':''}
        
        try:
            if args:
                args=urllib.parse.parse_qs(args).items()
                args=dict([(k,v[0]) for k,v in args])
            else:
                args={}
            # ****************************************
            # ***************頁面開始*****************
            # ****************************************
            
            # ==>
            if path=="/":
                rtv["v"]="服務器已準備好"
                
            # ==>
            elif path=="/pinyin":
                txt=args.get("txt","")
                pinyin_list = HanLP.convertToPinyinList(txt)
                list=[]
                Pinyin=JClass("com.hankcs.hanlp.dictionary.py.Pinyin")
                for i in range(pinyin_list.size()):
                    pinyin=pinyin_list[i]
                    if pinyin==Pinyin.none5:
                        list.append('F'+txt[i])
                    else:
                        list.append(pinyin.getPinyinWithoutTone())
                        
                rtv["v"]=list
                
                
            # ****************************************
            # ****************頁面結束****************
            # ****************************************
            else:
                code=404
                rtv["c"]=404
                rtv["m"]="路徑"+path+"不存在"
        except Exception as e:
            rtv["c"]=1
            rtv["m"]='服務器錯誤:'+str(e)+"\n"+traceback.format_exc()
            
        try:
            rtv=json.dumps(rtv,ensure_ascii=False)
        except Exception as e:
            rtv={'c':2,'m':'服務器返回數據錯誤:'+str(e)+"\n"+traceback.format_exc(),'v':''}
            rtv=json.dumps(rtv,ensure_ascii=False)
        
        self.send_response(code)
        self.send_header('Content-type', 'text/json; charset=utf-8')
        self.send_header('Access-Control-Allow-Origin', '*')
        self.end_headers()
        self.wfile.write(rtv.encode())
    
    def do_GET(self):
        path,args=urllib.parse.splitquery(self.path)
        self._response(path, args)

    def do_POST(self):
        args = self.rfile.read(int(self.headers['content-length'])).decode("utf-8")
        self._response(self.path, args)


httpd = HTTPServer(('127.0.0.1', 9527), HttpHandler)
httpd.serve_forever()
複製代碼

HanLP的安裝

因爲HanLP是一個java庫,所以使用了pyhanlp這個Python包,底層仍是用jpype1來調用HanLPjava接口。Windows上仍是根據wiki來完成的安裝,這庫給予了蠻實用的安裝方法。

本方法只在windows 7 環境下運行過,其餘環境自測。

:: 安裝一個有效的版本
> conda create -n python364 python=3.6.4
:: 切換版本
> activate python364
:: 安裝jpype1
> conda install -c conda-forge jpype1
:: 安裝pyhanlp
> pip install pyhanlp
:: 執行一遍,會提示要下載哪些東西
> hanlp

:: 環境都搞定後就能夠運行服務了
> python server.py
複製代碼

【1】安裝Miniconda

conda版本隨意,conda.io/miniconda.h…

###【2】安裝pyhanlp 參考:github.com/hankcs/pyha…

這個庫是java庫,須要有java環境,若是沒有裝過,須要先安裝java下載JDK

測試發現python3.7.1 windowsssl有問題沒法安裝,conda切換成python 3.6.4測試安裝正常

安裝好後運行一下hanlp命令,會提示下載,看第3步

若是出現XXX.dll什麼的問題,多是C++運行庫缺失,安裝微軟經常使用運行庫合集應該可以解決,我在Windows Server 2012上使用時出現此問題,裝上就OJBK了。

【3】下載字典和jar

參考半自動配置: github.com/hankcs/pyha…

字典和jar存放目錄通常在Miniconda3[\envs\py36]\Lib\site-packages\pyhanlp\static

jar直接下載最新releases

字典最好直接clone倉庫/data目錄最新版本(用svn下載速度快不少,無需model數據),同樣的在存儲目錄內放一個data文件夾,releases對bug處理稍微滯後一點。

另外須要修改hanlp.properties,給root賦值爲當前目錄完整路徑。

svn: https://github.com/hankcs/HanLP/trunk/data

【4】運行

python server.py

【5】瀏覽器訪問

http://127.0.0.1:9527/pinyin?txt=要拼的文字

好比: 拼音。m 返回結果 {c:0,m:"",v:["pin","yin","F。","Fm"]},c=0時表明正常,其餘表明出錯,m爲錯誤緣由,拼音若是是字母符號會用F打頭。

最後

Github源碼:github.com/xiangyuecn/…

若是這個庫有幫助到您,請 Star 一下。

相關文章
相關標籤/搜索