【python】結巴分詞案例(英文詞組識別)

本人菜雞一隻,今天來寫寫結巴分詞!

哇,距離上一次寫文章已經20天過去了,最近這些天還真是挺忙的,主要是上上週到了跑月數據的節點,然後上週原始數據出了問題,我調了一週多才把這個錯誤解決了,還修復了一個隱藏的小bug

在這裏提醒下自己,用任何表在做關聯的時候一定要好好檢查,關聯鍵是不是唯一的,否則會數據倍增!!

其實在這一段時間裏,還是有一點點自己學習的。

首先整理了ES的一些API,還沒整理完,後面我會出一篇文章來寫這個的!

本文,是最近使用結巴分詞做的一些事情的總結!

業務場景:

先說說業務場景,數據庫中有一些文本,我想從文本里面統計某些詞的count,這些詞是我自定義的詞(就比如說是洗髮水的品牌名稱吧),我想看看每一條文本中,是否有提到相關品牌,並且提到的次數!

 

實現工具:

python+結巴分詞+數據庫,應該是開發起來最快的了吧,雖然python可能性能不是那麼好,但是用起來方便!

結巴的介紹1:https://gitee.com/fxsjy/jieba

結巴的介紹2:https://github.com/fxsjy/jieba

 

實際遇到的問題:

1、英文詞組如何處理

有一些品牌是英文,有大小寫的,有空格,比如(SUPER MILD),可能結巴分詞直接把這兩個英文拆開了,那就匹配不上了!

因此我搜索到了解決方案:https://segmentfault.com/q/1010000016011808

1、在結巴根目錄下,打開__init__.py只需要往相應的正則表達式添加空格即可,如

-re_han_default = re.compile("([\u4E00-\u9FD5a-zA-Z0-9+#&\._%]+)", re.U)
+re_han_default = re.compile("([\u4E00-\u9FD5a-zA-Z0-9+#&\._% ]+)", re.U)

 2、或者在調用jieba.cut(text, cut_all=False)之前,先執行:

jieba.re_han_default = re.compile('(.+)', re.U)

 

 這樣就可以識別詞組了,當然也可以考慮換一個分詞包來實現,比如:MWETokenizer

2、添加自定義詞(其實就是洗髮水品牌名稱)

https://github.com/fxsjy/jieba/issues/14

上面這個網址,大家有興趣可以往後翻翻,有一些開發者提出的一些使用上的問題,也有人在幫忙回答,說不定就能解決你的問題!

 

 

貼上代碼:

# coding=gbk
import psycopg2
import jieba.analyse


##讀取品牌名稱列表,通過這個list來過濾掉不屬於洗髮水品牌的詞彙
def readbrandTXT(rootdir):
    brandList = []
    with open(rootdir, 'r', encoding='UTF-8') as file_to_read:
        while True:
            line = file_to_read.readline()
            if not line:
                break
            line = line.strip('\n')
            brandList.append(line)
    return brandList 

# 獲取該文本的詞和詞的出現次數
def getNum(text, list):
    word = []
    counter = {}
    jieba.re_han_default = re.compile('(.+)', re.U)
    seg_list = jieba.cut(text)
    listStr = "#".join(seg_list)
    list = listStr.split('#')
    for w in list:
        if w in list:
            if not w in word:
                word.append(w)
            if not w in counter:
                counter[w] = 1
            else:
                counter[w] += 1
    counter_list = sorted(counter.items(), key=lambda x: x[1], reverse=True)
    return counter_list
   


# 插入數據庫的方法
def insertManyRow(strings):
    try:
        conn = psycopg2.connect(database="數據庫名", user="用戶名", password="密碼", host="ip地址",port="端口號")
        cur2 = conn.cursor()
        sql2 = "INSERT INTO schema名稱.表名(字段1,字段2,字段3,字段4...很多字段) " \
               "VALUES(%(字段1)s,%(字段2)s,%(字段3)s,%(字段4)s...很多字段)"
        cur2.executemany(sql2, strings)
        conn.commit()
        cur2.close()
        conn.close()
        print("成功插入數據,關閉連接")
    except Exception as e:
        print("執行sql時出錯:%s" % (e))
        cur2.rollback()
        conn.close()


##獲取還未處理的數據
def getUnprocessedData():
   conn = psycopg2.connect(database="數據庫名", user="用戶名", password="密碼", host="ip地址",port="端口號")
    cur3 = conn.cursor()
    #這裏是一段leftjoin,查出有哪些數據還未處理,一次性查出2000條
    cur3.execute("""SELECT a.* FROM 全量數據表 a LEFT JOIN  已處理的數據表 b \
on a.主鍵=b.主鍵 \
WHERE b.主鍵 is null limit 2000""")
    rows = cur3.fetchall()  # all rows in table
    #print(rows)
    #print("============")
    return rows


