Найти всё. Text Mining

Всем добрый день! Хочу поделиться опытом анализа текста.

Возьму рабочий пример документов в отношении граждан, проходящих процедуру банкротства. Задача заключается в автоматизированном сборе информации из текста 300 тыс. документов такой как: номер счета, с которого можно снять средства, разрешенная сумма, период действия. Пример интересующей меня части документа:

Найти всё. Text Mining

Немного скучной теории: формулировка заключений финансовых управляющих не регламентируется специальными правилами и потому может выглядеть по-разному и может находится в разных частях документа. Но в подобных заключениях (если они имеются) всегда будут упоминаться номер специального счета (для физ. лица он начинается с 4), сумма и фразы, подразумевающие разрешение на снятие денежных средств. С помощью данных эвристик можно локализовать нужный нам кусок текста!

Воспользуюсь python и библиотеками regex и Natasha. С помощью регулярных выражений локализирую нужные предложения, а разбивать текст на предложения и вытаскивать необходимые поля буду с Natasha. Многие знакомы с отличными способностями библиотеки Natasha к распознаванию именованных сущностей (имена, города, названия компаний и т.д.). Но, помимо этого, она умеет находить даты, суммы денег и даже адреса!

Импортирую нужные библиотеки:

import os import pandas as pd from natasha import (Doc, Segmenter, NewsEmbedding, NewsMorphTagger, NewsSyntaxParser, MorphVocab, NewsNERTagger, DatesExtractor, MoneyExtractor) import re from collections import Counter import openpyxl import datetime pd.options.display.max_columns = 100 pd.options.display.max_rows = 100 import warnings warnings.filterwarnings('ignore')

Напишу функцию get_info, которая на вход будет принимать текст и предложения, а на выходе возвращает номер счета, сумму и период. Сначала с помощью регулярных выражений нахожу центральное предложение и беру соседние 2 предложения.

def get_info(text, sents): try: match = re.finditer('4\d{19}', text) marks = [m.start() for m in match] bill = None money = None date1 = None date2 = None for ind, i in enumerate(sents): for mark in marks: if i.start <= mark and i.stop > mark: mini_sents = [sents[ind-1], i ,sents[ind+1]] # берем 3 предложения bill_text = ' '.join([x.text for x in mini_sents])

Далее проверяю, если там идет речь о разрешении на снятия или о денежных лимитах, то забираем нужные поля.

patterns = ['не более', 'в пределах', 'разблокир\w+', 'не может превышать', 'превыша\w+', 'самостоятельно', 'имеет право', 'распоряж\w+', '[Дд]еньги снимаются'] matches = [] [matches.extend(re.findall(pattern, bill_text)) for pattern in patterns] if matches: matches = money_extractor(bill_text) # распознавание денег facts = [i.fact.as_json for i in matches] facts = [f.get('amount') for f in facts] money = facts bill = re.search('4\d{19}',bill_text) # забираем счет try: start = [m.start() for m in re.finditer('\s+с\s+\d{2}', bill_text)][0] # находим упоминание периода dates = dates_extractor(bill_text[start:]) # распознавание дат dates = [datetime.date(d.fact.as_json.get('year'), d.fact.as_json.get('month'), d.fact.as_json.get('day')) for d in dates] date1 = dates[0] date2 = dates[1] except: pass if money: money = [0] return bill, money, date1, date2 except: return None, None, None, None

Создаю датафрейм:

data = pd.DataFrame({'ЗНО':[], 'Номер счета' : [], 'Дата 1' : [], 'Дата 2' : [], 'Сумма' : []})

В цикле открываю и считаю информацию из файлов, убираю символы переноса и табуляции. Затем передаю текст в Natasha для токенизации и распознавания сущностей. Вызываю написанную мной функцию get_info и дописываю в датафрейм найденную информацию.

segmenter = Segmenter() emb = NewsEmbedding() morph_tagger = NewsMorphTagger(emb) syntax_parser = NewsSyntaxParser(emb) morph_vocab = MorphVocab() ner_tagger = NewsNERTagger(emb) path = 'docs/' filenames = os.listdir(path) for filename in filenames: with open(path + filename, 'r') as file: text += file.read() text = text.replace('\n', ' ') text = text.replace('\t', ' ') doc = Doc(text) dates_extractor = DatesExtractor(morph_vocab) money_extractor = MoneyExtractor(morph_vocab) doc.segment(segmenter) doc.tag_morph(morph_tagger) doc.parse_syntax(syntax_parser) doc.tag_ner(ner_tagger) num, money, date1, date2 = get_info(doc.text, doc.sents) data = pd.concat([pd.DataFrame({ 'ЗНО':[filename.split('.')[0].split('_')[0]], # ЗНО в нашем случае являлось название документа 'Номер счета' : [num], 'Дата 1' : [date1], 'Дата 2' : [date2], 'Сумма' : [money]}), data])

На выходе получаю желаемые данные в виде таблицы:

Найти всё. Text Mining

Таким образом, с помощью специальных эвристик и уже существующих инструментов мне удалось автоматизировано и качественно получить желаемую информацию из нескольких сотен тысяч документов и, при этом, не изобретать колесо. На этом всё, надеюсь была полезна, всем успехов!

2424 показа
1.4K1.4K открытий
Начать дискуссию