用正则表达式re+词典等规则进行文本信息提取

文本信息提取方法有很多,最简单直接暴力的就是直接使用规则,利用python re模块的强大功能,已经可以满足大部分需求。而后可再逐步深入,利用HMM 或 CRF 模型,提高准确率和召回率

先上demo:

 

 

#!/usr/bin/env python
# coding=utf-8

import jieba,sys,re
from datetime import datetime,timedelta
import cPickle
from collections import OrderedDict,defaultdict
from pprint import pprint
from copy import deepcopy
reload(sys)
sys.setdefaultencoding('utf-8')


class JdParser(object):

    def __init__(self):
        self.degreedic = [line.strip() for line in open('./data/degrees.txt')] # 载入学历词库
        self.majordic = [line.strip() for line in open('./data/majordic.txt')] # 载入专业词库
        self.citydic = [line.strip() for line in open("./data/citydic.txt")]   # 载入城市词库
        self.firmnames = [line.strip() for line in open('./data/firm.txt')]    # 载入公司缩写名库
        self.jobdic = [line.strip() for line in open('./data/jobposition.txt')]# 载入招聘职位名库
        self.skills = [ line.strip() for line in open('./data/skills.txt') ]
        # self.w2vdict = pickle.load(open('./data/word2vec_dict.pkl'))
        # self.stoplist = [line.strip() for line in open('./data/full_stopwords.txt')]
        
        self.SEX = re.compile(u"性别不限|性别|要求|条件|资格")
        self.AGE = re.compile(u"\d+周?岁|年龄")
        self.DEGREE = re.compile(u"(全日制)?(初中|高中|中专|大专|专科|大学专科|中职|本科|大学本科|硕士|研究生|博士|博士后)(.?以上)?")
        self.MAJOR = re.compile(u"相关专业|专业")
        self.EXP = re.compile(u"工作经验|工作年限|工作经历|项目经[历验]|经[历验]|\d年经[历验]|\d+年")
        self.PUB_TIME = re.compile(u"(\d+)(天前发布)")
        
        self.INCNAME = re.compile(u"\S+(有限公司|企业|酒店|银行|集团|厂|研究中心|研究所|学校|旅行社|中心|公司|研发部?|技术部|招聘)") 
        self.INCTAG = re.compile(u"大公司|五百强|全球500强|小公司|成长型公司|创业公司|私有经济|集体经济|集团|外企|已上市|稳定性高|平均年龄\d+岁|妹纸多|学历高|福利待遇好|晋升机会大|民营公司|民营企业|互联网|创业型|国企|央企")

        self.JOBNAME = re.compile(u'\S+(工程师|经理|助理|顾问|前台|秘书|主管|研究员|实习生|操作员|专员|教学人员|技术人员|管理员|业务员|公关|程序员|教师|老师|培训生|\
                                  文员|研究员|策划|主任|总监|设计师|分析师|架构师|摄影师|编辑|Android(开发)?|PHP(开发)?|Python(开发)?)|行政人事|网店设计|客服|会计|电话销售|外贸跟单')

        self.START_DEMAND = re.compile(u"(岗位要求|应聘条件|任职要求|岗位资格|任职资格|岗位条件|工作要求|任职条件|职位要求|职位条件|岗位资格|职位资格)[::\s]?|要求[::\s]")
        self.DEMAND = re.compile(u"熟悉|熟练|具有|善于|懂得|掌握|具备|能够|优先|不少于|不超过|至少|团队.作|良好的|工作经验|开发经验|实习经历|能力强|富有|喜欢|较强的.{2,8}能力|相关专业|相关学历|\S+优先|精通|了解")
        self.DUTY = re.compile(u"跟进|协助|负责|配合|其他工作|领导交办的|对.+提供|审核|参与|为.+提出|日常.+工作|指导|对.+进行|为.+提供|跟进|拓展|运营")
        self.START_DUTY = re.compile(u"(岗位职责|岗位描述|职位描述|任职描述|职位职责|工作职责|职位资格|工作内容|职位内容)[::\s]?")
        self.PAY = re.compile(u"薪酬|待遇|月薪|薪资|年薪|底薪|\d+k|\d+万|\d+元|工资|报酬|薪水|福利")
        self.BENEFIT = re.compile(u"周休|补助|补贴|假日|餐补|提成|交通补助|食宿|加班工资|年假|休假|月假|带薪|全休|晋升|培训|舒适的|旅游|奖励|过节费|五险一金")
        
        self.SPLIT_JD = re.compile(u"岗位[【(]?[一二三四五六七八九][】)]?|^招聘岗位\S+|岗位\d|岗位[一二三四五六]")
        self.CLEAR_NUM = re.compile(u"^\d[\.: :。、]|^[\((【]\d[\))】\.]")
        self.CLEAR_COLO = re.compile(u"^[\.。)(【】,,]|[。;,\.;,]$")
        
        
        jieba.load_userdict('./data/majordic.txt')
        jieba.load_userdict('./data/skills.txt')
        jieba.load_userdict('./data/firm.txt')
        jieba.load_userdict('./data/degrees.txt')


        self.jdStr = ""
        self.linelist = []
        self.lineindex = defaultdict(int)
        self.result = OrderedDict() 

    
    # 分句,预处理
    def preprocess(self,jdstr):
        self.result.clear()
        jdstr = jdstr.decode('utf-8')
        self.linelist = [ line.strip() for line in jdstr.split('\n') if len(line)>3 ]
        self.jdStr = '\n'.join(self.linelist)
        for line in self.linelist:
            self.lineindex[line] = 0


    # 抽取性别要求
    def regular_sex(self):
        res = set()
        findsex = self.SEX.search(self.jdStr)
        if findsex:
            pos = findsex.span()[1]
            jdstr = self.jdStr[max(0,pos-5):min(pos+8,len(self.jdStr))]
            getsex = re.search(u"男|女|男女不限|性别不限",jdstr)
            if getsex:
                res.add(getsex.group())
        if res:
            self.result['sex'] = ' / '.join(res)
        else:
            self.result['sex'] = u'性别不限'


    # 抽取年龄要求
    def regular_age(self):
        res = ''

        for line in self.linelist:
            findage = self.AGE.search(line)
            if findage:
                age = re.findall(u'\d{2}',line)
                if len(age)>=2:
                    res = '-'.join(age)
                elif len(age)==1:
                    if re.search(u'以上|不低于',line):
                        res = age[0]+u'以上'
                    if re.search(u"不超过|不高于|以下",line):
                        res = age[0]+'u以下'
                break
        if len(res)<2:
            res = u'年龄不限'
        self.result['age'] = res
        return res



    # 抽取专业要求
    def regular_major(self):
        res = set()
        for line in self.linelist:
            findmajor = self.MAJOR.search(line)
            if findmajor:
                for majorword in jieba.cut(line):
                    if majorword in self.majordic:
                        res.add(majorword)

        if re.search(u"专业.限|.限专业",self.jdStr) or len(res)<1:
            res.add(u"专业不限")
        elif re.search(u"及相关专业",self.jdStr) and len(res)==1:
            res = list(res)
            res[0]+=u"及相关专业"

        self.result['major'] = ' / '.join(res)
        

    # 抽取学历要求
    def regular_degree(self):
        res = set()
        for line in self.linelist:
            if re.search(u"学历不限|学历要求不限|不限学历",line):
                res.add("学历不限")
                break

            finddegree = self.DEGREE.search(line)
            if finddegree:
                res.add(finddegree.group())
                break

        # 如果没有匹配到学历的要求信息,就整个文本切词后匹配查找
        if len(res)==0:
            for word in jieba.cut(self.jdStr):
                if word in self.degreedic:
                    res.add(word)

        self.result['degree'] = ' / '.join(res)



   
    # 抽取工作经验年限要求    
    def regular_exp(self):

        cnyear = u'[半一二三四五六七八九十两]年|\d-\d年|\d年及?以上|不少于\d年|\d年'
        res = []
        jdStr = self.jdStr

        if re.search(u'经验不限',jdStr):
            res.append(u"经验不限")
            self.result['exp'] = res[0] 
            return res[0]

        findexp = self.EXP.search(jdStr)
        if findexp:
            pos = findexp.span()[1]
            jdStr = jdStr[max(0,pos-15):min(pos+15,len(jdStr))]
            exp = re.search(cnyear,jdStr)
            if exp:
                res.append(exp.group()) 
        else:
            exp = re.search(u"(\d-)?\d{1,2}年(工作|开发|项目)?经[历验]|(不少于)?([半\d]年)及?(以上)?经[历验]|经[历验]\s?(\d-)?\d{1,2}年",jdStr)      
            if exp:
                res.append(exp.group())

        
        self.result["exp"] ="-".join(res)
        self.result["exp"] = self.result['exp'].replace(u'经验',"").replace(u"经历","")
        return res

    def regular_jobtag(self):
        """
        有关职位标签信息
        """
        res = []
        job_tag = re.search(u"应届生|全职|实习生|兼职|应届毕业生|社招",self.jdStr)
        if job_tag:
            res.append(job_tag.group())
        
        
        job_tag = re.search(u"招聘人数[::]?|招聘[::]?",self.jdStr)
        if job_tag:
            jdstr = self.jdStr[job_tag.span()[1]:]
            for line in jdstr.split('\n'):
                if len(line.strip())<2:continue
                else:
                    num = re.search(u"(\d+-)?\d+人?|若干",line)
                    if num:
                        res.append(u"招聘人数:"+num.group())
                    else:
                        res.append(line.strip())
                    break

        job_tag = re.search(u"职位标签[::]?",self.jdStr)
        if job_tag:
            jdstr = self.jdStr[job_tag.span()[1]:]
            for line in jdstr.split('\n'):
                if len(line.strip())<2:continue
                else:
                    res.append("职业标签:"+line.strip())
                    break

        #  根据产品部需求专门切割出包含经验的句子等有关职位标注信息,句子进行更精细化切割
        linelist = [ line for line in re.split(u"[,。;\s]",self.jdStr) if len(line)>5 ]
        for line in linelist:
            if re.search(u"经验",line):
                if re.search(u"\d+k",line):continue
                res.append(self.clean_line(line))
        

        self.result["job_tag"] = ' \n '.join(res)
        return res

    # 清除句子前的数字和标点符合
    def clean_line(self,line):
        line = self.CLEAR_NUM.sub("",line.strip())
        line = self.CLEAR_COLO.sub("",line)
        return line


    # 抽取工作地点
    def regular_workplace(self):
        res = set() 
        jdstr = self.jdStr
        pos = list(re.finditer(u"(工作地.|上班地.)[::]",jdstr))
        if pos:
            jdstr = jdstr[pos[0].span()[0]+4:]
            for line in jdstr.split():
                if len(line.strip())<2:continue
                if len(line)>2 and len(line)<16:
                    res.add(line.strip().replace(":","").replace("",""))
                    self.lineindex[line] = 1
                break
        else: 
            items = (jieba.cut(jdstr))
            for city in items:
                if city in self.citydic and city[:-1] not in res:
                    res.add(city)
        self.result["workplace"] = " / ".join(res)
        return res




    # 抽取证书获奖情况等其他要求
    def regular_cert(self):
        res = set()
        for line in self.linelist:
            findcert = re.search(u"证书|四级|六级|CET-\d|等级证书|(英语|口语|日文|雅思|托福|托业)\d级",line)
            if findcert:
                res.add(findcert.group())
        self.result['cert'] = ' / '.join(res)

    
    # 利用技能词库抽取相关技能
    def regular_skill(self):
        res = set()
        for line in self.linelist:
            for skilltoken in jieba.cut(line.lower()):
                if skilltoken in self.skills:
                    res.add(skilltoken)
        self.result['skill'] = ' / '.join(res)


    # 抽取岗位职责
    def regular_duty(self):
        res = []
        jdStr = self.jdStr
        pos = list(self.START_DUTY.finditer(jdStr))
        if len(pos)>0:
            linelist = jdStr[pos[-1].span()[1]:].split("\n")  
            for line in linelist:
                if len(line)<3 or self.START_DUTY.search(line):
                    continue
                if re.match(r"\d",line):
                    res.append(line.strip())
                else:
                    break
        if len(res) < 2:
            for line in self.linelist:
                if self.DUTY.search(line):
                    res.append(line)
        #       elif self.clf.predict(line) == "duty":
        #           if self.lineindex[line]!=1:
        #               res.append(line)
        #res = sorted(set(res),key = self.linelist.index)
        self.result["duty"] ="\n".join(res)
        for line in res:
            self.lineindex[line]=1

        return res


    # 抽取岗位要求
    def regular_demand(self):
        res = []
        jdStr =self.jdStr
        pos = list(self.START_DEMAND.finditer(jdStr))
        if len(pos)>0:
            linelist = jdStr[pos[-1].span()[1]:].split("\n")
            for line in linelist:
                if len(line)<4 or self.START_DEMAND.search(line):
                    continue
                if re.match(r"\d{1,2}",line.strip()):
                    res.append(line.strip())
                else:
                    break
        if len(res)<2:
            for line in self.linelist:
                if self.lineindex[line]==1:continue # 如果该句已经被处理过,就不再重复显示
                if self.DEMAND.search(line):
                    res.append(line.strip())

        self.result['demand'] = '\n'.join(res)
        for line in res:
            self.lineindex[line.strip()]=1

        return res
    
    # 招聘的职位名
    def regular_jobname(self):
        res = set()

        jdStr = self.jdStr
        findpos = re.search(u"(招聘岗位|招聘职位|职位名称|职位类别|岗位[一二三四五六七八九])[::\s]",jdStr)
        if findpos:
            pos = findpos.span()[1]
            linelist = jdStr[pos:].split("\n")
            for line in linelist:
                if len(line)<2:continue
                if len(line)>2 and len(line)<15:
                    if line.find(u"职位描述")!=-1:continue
                    res.add(re.sub(u"聘请|高薪诚聘|诚聘","",line.strip()))
                    print "jobname",line
                    break

        if res:
            self.result["job_name"] = " / ".join(res)
            return res

        # 如果没有匹配到招聘的具体职位信息,就切词后到职位列表去匹配
        if not res:
            findPos = self.JOBNAME.search(jdStr)
            if findPos and len(findPos.group())<20 and not re.search(u'\d',findPos.group()):
                res.add(findPos.group())
            
            else:
                for line in self.linelist:
                    for word in jieba.cut(line):
                        if word in self.jobdic:
                            res.add(word)
        self.result["job_name"] = " / ".join(res)
        return res
    
    # 薪酬
    def regular_pay(self):
        
        lagoup =  re.search(u"(\d+[kK][-——]\d+[kK])|(\d{3,5}-\d{3,5}元?/月)|(\d{3,5}-\d{3,5}元)|((\d+[-~]\d+)万)|底薪\d+(-\d+)?元?|\d{3,5}元(左右|以上)?|年薪\d+万?元(左右|以上)?",self.jdStr) # 针对拉勾网,没有待遇等关键字符
        pay = ""
        if lagoup:
            pay = lagoup.group()
            self.result["pay"] = pay
            self.result["pay"] = pay.replace(u'k','000').replace(u'K','000').replace(u'','0000')
            return pay

        findpay = self.PAY.search(self.jdStr)
        if findpay:
            pos = findpay.span()[1]
            
            jdstr = self.jdStr[max(0,pos-5):min(pos+10,len(self.jdStr))]
            findpay = re.findall(u"\d+",jdstr)
            if re.search(u"\d+[kK]",jdstr):
                for i in range(len(findpay)):
                    findpay[i] = int(findpay[i])
                    findpay[i] *= 1000
                    findpay[i] = str(findpay[i])
            if re.search(u"面议",jdstr):
                findpay.append(u"面议")
                pay = "-".join(findpay)
        self.result["pay"] = pay.replace(u'k','000').replace(u'K','000').replace(u'','0000')
        return pay


    # 抽取薪资福利
    def regular_benefits(self):
        res = []
        jdStr = self.jdStr 
        findpos =list(re.finditer(u"(福利|待遇)[::\s]",jdStr))
        if not findpos:
            findpos =list(re.finditer(u"晋升制度[::\s]",jdStr))
        if findpos:
            pos = findpos[-1].span()[0]
            linelist = jdStr[pos+3:].split('\n')
            for line in linelist:
                if len(line)<3:continue
                if re.match(ur"[((]?\d+",line) or self.BENEFIT.search(line):
                    res.append(line.strip())
                    self.lineindex[line.strip()] = 1
                else:
                    break
        
        if len(res)==0:
            for line in jdStr.split():
                if len(line)>2 and re.search(u"带薪|福利|诱惑|休假|薪酬|补助|年假",line):
                    if re.search(u"福利|待遇",line) and len(line.strip())<6:continue
                    res.append(line.strip())
        if res:
            self.result["benefits"] ="\n".join(res)
        return res


    # 抽取发布时间和截止时间
    def regular_pubtime(self):

        pub_time = ""
        end_time = ""
        for line in self.linelist:
            if re.search(u"发布时间|发布日期|添加时间|刷新日期",line) and len(line)>6:  # 发布时间
                findtime = re.search("\d+.\d+.\d+.?",line)
                if findtime:
                    pub_time= findtime.group()
                else:
                    pub_time = line[line.find(u"发布")+5:]
                self.lineindex[line]=1
            
            if re.search(u"截止时间|截止日期",line) and len(line)>6:
                findtime = re.search("\d+.\d+.\d+.?",line[line.find(u"截止")+4:])
                if findtime:
                    end_time = findtime.group()
                else:
                    end_time = line[line.find(u"截止")+5:]
                self.lineindex[line]=1

        # 如果是当天,就加上当天日期       
        if re.match(u"\d{1,3}[::]\d{1,3}发布$",pub_time.strip()):
            pub_time = datetime.today().strftime("%Y-%m-%d  ")+pub_time.replace(u"发布","") 

        # 如果是n天前发布
        isBefore = self.PUB_TIME.search(pub_time)
        if isBefore:
            tmpdate = datetime.today() - timedelta(days = int(isBefore.group(1).encode('utf-8')))
            pub_time = tmpdate.strftime("%Y-%m-%d ")

        self.result["pub_time"] = pub_time
        self.result["end_time"] = end_time

        return pub_time+" "+ end_time

    # 企业名抽取
    def regular_inc_name(self):
        res = set ()
        findinc = self.INCNAME.search(self.jdStr)
        if findinc:
            if len(findinc.group())<22:
                res.add(findinc.group().replace("招聘",""))
        else:
            for line in self.linelist:
                for item in jieba.cut(line):
                    if item in self.firmnames:
                        res.add(item)
                if len(res)>0:break
        res = filter(lambda x:len(x)>2 and not re.match('\d',x),list(res))
        self.result["inc_name"] = ' / '.join(res)
        return res
   

    # 公司标签,如公司规模,是否已上市,外企,员工稳定性等
    def regular_inc_tag(self):

        res = set()
        jdStr = self.jdStr

        # 公司性质
        findTag = re.search(u"(公司性质|企业性质)[::\s]?|性质[:: ]",jdStr)
        if findTag:
            jdstr =jdStr[findTag.span()[1]:]
            for line in jdstr.split("\n")[:3]:
                if len(line.strip())<2:continue
                elif len(line)<15: 
                    print "inc_tag1",line
                    res.add(u"性质:"+line.strip())
                    break
        else:
            findTag = re.search(u"欧美外资|非欧美外资|欧美合资|非欧美合资|外企代表处|政府机关|事业单位|民营公司|民营企业|创新型公司|非盈利机构|其他性质",jdStr)
            if findTag:
                res.add(findTag.group())

        # 所属行业
        findTag = re.search(u"(公司行业|所属行业)[::\s]|行业[:: ]",jdStr)
        if findTag:
            jdstr =jdStr[findTag.span()[1]:]
            for line in jdstr.split("\n"):
                if len(line.strip())<2:continue
                elif len(line)<35: 
                    print "inc_tag2",line
                    res.add(u"行业:"+line.strip())
                    break

        else:
            P1 = re.compile(ur"教育培训|采矿/金属|医疗健康|移动互联网|互联网|科技|医疗器械|新材料|奢侈品/珠宝|通信|航天/造船|出版|军工/国防|农/林/牧/渔|家电/数码产品|造纸|人力资源|体育|会计/审计|艺术/工艺|管理咨询|印刷/包装|运输/铁路/航空|环境保护|物流/仓储|化工|原油/能源|生物技术|翻译|原材料/加工|专业服务|报纸/杂志|医疗/卫生|酒店|IT信息|制造工业|服装/纺织|互联网|休闲/娱乐/健身|/基金/期货|计算机软件|广播|私人/家政服务|建材|初中等教育|烟草业|旅游|银行|汽车|法律|电子商务|游戏|政府部门|服务业|公共事业|IT服务|建筑/房地产|批发/零售|非营利组织|计算机硬件|电子/半导体|食品/饮料|机械/自动化|检测/认证|日用品/化妆品|研究所/研究院|建筑设计/规划|贸易物流|商店/超市|图书馆/展览馆|医药|文化传媒|广告/公关/会展|房地产|进出口|土木工程|动漫|餐饮业?|医疗/护理|培训|消费品|金融|影视|高等教育|酒品|家具/家居|物业管理|装修装潢")
            
            findTag = P1.search(jdStr)
            if findTag:
                res.add(findTag.group())

        # 公司规模
        pos = re.search(u"公司规模|员工人数|规模[::]",jdStr)
        if pos:
            jdStr = jdStr[max(0,pos.span()[1]-3):min(pos.span()[1]+20,len(jdStr))]
        
        scale = re.search(ur"\d{2,}[-~——]\d{2,}人|(\d+人以下)|(\d{3,}人以上)|\d+人",jdStr)
        if scale:
            res.add(u"规模:"+scale.group())

        # 其他特别信息
        if len(res)<3:
            for line in self.linelist:
                find_tag = re.search(ur"大公司|五百强|世界500强|全球500强|小公司|成长型公司|创业公司|私有经济|集体经济|集团|外企|已上市|稳定性高|平均年龄\d+岁|妹纸多|学历高|福利待遇好|晋升机会大|民营公司|民营企业|互联网|创业型|国企|央企|500强",line)
                if find_tag:
                    res.add(find_tag.group())
                    self.lineindex[line] = 1

        self.result["inc_tag"] = " \n ".join(res)
        return " / ".join(res)


    # 将剩余未处理的标记出来
    def regular_other(self):
        res = []
        cnt = 0.0
        for line in self.linelist:
            if self.lineindex[line]!=1:
                if (re.search(u"内容|描述|职责|要求|条件|制度|时间|地点|福利|岗位",line) and len(line)<6):continue
                res.append(line)
                cnt += 1
        print "处理百分率%.3f"%(1.0-cnt/len(self.linelist))
        self.result["other"] ="\n".join(res)


            
    def parser(self,jdstr):
        self.preprocess(jdstr)
        self.regular_inc_name() # 企业名称
        self.regular_inc_tag()  # 企业标签
        self.regular_pubtime() # 发布时间,截止时间
        self.regular_jobname() # 职位名称
        self.regular_jobtag()  # 职位标签
        self.regular_sex()      # 性别
        self.regular_age()      # 年龄
        self.regular_major()    # 专业
        self.regular_degree()   # 学历
        self.regular_exp()      # 经验
        self.regular_skill()    # 技能
        self.regular_workplace() # 工作地点
        self.regular_pay()      # 薪酬
        self.regular_cert()     # 证书
        self.regular_duty()     # 职责
        self.regular_demand()   # 要求
        self.regular_benefits() # 福利
        self.regular_other()    # 其他
        return self.result

    def split_multi_jd(self,jdstr):
        jds =[ jd for jd in self.SPLIT_JD.split(jdstr) if len(jd)>10 ]
        res = []
        for jd in jds:
            result = self.parser(jd)
            res.append(deepcopy(result))
        return res



