уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.

Пока собираюсь с мыслями по поводу написания одной большой статьи про использование Ribbon Controls решил между делом поразбираться с API Яндекс.Спеллера в Delphi.

Во-первых, следует объяснить тем, кто не в курсе, что это за API.

Яндекс.Спеллер – это сервис проверки правописания, который помогает находить и исправлять орфографические ошибки. Работа сервиса основана на использовании орфографических словарей для трех языков — русского, украинского и английского.

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

В целом, работа с API Яндекса никогда не вызывали, по крайней мере у меня, особых осложнений. В основном вся работа основывается на работе с двумя-тремя методами, у которых пара-тройка входных параметров. Самое «сложное» — это правильно разобрать ответ сервера.

Судя по тому, что написано в документации к Яндекс.Спеллеру, для нас наиболее важно научиться использовать метод checkText.. Его мы сегодня и рассмотрим.

Метод checkText Яндекс.Спеллера

Метод проверяет орфографию в указанном отрывке текста. Для использования метода допустимо использовать как GET так и POST запросы. При этом размер передаваемого текста в POST-запросе не должен превышать 10000 символов.

Чтобы лишний раз не копипастить документацию я просто приведу ссылку на описание метода.  Судя по тому, что сказано в документации, наша работа с сервисом будет состоять из двух частей:

  1. Отправка POST-запроса с текстом для проверки
  2. Получение ответа и парсинг 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-документ.

Анализ как таковой проводится элементарно:

  1. Получаем список всех узлов error — размер списка и будет размером массива, который возвращает функция
  2. Далее проходим по порядку по каждому из узлов списка (цикл по i) и читаем все атрибуты попутно занося в элемент массива описание ошибки в зависимости от значения атрибута code.
  3. Получаем для каждого узла List.item[i] список дочерних узлов.
  4. Исходя из описания API первым дочерним узлом всегда является слово с ошибкой — читаем первый дочерний узел и заносим его значение в поле Word
  5. Остальные дочерние узлы (если они имеются) — это варианты правильного написания слова. Создаем цикл по j и читаем значения оставшихся узлов.

Вот и все. Остается продемонстрировать работу функции на примере. Использовать можно, например так:

CheckGrammar('синхрафазатрон в дубне', 5);

При этом проверка будет проходить с использованием опций: IGNORE_UPPERCASE и IGNORE_URLS.

Можно вызвать метод и так:

CheckGrammar('синхрафазатрон в дубне', IGNORE_UPPERCASE+ IGNORE_URLS, 'ru');

Так будет проверяться только русский текст, опять же используя опции из предыдущего примера.

Скачать исходник: Исходники —> API онлайн-сервисов —> Яндекс API
0 0 голоса
Рейтинг статьи
уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.
Подписаться
Уведомить о
13 Комментарий
Межтекстовые Отзывы
Посмотреть все комментарии
trackback

[…] 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 […]

Гимаев Наиль
Гимаев Наиль
20/01/2010 10:26

Получается, что я Вас проэкслуатировал.
Я сам пытался решить эту задачу, залез в поисках решения на этот блог.
Сообщил, что эта задача мне интересна.
И вот оно решение!
Спасибо огромное!
У меня всего час в неделю на программирование, и узучить XML на практике не успеваю.
Ещё раз спасибо.
С уважением, Гимаев Наиль.

Алексей Тимохин

А для Spell-checker-a Гугла решение, случайно, не планируется? ;-)
Всё-таки Гугл поддерживает намного больше языков.

Гимаев Наиль
Гимаев Наиль
23/01/2010 21:11

Для этого не нужно быть гуглом.
Возможно в ближайшее время появится подражатель яндекса, который сделает сайт с поддержкой этого API. Прикрутит к нему HSpell (самый популярный на данный момент). Словари для всех языков можно скачать с сайта OpenOffice. Тогда все смогут пользоваться проверкой текстов на всех языках. Для профессионального веб-программиста работа на один день.

Алексей Тимохин

Гимаев Наиль, спасибо за идею! Мне вдруг пришло в голову, что тогда проще использовать этот Spellchecker напрямую. =)

Алексей Тимохин

Я погорячился. Api для Spellchecker-a гугла не опубликовано официально и не документировано.
 

Алексей Тимохин

Есть, конечно. Народ расковырял Google Toolbar, чтобы посмотреть как там реализована проверка правописания. =) Но толку-то? На такое решение нельзя полагаться — если протокол неофициальный, то он может быть изменён в любой момент.  Даже GoogleChrome использует для проверки правописания не сервисы гугла, а вышеупомянутый hunspell.
 
Но если очень хочется поковыряться с API — то, вот идея. Для Google API ещё нет полной бесплатной библиотеки для Delphi. Есть решения и примеры для работы с отдельными сервисами, в основном через публичные методы. А полного API c авторизацией и поддержкой всех методов нет. С другой стороны, раз нет, значит никому и не нужно. =)

Алексей Тимохин

Совсем полный — да, пофиг.  Мне, например, пофиг APIAdsense, AdWords, Analytics, Apps и многие другие. А интересны Calendar API (или любой способ для работы с Google Tasks), Translate API, Google Static Maps API.
Google Calendar API, наверняка потребует реализовать Google Account Authentication и Google Data Protocol, а имея удобные базовые классы — по идее можно будет легко прикрутить и поддержку других сервисов. Но это так… мысли вслух. =)
 

Гимаев Наиль
Гимаев Наиль
25/01/2010 12:24

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