使用Python的http.server实现一个简易的Web Api对外提供HanLP拼音转换服务

由于采集省市区镇数据需要对地名进行拼音转换,由于第三方高准确度接口对IP进行了限制,处理大量数据变得异常缓慢。

使用了一个折中的办法,省市区 3级(3千+)用高准确度接口(几乎没有拼错的地名),镇级(4万+)用本地HanLP提供的接口(大部分多音字还算是能拼正确)。

Github源码:https://github.com/xiangyuecn/AreaCity-JsSpider-StatsGov/tree/master/assets/pinyin-python-server

另外我提供了一个临时测试服务器,国内的但域名备案掉了走的海外线路,仅供测试,随时可能关闭。测试:http://pinyin-test.haozgz.com/pinyin?txt=重庆着陆OK,重力看着没有异常

HanLP是一个优秀的开源自然语言处理工具,提供了颇为准确的拼音转换功能。开始本想使用更为顺手的nodejs来处理,但测试了Github上排第一的hotoo/pinyin库,就是开了分词,对多音字支持也不太理想。最后决定使用HanLPPython版来进行转换,但是这个库对地名的支持还是有限,字地名很多转换成dou囧,不过在同类型里面算是最好的。

新手第一次正经写Python代码,刚开始电脑上以前装的Python 2.7.x,就按Python2写了一个服务,发现字符串编码需要转来转去,够折腾的,但好歹功能没问题。后面因为这个编码问题,越想越觉得不舒服,一个优秀的语言哪有这么折腾的,就改成了3.6.x(Miniconda里面3.7.x环境SSL有问题,pip用不了,不愿折腾了)。最终结果就是仅支持Python3,没有了奇异的代码。

http.server的简单上手

创建服务

网上有很多例子,也都非常简单,这个玩意也很容易上手。几行代码就能创建一个HTTP服务功能。

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省的配置防火墙,只让本机访问。

接收请求参数

一个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版本随意,https://conda.io/miniconda.html

【2】安装pyhanlp

参考:https://github.com/hankcs/pyhanlp/wiki/Windows

这个库是java库,需要有java环境,如果没有装过,需要先安装java下载JDK

测试发现python3.7.1 windowsssl有问题无法安装,conda切换成python 3.6.4测试安装正常

安装好后运行一下hanlp命令,会提示下载,看第3步

如果出现XXX.dll什么的问题,可能是C++运行库缺失,安装微软常用运行库合集应该能够解决,我在Windows Server 2012上使用时出现此问题,装上就OJBK了。

【3】下载字典和jar

参考半自动配置: https://github.com/hankcs/pyhanlp/wiki/%E6%89%8B%E5%8A%A8%E9%85%8D%E7%BD%AE

字典和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源码:https://github.com/xiangyuecn/AreaCity-JsSpider-StatsGov/tree/master/assets/pinyin-python-server

如果这个库有帮助到您,请 Star 一下。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容