if __name__ == "__main__":
    brandList = readbrandTXT('D:\\洗髮水品牌列表.txt')
    for i in range(100):
        print('開始循環:',i+1)
        strings = []
        rows = getUnprocessedData()
        #通過i來判斷批量插入的時間點
        i = 0
        try:
            for index in range(len(rows)):
                #將文本傳入getNum,和想要的品牌列表
                counter_list = getNum(rows[index][4] + rows[index][5], brandList )
                if (counter_list.__len__()!=0):
                    for j in counter_list:
                        text = {}
                        text = {'字段1': rows[index][0], '字段2': rows[index][1], '字段3': rows[index][2],
                            '字段4': rows[index][3],
                            "字段5": rows[index][4]....把字段都放進來}
                        strings.append(text)
                        i = i + 1
                        # print(text)
                else:
                    #如果返回的結果爲空,證明該文本中沒有相關詞彙,就插入null
                    text = {'字段1': rows[index][0], '字段2': rows[index][1], '字段3': rows[index][2],
                            '字段4': rows[index][3],
                            "字段5": None....把字段都放進來,}
                    strings.append(text)
                    i = i + 1
                #約500條數據插入一次
                if (i >= 500):
                    print("i:",i)
                    insertManyRow(strings)
                    strings.clear()
                    i = 0
            #如果到了最後,數據不到500條也需要強行執行插入
            if (strings.__len__() != 0):
                print("strings.__len__()",strings.__len__())
                insertManyRow(strings)
                strings.clear()
        except Exception as e:
            print('Error',e)
            #就算當前循環插入失敗,也繼續下一步循環(因爲是增量處理,所以插入失敗的數據會被重新查出來)
            continue

 

好的,本人比較不是專業的python工程師,所以代碼可能寫的稀爛,不好意思,請諒解!

 

我遇到的問題,與需要注意的點:

1、要使用英文詞彙,需要添加正則匹配規則的修改

2、添加自定義詞的時候

-1.加載文檔

jieba.load_userdict("D:\\load_taobrand.txt")

但是在使用加載文檔的時候,會遇到文檔裏面用空格來做不同列的分割符,但是我的詞彙中自帶空格,導致詞彙無法添加,解決方案是要麼用單獨添加詞組的方式,要麼修改load_userdict方法默認的分隔符,詳情見如下:

相關issue:https://github.com/fxsjy/jieba/issues/423

 -2.單獨添加詞語

jieba.add_word("自定義詞語")

3、判別是否需要添加該自定義的詞

-1.詞組純英文單詞

如果有的品牌是純英文單詞,例如:AB,如果修改了正則匹配規則,並添加了該詞到自定義詞彙中,會導致“ABCDEFG HIJKLMN”這樣的一串英文,原本分爲ABCDEFG,HIJKLMN兩個詞,變成分爲AB,CDEFG,HIJKLMN三個詞,這樣其實違背了我們的意願,我們想要的是,單獨提到AB品牌的文章,因此純英文單詞,沒有任何符號的詞不需要加入到自定義詞彙中!

-2.純中文品牌

純中文品牌也需要注意,有些品牌名字比較長,比如:“悅詩風吟”,如果不添加該詞的話,結巴分詞默認會分爲“悅詩”,“風吟”兩個詞,這樣也就匹配不上該品牌,所以名字較長,或者不是常規詞彙的中文品牌,需要添加到自定義詞彙中!

-3.品牌名稱夾雜中英文或者特殊符號

這部分品牌建議還是要添加到詞彙中的,例如:DE&E,如果不添加,默認是“DE”,“&”,“E”,因此如果要識別,也需要添加

4、處理數據當中,我遇到了一些識別字符串中是否包含英文,是否包含中文,是否純英文等等需求,我稍微記錄下(不同數據庫用法不同,我是greenplum/postgresql)

--當包含中文的時候,如下兩個函數返回長度不一樣
select octet_length(字符串) <> char_length(字符串)
--是否純英文
select 字符串 ~ '^([a-zA-Z]+\s?[a-zA-Z]*\s?[a-zA-Z]*)$';

 

 

 

總結:

總結下有哪些需要改進的地方:

1、品牌名字過短,是一些常用詞彙或者有歧義,比如“一點點”,“時候”,“美的”等品牌,可能文章中是“天冷的時候”,“美的一天”,但是卻被識別成某個品牌,就是有些品牌名字會跟大衆使用的詞彙混合起來,就不好識別,暫時沒有想到好的方法來解決

2、性能比較差,我的每個文本其實很短,也就40個字左右吧(可能還沒有),有21W條數據,運行我的代碼,批量插入數據庫,最終運行了5個小時左右,也就是每秒能夠往數據庫插入十幾條數據,相對來說還是比較慢的

 

當然這個需求只是個先行版,後面肯定會有更多更復雜的東西在裏面,我也沒想好,後面再說吧!有興趣大家可以好好看看結巴分詞的GitHub的README,裏面還是寫的很清晰的,文本分詞還是很常見的需求~

好了本文就到這裏,菜雞一隻,如果有任何疑問,或者我說的不對的地方,歡迎大家留言!

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章