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))