if __name__ == "__main__":
    test = JdParser()
    jdstr = [line.strip() for line in open('data/jd_text.txt')]
    print "input jdstr:\n",jdstr
    print "output:"
    result = test.parser('\n'.join(jdstr))
    for k,v in result.items():
        print "{}:{}".format(k,v)

对提取的结果进行评测:

#!/usr/bin/env python
# coding=utf-8

import sys,re
from simhash import Simhash
from copy import deepcopy
from collections import defaultdict
from jd_parser import JdParser
reload(sys)
sys.setdefaultencoding("utf-8")


class Evaluate(object):
    """
    对最后jd结果进行评测
    """
    def __init__(self):
        self.LABELLIST = ["job_name","job_tag","sex","age","degree","major","exp","pay","skill","duty","demand","inc_name","inc_tag","benefits","pub_time","end_time","cert","workplace","other"]

    def cal_dist(self,stra,strb):
        return Simhash(stra.strip()).distance(Simhash(strb.strip()))

    def evaluate_single(self,dicta,dictb):
        res = defaultdict(int)
        both_keys = set(dicta.keys()) | set(dictb.keys())
#        print "len both_keys",len(both_keys),' / '.join(both_keys)
        for key in both_keys:
            try:
                if self.cal_dist(dicta[key],dictb[key])<5:
                    res[key] = 1
                else:
                    res[key] = 0
                    print "==="*20
                    print "error",key
                    print "reference",dicta[key]
                    print 'output',dictb[key]
                    print "==="*20
            except Exception,e:
                print e
                continue
        return res

    def evaluate(self,tagged_jd,output_jd):
        assert len(tagged_jd)==len(output_jd),"list must have the same length, %d,%d" %(len(tagged_jd),len(output_jd))
        cal_res = []
        for jd in zip(tagged_jd,output_jd):
            tmp = self.evaluate_single(jd[0],jd[1])
            cal_res.append(deepcopy(tmp))

        res = defaultdict(lambda :defaultdict(float))
        for jd in cal_res:
            for key in jd:
                res[key]["precision"]+=jd[key]
                res[key]["count"] += 1
        for key in res:
            res[key]["precision"] /= res[key]["count"]
            res[key]['precision'] = "%3.1f%%" %(res[key]['precision']*100)
        return res


    def isLabel(self,label):
        if label in self.LABELLIST:
            return True
        return False

    def read_test_iter(self,fname="./data/lagou_test.txt"):
        res = {}
        block = []
        LABEL = re.compile(u'^[a-z_]{3,10}')
        label = "job_name"
        for line in open(fname):
            if line.startswith(u"=====") and len(res)>0:
                yield res
                block = []
                res.clear()
            elif LABEL.search(line) and self.isLabel(LABEL.match(line.strip()).group()): 
                res[label] = '\n'.join(block)
                block = []
                label = LABEL.match(line.strip()).group()
                continue
            else:
                block.append(line.strip())
        
        if block:
            res[label] ='\n'.join(block)
        if res:
            yield res

    
    def read_train_iter(self,fname='./data/lagou_train.txt'):
        """
        读入测试数据,31份JD
        """
        res = []
        for line in open(fname):
            if line.startswith(u"====") and len(res)>0:
                yield '\n'.join(res)
                res = []
            elif len(line.strip())>2:
                res.append(line.strip())
        if res:
            yield '\n'.join(res)


    def clean_data():
        line = [line.strip() for line in open('./data/lagou_test.txt') if len(line.strip())>2]
        jdstr = '\n'.join(line)
        jdstr = re.sub(" : ","\n",jdstr)
        open('./data/lagou_test_txt.clean','wb').write(jdstr+"\n")


    def load_test_data(self,fname='./data/lagou_train.txt',testfame='./data/lagou_test.txt'):
        test = []
        for i,jd in enumerate(self.read_test_iter(fname='./data/lagou_test.txt')):
            test.append(deepcopy(jd))
        return test


    def get_output_data(self,jd_parser,fname='./data/lagou_train.txt'):
        res = []
        for jdstr in self.read_train_iter(fname):
            single_res = jd_parser.parser(jdstr.decode('utf-8'))
            res.append(deepcopy(single_res))
        return res


