Python разбить текст на предложения - PullRequest
81 голосов
/ 02 января 2011

У меня есть текстовый файл.Мне нужно получить список предложений.

Как это можно реализовать?Есть много тонкостей, таких как точка, используемая в сокращениях.

Мой старый регулярное выражение работает плохо.

Ответы [ 10 ]

128 голосов
/ 02 января 2011

Инструментарий естественного языка ( 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))

(я не пробовал!)

78 голосов
/ 19 июля 2015

Эта функция может разбить весь текст Гекльберри Финна на предложения примерно за 0,1 секунды и обрабатывает многие из наиболее болезненных крайних случаев, которые делают синтаксический анализ предложения нетривиальным, например. " Мистер Джон Джонсон-младший родился в США, но получил докторскую степень в Израиле, прежде чем присоединиться к Nike Inc. в качестве инженера. Он также работал на craigslist.org в качестве бизнес-аналитика. "

# -*- coding: utf-8 -*-
import re
alphabets= "([A-Za-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" + alphabets + "[.] "," \\1<prd> ",text)
    text = re.sub(acronyms+" "+starters,"\\1<stop> \\2",text)
    text = re.sub(alphabets + "[.]" + alphabets + "[.]" + alphabets + "[.]","\\1<prd>\\2<prd>\\3<prd>",text)
    text = re.sub(alphabets + "[.]" + alphabets + "[.]","\\1<prd>\\2<prd>",text)
    text = re.sub(" "+suffixes+"[.] "+starters," \\1<stop> \\2",text)
    text = re.sub(" "+suffixes+"[.]"," \\1<prd>",text)
    text = re.sub(" " + alphabets + "[.]"," \\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
23 голосов
/ 30 октября 2017

Вместо использования регулярных выражений для разбиения текста на предложения вы также можете использовать библиотеку 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.']

ref: https://stackoverflow.com/a/9474645/2877052

8 голосов
/ 22 января 2015

Вот середина дорожного подхода, который не зависит от каких-либо внешних библиотек. Я использую списочное понимание, чтобы исключить совпадения между аббревиатурами и терминаторами, а также исключить перекрытия между вариациями завершений, например: '. против "." "

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

5 голосов
/ 10 января 2018

Вы можете попробовать использовать Spacy вместо регулярных выражений. Я использую это, и это делает работу.

import spacy
nlp = spacy.load('en')

text = '''Your text here'''
tokens = nlp(text)

for sent in tokens.sents:
    print(sent.string.strip())
4 голосов
/ 02 января 2011

Для простых случаев (когда предложения обычно заканчиваются) это должно работать:

import re
text = ''.join(open('somefile.txt').readlines())
sentences = re.split(r' *[\.\?!][\'"\)\]]* *', text)

Регулярное выражение равно *\. +, что соответствует периоду, окруженному 0 или более пробелами слева и 1 илиправее (чтобы не считать период в re.split изменением предложения).

Очевидно, что это не самое надежное решение, но в большинстве случаев оно подойдет.Единственный случай, который не охватит это аббревиатуры (возможно, просмотрите список предложений и убедитесь, что каждая строка в sentences начинается с заглавной буквы?)

1 голос
/ 14 мая 2012

Нет сомнений, что НЛТК является наиболее подходящим для этой цели.Но начать работать с NLTK довольно больно (но как только вы его установите - вы просто пожинаете плоды)

Итак, вот простой пере-основанный код, доступный на 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 
1 голос
/ 28 января 2012

@ Артём,

Привет!Вы можете создать новый токенизатор для русского (и некоторых других языков), используя эту функцию:

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)

Удачи, Марилена.1009 *

0 голосов
/ 27 июня 2019

Вы также можете использовать функцию токенизации предложений в NLTK:

from nltk.tokenize import sent_tokenize
sentence = "As the most quoted English writer Shakespeare has more than his share of famous quotes.  Some Shakespare famous quotes are known for their beauty, some for their everyday truths and some for their wisdom. We often talk about Shakespeare’s quotes as things the wise Bard is saying to us but, we should remember that some of his wisest words are spoken by his biggest fools. For example, both ‘neither a borrower nor a lender be,’ and ‘to thine own self be true’ are from the foolish, garrulous and quite disreputable Polonius in Hamlet."

sent_tokenize(sentence)
0 голосов
/ 15 марта 2018

Мне пришлось читать файлы субтитров и разбивать их на предложения.После предварительной обработки (например, удаления информации о времени и т. Д. В файлах .srt) переменная 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;

О!Что ж.Теперь я понимаю, что, поскольку мой контент был испанским, у меня не было проблем с «мистером Смитом» и т. Д. Тем не менее, если кто-то хочет быстрый и грязный парсер ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...