Python разбивает текст на предложения
У меня есть текстовый файл. Мне нужен список предложений.
Как это можно реализовать? Есть много тонкостей, таких как точка используется в аббревиатурах.
мой старый и работает плохо.
re.compile('(. |^|!|?)([A-Z][^;↑.<>@^&/[]]*(.|!|?) )',re.M)
9 ответов:
инструментарий естественного языка (nltk.org) имеет то, что вам нужно. Это групповое сообщение указывает, что это не так:
import nltk.data tokenizer = nltk.data.load('tokenizers/punkt/english.pickle') fp = open("test.txt") data = fp.read() print '\n-----\n'.join(tokenizer.tokenize(data))
(Я еще не пробовал!)
эта функция может разделить весь текст Huckleberry Finn на предложения примерно за 0,1 секунды и обрабатывает многие из более болезненных краевых случаев, которые делают разбор предложений нетривиальным, например"г-н Джон Джонсон-младший родился в США, но получил степень доктора философии в Израиле, прежде чем присоединиться к Nike Inc. в качестве инженера. Он также работал в craigslist.org как бизнес-аналитик."
# -*- coding: utf-8 -*- import re caps = "([A-Z])" prefixes = "(Mr|St|Mrs|Ms|Dr)[.]" suffixes = "(Inc|Ltd|Jr|Sr|Co)" starters = "(Mr|Mrs|Ms|Dr|He\s|She\s|It\s|They\s|Their\s|Our\s|We\s|But\s|However\s|That\s|This\s|Wherever)" acronyms = "([A-Z][.][A-Z][.](?:[A-Z][.])?)" websites = "[.](com|net|org|io|gov)" def split_into_sentences(text): text = " " + text + " " text = text.replace("\n"," ") text = re.sub(prefixes,"\1<prd>",text) text = re.sub(websites,"<prd>\1",text) if "Ph.D" in text: text = text.replace("Ph.D.","Ph<prd>D<prd>") text = re.sub("\s" + caps + "[.] "," \1<prd> ",text) text = re.sub(acronyms+" "+starters,"\1<stop> \2",text) text = re.sub(caps + "[.]" + caps + "[.]" + caps + "[.]","\1<prd>\2<prd>\3<prd>",text) text = re.sub(caps + "[.]" + caps + "[.]","\1<prd>\2<prd>",text) text = re.sub(" "+suffixes+"[.] "+starters," \1<stop> \2",text) text = re.sub(" "+suffixes+"[.]"," \1<prd>",text) text = re.sub(" " + caps + "[.]"," \1<prd>",text) if "”" in text: text = text.replace(".”","”.") if "\"" in text: text = text.replace(".\"","\".") if "!" in text: text = text.replace("!\"","\"!") if "?" in text: text = text.replace("?\"","\"?") text = text.replace(".",".<stop>") text = text.replace("?","?<stop>") text = text.replace("!","!<stop>") text = text.replace("<prd>",".") sentences = text.split("<stop>") sentences = sentences[:-1] sentences = [s.strip() for s in sentences] return sentences
вот середина пути подхода, который не зависит от каких-либо внешних библиотек. Я использую понимание списка, чтобы исключить перекрытия между аббревиатурами и Терминаторами, а также исключить перекрытия между вариациями окончаний, например: '."против"."'
abbreviations = {'dr.': 'doctor', 'mr.': 'mister', 'bro.': 'brother', 'bro': 'brother', 'mrs.': 'mistress', 'ms.': 'miss', 'jr.': 'junior', 'sr.': 'senior', 'i.e.': 'for example', 'e.g.': 'for example', 'vs.': 'versus'} terminators = ['.', '!', '?'] wrappers = ['"', "'", ')', ']', '}'] def find_sentences(paragraph): end = True sentences = [] while end > -1: end = find_sentence_end(paragraph) if end > -1: sentences.append(paragraph[end:].strip()) paragraph = paragraph[:end] sentences.append(paragraph) sentences.reverse() return sentences def find_sentence_end(paragraph): [possible_endings, contraction_locations] = [[], []] contractions = abbreviations.keys() sentence_terminators = terminators + [terminator + wrapper for wrapper in wrappers for terminator in terminators] for sentence_terminator in sentence_terminators: t_indices = list(find_all(paragraph, sentence_terminator)) possible_endings.extend(([] if not len(t_indices) else [[i, len(sentence_terminator)] for i in t_indices])) for contraction in contractions: c_indices = list(find_all(paragraph, contraction)) contraction_locations.extend(([] if not len(c_indices) else [i + len(contraction) for i in c_indices])) possible_endings = [pe for pe in possible_endings if pe[0] + pe[1] not in contraction_locations] if len(paragraph) in [pe[0] + pe[1] for pe in possible_endings]: max_end_start = max([pe[0] for pe in possible_endings]) possible_endings = [pe for pe in possible_endings if pe[0] != max_end_start] possible_endings = [pe[0] + pe[1] for pe in possible_endings if sum(pe) > len(paragraph) or (sum(pe) < len(paragraph) and paragraph[sum(pe)] == ' ')] end = (-1 if not len(possible_endings) else max(possible_endings)) return end def find_all(a_str, sub): start = 0 while True: start = a_str.find(sub, start) if start == -1: return yield start start += len(sub)
я использовал функцию find_all Карла из этой записи: найти все вхождения подстроки в Python
вместо того, чтобы использовать регулярное выражение для разделения текста на предложения, вы также можете использовать библиотеку nltk.
>>> from nltk import tokenize >>> p = "Good morning Dr. Adams. The patient is waiting for you in room number 3." >>> tokenize.sent_tokenize(p) ['Good morning Dr. Adams.', 'The patient is waiting for you in room number 3.']
для простых случаев (когда предложения обычно заканчиваются) это должно работать:
import re text = ''.join(open('somefile.txt').readlines()) sentences = re.split(r' *[\.\?!][\'"\)\]]* *', text)
регулярное выражение
*\. +
, который соответствует периоду, окруженному 0 или более пробелами слева и 1 или более справа (чтобы предотвратить что-то вроде периода в re.сплит засчитывается как изменение в предложении).очевидно, что это не самое надежное решение, но в большинстве случаев все будет хорошо. Единственный случай, когда это не будет охватывать аббревиатуры (возможно, запустить через список предложения и проверьте, что каждая строка в
sentences
начинается с большой буквы?)
вы можете попробовать использовать Spacy вместо регулярных выражений. Я использую его, и он делает свою работу.
import spacy nlp = spacy.load('en') text = '''Your text here''' tokens = nlp(text) for sent in tokens.sents: print(sent.string.strip())
@Artyom,
Привет! Вы можете сделать новый токенизатор для русского (и некоторых других языков), используя эту функцию:
def russianTokenizer(text): result = text result = result.replace('.', ' . ') result = result.replace(' . . . ', ' ... ') result = result.replace(',', ' , ') result = result.replace(':', ' : ') result = result.replace(';', ' ; ') result = result.replace('!', ' ! ') result = result.replace('?', ' ? ') result = result.replace('\"', ' \" ') result = result.replace('\'', ' \' ') result = result.replace('(', ' ( ') result = result.replace(')', ' ) ') result = result.replace(' ', ' ') result = result.replace(' ', ' ') result = result.replace(' ', ' ') result = result.replace(' ', ' ') result = result.strip() result = result.split(' ') return result
а потом назовем это так:
text = 'вы выполняете поиск, используя Google SSL;' tokens = russianTokenizer(text)
удачи, Марилена.
несомненно, что NLTK является наиболее подходящим для этой цели. Но начало работы с NLTK довольно болезненно (но как только вы его установите - вы просто пожинаете плоды)
Итак, вот простой re-код, доступный по адресу http://pythonicprose.blogspot.com/2009/09/python-split-paragraph-into-sentences.html
# split up a paragraph into sentences # using regular expressions def splitParagraphIntoSentences(paragraph): ''' break a paragraph into sentences and return a list ''' import re # to split by multile characters # regular expressions are easiest (and fastest) sentenceEnders = re.compile('[.!?]') sentenceList = sentenceEnders.split(paragraph) return sentenceList if __name__ == '__main__': p = """This is a sentence. This is an excited sentence! And do you think this is a question?""" sentences = splitParagraphIntoSentences(p) for s in sentences: print s.strip() #output: # This is a sentence # This is an excited sentence # And do you think this is a question
мне пришлось читать файлы субтитров и разбивать их на предложения. После предварительной обработки (например, удаление информации о времени и т. д.srt files), переменная fullFile содержала полный текст файла субтитров. Нижеприведенный грубый способ аккуратно разделил их на предложения. Наверное, мне повезло, что предложения всегда заканчивались (правильно) пробелом. Сначала попробуйте это, и если у него есть какие-либо исключения, добавьте больше сдержек и противовесов.
# Very approximate way to split the text into sentences - Break after ? . and ! fullFile = re.sub("(\!|\?|\.) ","\1<BRK>",fullFile) sentences = fullFile.split("<BRK>"); sentFile = open("./sentences.out", "w+"); for line in sentences: sentFile.write (line); sentFile.write ("\n"); sentFile.close;
о! что ж. Теперь я понимаю, что с моего контента был испанцем, у меня не было проблем с "мистером Смитом" и т. д. Тем не менее, если кто-то хочет быстрый и грязный парсер...