if __name__ == "__main__":
    test = Evaluate()
    jdparser = JdParser()
    input = test.load_test_data()
    output = test.get_output_data(jdparser)
    res = test.evaluate(input,output)
    for k in res:
        print k,res[k]

演示demo:

input: http://www.lagou.com/jobs/281175.html?source=position_rec&i=position_rec-1

output:

  • inc_name :
     魔线科技公司销售部
  • inc_tag :
     科技 
     互联网
  • pub_time :
     2015-10-08
  • end_time :
     
  • job_name :
     销售总监
  • job_tag :
     全职 
     销售总监 
     经验5-10年 
     商家拓展经验 
     4年以上的管理经验
  • sex :
     性别不限
  • age :
     年龄不限
  • major :
     专业不限
  • degree :
     本科及以上
  • exp :
     5-10年
  • skill :
     销售总监 / 推广活动 / 项目管理 / 整体规划 / 个性化营销 / 市场信息 / 团队管理
  • workplace :
     深圳 / 美国
  • pay :
     15000-30000
  • cert :
     
  • duty:
    1、了解各地区商家的需求,制定个性化营销方案,与商户洽谈并达成合作;
    2、具有出色的团队组建和管理能力, 有综合沟通能力和个人影响力,能够快速地组建团队以不断开拓新的合作商家;
    3、拓展及维护新老商家,与各商家建立长期稳定的合作关系,并配合商家的推广活动;
    4、与公司各部门有效配合,及时处理来自商家及消费者的投诉、反馈、建议等,以提高消费者和商家的满意度;
    5、收集一线商家信息、用户意见、当地市场信息、竞争对手信息等,给公司决策层提供重要的参考数据;
    6、对商家市场有一定的分析和把控能力并相对应制定魔线商家渠道拓展的策略;
    7、根据公司整体规划,制定魔线商家的拓展计划、达成目标及费用预算;
    8、制定本部门各阶段工作计划,按计划开展各项工作的里程碑。 
  • demand:
    1、本科及以上学历,专业不限;
    2、有丰富的业务销售,商家拓展经验,7年以上相关行业背景,4年以上的管理经验;
    3、对O2O商务产品领域有深刻理解,拥有一定的产品战略和业务规划的能力,性格稳定踏实,具备优秀的团队管理能力,超强的沟通协调能力和项目管理能力;
    4、充满使命感和创业激情,能承受较大工作压力;
    5、具有良好的个人影响力及广泛的社会资源,有连锁店或行业资源者优先;
    6、热爱生活、热爱互联网及本地生活服务行业。 
  • benefits:
    职位诱惑
    美国上市公司,福利齐全! 
  • other:
    魔线科技公司销售部招聘
    销售总监
    15k-30k 深圳 经验5-10年 本科及以上 全职
    职位诱惑 : 美国上市公司,福利齐全! 

 

posted on 2015-10-13 15:25  星空守望者--jkmiao  阅读(3398)  评论(1编辑  收藏  举报