python

Работа с иностранными текстами. Как увеличить процент понимания и выучить язык?

  • среда, 28 мая 2014 г. в 03:10:36
http://habrahabr.ru/post/224301/

По жизни или по работе иногда приходится сталкиваться с текстами на иностранном языке, знания которого еще далеки от совершенства. Чтобы прочесть и понять, о чем идет речь (и, в лучшем случае, выучить несколько новых слов), я обычно использовал два варианта. Первый — это перевод текста в браузере, второй — перевод каждого слова по отдельности с помощью, например, ABBYY Lingvo. Но у этих методов есть множество недостатков. Во-первых, браузер предлагает перевод предложениями, а значит, он может менять порядок слов и перевод может оказаться еще более непонятным, чем оригинальный текст. Во-вторых, браузер не предлагает ни альтернативных вариантов перевода, ни синонимов к словам, а значит, учить новые слова становится проблематично. Другие варианты и синонимы можно получить при поиске конкретного слова в переводчике, а это требует некоторого времени, особенно если таких слов много. Наконец, читая текст, мне хотелось бы знать, какие слова наиболее популярны в этом языке, чтобы я мог их запомнить и потом использовать в своей письменной или разговорной речи.

Я подумал, что иметь под рукой подобный «переводчик» было бы неплохо, и поэтому решил реализовать его на python. Всех, кого заинтересовало, прошу под кат.

Подсчет слов


