Пока собираюсь с мыслями по поводу написания одной большой статьи про использование Ribbon Controls решил между делом поразбираться с API Яндекс.Спеллера в Delphi.
Во-первых, следует объяснить тем, кто не в курсе, что это за API.
Яндекс.Спеллер – это сервис проверки правописания, который помогает находить и исправлять орфографические ошибки. Работа сервиса основана на использовании орфографических словарей для трех языков — русского, украинского и английского.
В принципе, для тех, кто большую часть времени работает в оффлайне этот сервис особо не пригодиться. Другое дело — использовать его в своих онлайн проектах и программах, ориентированных на работу в Сети.
В целом, работа с API Яндекса никогда не вызывали, по крайней мере у меня, особых осложнений. В основном вся работа основывается на работе с двумя-тремя методами, у которых пара-тройка входных параметров. Самое «сложное» — это правильно разобрать ответ сервера.
Судя по тому, что написано в документации к Яндекс.Спеллеру, для нас наиболее важно научиться использовать метод checkText.. Его мы сегодня и рассмотрим.
Метод checkText Яндекс.Спеллера
Метод проверяет орфографию в указанном отрывке текста. Для использования метода допустимо использовать как GET так и POST запросы. При этом размер передаваемого текста в POST-запросе не должен превышать 10000 символов.
Чтобы лишний раз не копипастить документацию я просто приведу ссылку на описание метода. Судя по тому, что сказано в документации, наша работа с сервисом будет состоять из двух частей:
- Отправка POST-запроса с текстом для проверки
- Получение ответа и парсинг XML.
Для работы нам понадобиться совсем немного модулей: SysUtils, Classes, XMLIntf,xmldom, XMLDoc;
Ну и для отправки запросов я использовал httpsend из библиотеки synapce. Вы, если хотите, можете использовать тот же IdHTTP из Indy.
Первое, что делаем — это объявляем все константы, которые могут потребоваться нам для работы, а именно:
- Коды ошибок Яндекс.Спеллера
- Константы настроек Яндекс.Спеллера
- Ну и, чтобы вообще было хорошо — создадим несколько ресурсных строк для описания ошибок
У меня получилось следующее:
resourcestring rcERROR_TEXT_LENGTH = 'Общая длина передаваемого текста превышает 10000 символов!'; rcERROR_UNKNOWN_WORD = 'Слова нет в словаре'; rcERROR_REPEAT_WORD = 'Повтор слова'; rcERROR_CAPITALIZATION = 'Неверное употребление прописных и строчных букв'; rcERROR_TOO_MANY_ERRORS = 'Текст содержит слишком много ошибок'; const IGNORE_UPPERCASE = 1; IGNORE_DIGITS = 2; IGNORE_URLS = 4; FIND_REPEAT_WORDS = 8; IGNORE_LATIN = 16; NO_SUGGEST = 32; FLAG_LATIN = 128; ERROR_UNKNOWN_WORD = 1; ERROR_REPEAT_WORD = 2; ERROR_CAPITALIZATION = 3; ERROR_TOO_MANY_ERRORS = 4;
Теперь договоримся как будем хранить сообщения об орфографических ошибках. Чтобы все было точно как задумано в сервисе, будем хранить всю информацию об ошибке как есть. Для этого вводим новый тип данных:
type TBadWord = record Word: string;//слово с ошибкой ErrCode: integer;//код ошибки ErrMsg : string;//описание ошибки Pos:integer;//позиция слова с ошибкой (отсчет от 0); Row:integer;//номер строки (отсчет от 0) Col:integer;//номер столбца (отсчет от 0); Len:integer;//длина слова с ошибкой Variants: TStringList; //варианты правильного написания end;
И ещё пару типов
type TBadWords = array of TBadWord; type TTextFormat = (tfPlain, tfHTML);
Теперь можно отправлять данные на сервер и анализировать ответ. Для этого я создал такую функцию:
function CheckGrammar(const aText:string; Options:integer=0;lang:string='ru,en';format:TTextFormat=tfPlain):TBadWords; implementation function CheckGrammar(const aText:string; Options:integer;lang:string;format:TTextFormat):TBadWords; var Query: TStringStream; Doc: IXMLDocument; List,Variant: IDOMNodeList; i,j:integer; begin try Query:=TStringStream.Create(''); Query.WriteString('text='+aText+'?='+lang+'&options='+inttostr(Options)); case format of tfPlain: Query.WriteString('&format=plain&ie=1251'); tfHTML: Query.WriteString('&format=html&ie=1251'); end; if Length(Query.DataString)>10000 then raise Exception.Create(rcERROR_TEXT_LENGTH); with THTTPSend.Create do begin Document.LoadFromStream(Query); MimeType:='application/x-www-form-urlencoded'; if HTTPMethod('POST','http://speller.yandex.net/services/spellservice/checkText') then begin Doc:=NewXMLDocument(); Doc.LoadFromStream(Document); List:=Doc.DOMDocument.getElementsByTagName('error');//получаем список ошибок SetLength(Result,List.Length);//устанавливаем длину массива for i:=0 to List.length-1 do begin //запоминаем атрибуты ошибки with List.item[i] do begin Result[i].ErrCode:=StrToInt(attributes.getNamedItem('code').nodeValue); Result[i].Pos:=StrToInt(attributes.getNamedItem('pos').nodeValue); Result[i].Row:=StrToInt(attributes.getNamedItem('row').nodeValue); Result[i].Col:=StrToInt(attributes.getNamedItem('col').nodeValue); Result[i].Len:=StrToInt(attributes.getNamedItem('len').nodeValue); end; case Result[i].ErrCode of ERROR_UNKNOWN_WORD:Result[i].ErrMsg:=rcERROR_UNKNOWN_WORD; ERROR_REPEAT_WORD:Result[i].ErrMsg:=rcERROR_REPEAT_WORD; ERROR_CAPITALIZATION:Result[i].ErrMsg:=rcERROR_CAPITALIZATION; ERROR_TOO_MANY_ERRORS:Result[i].ErrMsg:=rcERROR_TOO_MANY_ERRORS; end; //читаем дочерние элементы Variant:=List.item[i].childNodes; Result[i].Word:=(List.item[i].firstChild as IDOMNodeEx).text; Result[i].Variants:=TStringList.Create; for j:=1 to Variant.length-1 do //оставшиеся дочерние узлы - варианты Result[i].Variants.Add((Variant.item[j]as IDOMNodeEx).text); end; end; end; finally FreeAndNil(Query); end; end;
Разберемся, что здесь происходит. Вначале создается тело нашего POST-запроса в зависимости от входных параметров. При этом, обратите внимание на последний необязательный параметр: ie=1251. Можно было бы обойтись и без него, но для этого необходимо переводить наш параметр aText в кодировку utf-8.
Далее проводится проверка длины текста в теле запроса. Если длина превышает 10000 символов, то работа функции завершается, иначе переходим к следующему шагу — отправке запроса.
MimeType:='application/x-www-form-urlencoded';
Определяет заголовок (Headers) Content-Type. Если не использовать заголовок или использовать другое его значение — сервис вернет нам ошибку «Unrecognized Content Type … «. Отправляем запрос и анализируем XML-документ.
Анализ как таковой проводится элементарно:
- Получаем список всех узлов error — размер списка и будет размером массива, который возвращает функция
- Далее проходим по порядку по каждому из узлов списка (цикл по i) и читаем все атрибуты попутно занося в элемент массива описание ошибки в зависимости от значения атрибута code.
- Получаем для каждого узла List.item[i] список дочерних узлов.
- Исходя из описания API первым дочерним узлом всегда является слово с ошибкой — читаем первый дочерний узел и заносим его значение в поле Word
- Остальные дочерние узлы (если они имеются) — это варианты правильного написания слова. Создаем цикл по j и читаем значения оставшихся узлов.
Вот и все. Остается продемонстрировать работу функции на примере. Использовать можно, например так:
CheckGrammar('синхрафазатрон в дубне', 5);
При этом проверка будет проходить с использованием опций: IGNORE_UPPERCASE и IGNORE_URLS.
Можно вызвать метод и так:
CheckGrammar('синхрафазатрон в дубне', IGNORE_UPPERCASE+ IGNORE_URLS, 'ru');
Так будет проверяться только русский текст, опять же используя опции из предыдущего примера.
[…] This post was mentioned on Twitter by progg, Vlad and ru_webdev, monitorium. monitorium said: API Яндекс.Спеллера в Delphi. | Delphi в Internet: Встраиваем он-лайн проверку орфографии в наше прилож… http://bit.ly/5v7se6 /*progg.ru […]
Получается, что я Вас проэкслуатировал.
Я сам пытался решить эту задачу, залез в поисках решения на этот блог.
Сообщил, что эта задача мне интересна.
И вот оно решение!
Спасибо огромное!
У меня всего час в неделю на программирование, и узучить XML на практике не успеваю.
Ещё раз спасибо.
С уважением, Гимаев Наиль.
:) Да как бы эксплуатации никакой не было — просто решил узнать, что Вас так заинтересовало в Яндекс.Спеллере. А прятать за пазухой решение и потом продавать — не в моих целях и планах. Пользуйтесь на здоровье. Будут проблемы с работой модуля — говорите — будем исправлять
А для Spell-checker-a Гугла решение, случайно, не планируется? ;-)
Всё-таки Гугл поддерживает намного больше языков.
Для этого не нужно быть гуглом.
Возможно в ближайшее время появится подражатель яндекса, который сделает сайт с поддержкой этого API. Прикрутит к нему HSpell (самый популярный на данный момент). Словари для всех языков можно скачать с сайта OpenOffice. Тогда все смогут пользоваться проверкой текстов на всех языках. Для профессионального веб-программиста работа на один день.
Гимаев Наиль, спасибо за идею! Мне вдруг пришло в голову, что тогда проще использовать этот Spellchecker напрямую. =)
Алексей, я сейчас, признаться, вообще мало чего планирую, кроме работы на 2х работах :) Но ежели дадите ссылочку на API от Гугла, то в свободное от работы время (думаю скоро оно появится) поковыряюсь с их чекером…тем более, что в последнее время меня частенько пробивает поковыряться во всяких API
Я погорячился. Api для Spellchecker-a гугла не опубликовано официально и не документировано.
А неофициальная версия есть?)
Есть, конечно. Народ расковырял Google Toolbar, чтобы посмотреть как там реализована проверка правописания. =) Но толку-то? На такое решение нельзя полагаться — если протокол неофициальный, то он может быть изменён в любой момент. Даже GoogleChrome использует для проверки правописания не сервисы гугла, а вышеупомянутый hunspell.
Но если очень хочется поковыряться с API — то, вот идея. Для Google API ещё нет полной бесплатной библиотеки для Delphi. Есть решения и примеры для работы с отдельными сервисами, в основном через публичные методы. А полного API c авторизацией и поддержкой всех методов нет. С другой стороны, раз нет, значит никому и не нужно. =)
Ну на полное API Google как бэ действительно всем пофег, т.к. есть куча «прибамбасов», которые в программах на Delphi будут выглядеть как у собаки пятая лапа, например. А так, что-то вздумалось спаять на досуге Яндекс.Спеллер+Google.Translate…ну и ещё чё-нить до кучи :)
Посмотрел по ссылке пост…что-то мне этот чеккер очень сильно напомнил Спеллер :) Ждем поддержки тучи языков от Яндекса…
Совсем полный — да, пофиг. Мне, например, пофиг APIAdsense, AdWords, Analytics, Apps и многие другие. А интересны Calendar API (или любой способ для работы с Google Tasks), Translate API, Google Static Maps API.
Google Calendar API, наверняка потребует реализовать Google Account Authentication и Google Data Protocol, а имея удобные базовые классы — по идее можно будет легко прикрутить и поддержку других сервисов. Но это так… мысли вслух. =)
Никогда не задумывался, но AdWords — хороший способ превратить бесплатную но популярную программу в приносящую доход. Нужно только подключить рекламу от гугла к своей программе, и тогда популярность можно будет вернуть деньгами.