При написании программы я руководствовался следующей логикой. Сначала необходимо переписать весь текст строчными буквами, убрать ненужные знаки и символы (.?! и т.д.) и подсчитать, сколько раз каждое слово встречается в тексте. Вдохновленный кодом от Google, я сделал это без малейшего труда, но полученные результаты решил записать в несколько ином виде, а именно {1: [group of words that is with frequency 1], 2: [--//-- with frequency 2], etc.}. Это удобно, если требуется сортировка в том числе и внутри каждой группы слов, например, если мы хотим, чтобы слова шли в том же порядке, как в тексте. Итого, я хочу получить двойную сортировку: чтобы в начале шли наиболее часто встречающиеся слова, а если они встречаются с одинаковой частотой, то чтобы они были упорядочены в соответствии с исходным текстом. Эта идея получила отражение в следующем коде.

def word_count_dict(filename, dictList=de500):   
    count = {}
    txt = re.sub('[,.!?":;()*]', '',
            open(filename, 'r').read().lower())
    words = txt.split()
    for word in words:
      if not word in count:
        count[word] = 1
      else:
        count[word] += 1
    return {i: sorted([w for w in count
               if (count[w]==i and w not in dictList.values())],
               key=lambda x: txt.index(x)) for i in set(count.values())}

Отлично, все работает, как и хотелось, но есть подозрение, что в топе списка будут вспомогательные слова (типа the) и другие, перевод которых очевиден (например, you). Избавиться от них можно, создав специальный список наиболее употребляемых слов, чтобы при формировании словаря исключать все слова, которые находятся в этом списке. Почему это еще удобно? Потому что, выучив нужное слово, мы можем добавить его в список, и соответствующий перевод больше не будет показан. Обозначим список переменной dictList и забудем о нем на некоторое время.

Перевод слов


Потратив несколько минут на поиски удобного онлайн-переводчика, решено было проверить в действии Google и Yandex. Так как ровно 3 года и 1 день назад Google закрыл Translate API, то будем использовать обходной вариант, предложенный WNeZRoS. В ответе на запрос того или иного слова Google предлагает перевод, альтернативные варианты перевода и их обратный перевод (то есть, синонимы). Использование Yandex'a как обычно требует получения ключа, и в ответе на запрос можно найти не только перевод, но и примеры, и наверное, еще что-то. В обоих случаях ответ будет содержать список в формате json, довольно простой у Google, и несколько усложненный у Yandex. По этой причине, а также потому, что Google знает больше языков (и зачастую слов), решено было остановиться именно на нем.

Запросы будем отправлять с помощью замечательной библиотеки grab, а ответы записывать во вспомогательный текстовый файл (dict.txt). В нем попробуем найти основной перевод, альтернативные варианты и синонимы, и если они есть, напечатать их. Сделаем так, чтобы последние две опции можно было отключить. Соответствующий код будет выглядеть следующим образом.

def tranlsate(word, key, lan1='de', lan2='ru', alt=True, syn=True):
    g = Grab(log_file = 'dict.txt')
    link = 'http://translate.google.ru/translate_a/t?client=x&text='\
           + word + '&sl=' + lan1 + '&tl=' + lan2
    g.go(link)
    data = json.load(open('dict.txt'))
    translation, noun, alternatives, synonims = 0, 0, 0, 0
    try:
        translation = data[u'sentences'][0][u'trans']
        noun = data[u'dict'][0][u'pos']
        alternatives = data['dict'][0]['terms']
        synonims = data['dict'][0]['entry'][0]['reverse_translation']
    except:
        pass
    if lan1=='de' and noun==u'имя существительное':
        word = word.title()
    if translation:
        print ('['+str(key)+']', word, ': ', translation)
        if alt and alternatives:
            [print (i, end=', ') for i in alternatives]
            print ('\r')
        if syn and synonims:
            [print (i.encode('cp866', errors='replace'), end=', ')
                                     for i in synonims]
            print ('\n')

Как можно заметить, дефолтный перевод настроен с немецкого на русский. Переменная key соответствует частоте слова в тексте. Ее будем передавать из другой функции, которая и будет вызывать перевод для каждого слова.

Вызов функции перевода


Здесь все просто: я хочу получить группы слов с соответствующей частотой в виде словаря (функция word_count_dict) и найти перевод каждого слова (функция tranlsate). Также я хочу, чтобы были показаны только первые n групп наиболее употребляемых слов.

def print_top(filename, n=100):
    mydict = word_count_dict(filename)
    mydict_keys = sorted(mydict, reverse=True)[0:n]
    [[tranlsate(word, key) for word in mydict[key]] for key in mydict_keys]


Список наиболее употребляемых слов


Отлично, программа практически готова, осталось только составить список наиболее употребляемых слов. Их легко найти в интернете, и я составил список 50, 100 и 500 наиболее употребляемых слов в немецком языке и записал его в отдельный файл, чтобы не засорять код.

Если кто-то захочет составить подобный список в английском или другом языке, я буду благодарен, если он или она поделится им, чтобы я мог добавить его в свой.

Предварительные результаты


Запустив программу, можно получить результаты приблизительно в следующем виде:

[частота повторения слова] слово: перевод
альтернативный перевод,
синонимы


Хорошо, код написан, программа работает, однако насколько это удобно и эффективно? Чтобы попытаться ответить на этот вопрос, я взял пару текстов на немецком языке для проверки.

Первая статья из Deutsche Welle посвящена теме финансирования добычи угля Deutsche Bank рядом с Австралией. Статья содержит 498 слов, из которых 15 наиболее часто встречаемых в тексте (воспользуемся списком 50 наиболее употребляемых немецких слов для исключения) соответствуют 16.87% всего текста. Грубо говоря, это означает, что если предположить, что человек не знает этих слов, то после прочтения перевода 6.67% всех слов, встречающихся в тексте, его уровень понимания увеличится почти на 17% (если измерять уровень понимания только количеством знакомых слов в тексте). На первый взгляд довольно неплохо.

Вторая статья из Spiegel рассказывает о том, как немецкий биржевой индекс DAX отреагировал на победу Порошенко в выборах президента на Украине (да-да, он вырос). Статья содержит 252 слова, из которых 8 наиболее встречаемых (6.06%) аналогично соответствуют 11.9% текста.

Кроме того, следует заметить, что если переводимый текст достаточно короткий, чтобы каждое слово встречалось только один раз (например, сообщение, полученное по электронной почте), то следовать предложенному переводу в том же порядке, как слова идут в тексте, весьма удобно.

Звучит красиво (es klingt schön), однако это очень грубые тесты, поскольку я ввел слишком много предпосылок. Думаю, что проверить, насколько эта идея может облегчить работу с текстами на иностранном языке, возможно лишь при некотором регулярном использовании этой программы, что, к сожалению, пока не очень удобно. Для того чтобы перевести текст нужно скопировать его сначала в .txt файл и присвоить имя файла переменной filename, а затем запустить функцию print_top.

Чего не хватает?


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

Во-первых, как только что было сказано, удобства. Код использовать неудобно — нужно копировать текст, + зависимость от python и библиотеки grab. Что делать? Как вариант, написать расширение для браузера, чтобы можно было выбирать определенный элемент на странице (например, аналогично тому, как это реализовано в Reedy) и получать его перевод. Во-вторых, списка слов для исключения наиболее употребляемых на других языках. Наконец, возможны различные косяки с кодировками.

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

Весь код целиком можно найти под спойлером, а также на github.

Исходный код
# -*- coding: utf-8-sig -*-

from __future__ import print_function
import re
import json
from pprint import pprint
from grab import Grab

from dictDe import *

def tranlsate(word, key, lan1='de', lan2='ru', alt=True, syn=True):
    """Prints the number of counts, word, translation, and example
    from lan1 to lan2 according to Translate.Google."""
    # First, write down a translation in some auxiliary txt file
    # and load it in json format
    g = Grab(log_file = 'dict.txt')
    link = 'http://translate.google.ru/translate_a/t?client=x&text='\
           + word + '&sl=' + lan1 + '&tl=' + lan2
    g.go(link)
    data = json.load(open('dict.txt'))
    # Then, let's try to get all the necessary elements in json
    translation, noun, alternatives, synonims = 0, 0, 0, 0
    try:
        translation = data[u'sentences'][0][u'trans']
        noun = data[u'dict'][0][u'pos']
        alternatives = data['dict'][0]['terms']
        synonims = data['dict'][0]['entry'][0]['reverse_translation']
    except:
        pass
    # German nouns should begin with capital letter
    if lan1=='de' and noun==u'имя существительное':
        word = word.title()
    # Finally, print out counts, word, translation with alternatives
    # and synonims, if applicable. Encoding is added up to allow
    # printing in cmd if you have a russian version of Windows
    if translation:
        print ('['+str(key)+']', word, ': ', translation)
        if alt and alternatives:
            [print (i, end=', ') for i in alternatives]
            print ('\r')
        if syn and synonims:
            [print (i.encode('cp866', errors='replace'), end=', ')
                                     for i in synonims]
            print ('\n')


def word_count_dict(filename, dictList=de50):
    """Returns a dictionary with key being number of counts
    and value being a list of words with that key.
    dictList is an optional argument: it is to eliminate
    the most common words. Default is the dictionary of
    the 50 most common German words"""
    
    count = {}
    txt = open(filename, 'r').read().lower()
    txt = re.sub('[,.!?":;()*]', '', txt)
    words = txt.split()
    for word in words:
      if not word in count:
        count[word] = 1
      else:
        count[word] += 1
    return {i: sorted([w for w in count
               if (count[w]==i and w not in dictList.values())],
               key=lambda x: txt.index(x)) for i in set(count.values())}


def print_top(filename, n=100):
    """Generates the top count groups for the given file.
    Default number equals 10. Drop reverse if you want
    to print the less frequent words in the text."""
    
    mydict = word_count_dict(filename)
    mydict_keys = sorted(mydict, reverse=True)[0:n]
    [[tranlsate(word, key) for word in mydict[key]] for key in mydict_keys]


filename = 'dictext.txt'
print (print_top(filename))