Нормализация адресов с помощью сервиса DaData.ru

Программирование - Практика программирования

Вариант решения проблемы нормализации адресов с помощью стороннего сервиса DaData.ru

Наверное, каждый программист рано или поздно сталкивается с пробемой нормализации адресов, которые были введены пользователями в произвольном или "частично произвольном" формате. И здесь нет какой-либо серебряной пули, люди пишут множество различных проверок, замен подстрок и т.д., в конце концов, можно отредактировать записи вручную или заставить пользователей это сделать. Но если количество адресов достаточно большое и измеряется десятками и сотнями тысяч становится совсем грустно... Как один из вариантов решения данной проблемы - использование сторонних сервисов (например, Яндекс Геокодер и прочие). В нашем случае, был выбран сервис DaData.ru по причине его тесной связи с КЛАДР и ФИАС, кроме того, сервис показал хорошие результаты - из примерно 84 000 адресов, около 4 000 были помечены как требующие ручной проверки, из них 20% были корректными, а остальные, в большинстве случаев, были заполнены изначально неправильно (например, указан только город  и всё).

Единственный существенный недостаток сервиса - то, что он платный, стоимость стандартизации одного адреса составляет 5-10 коп. (https://dadata.ru/pricing/), 100 адресов даются бесплатно после регистрации. Важное уточнение - стандартизируются только российские адреса.

DaData имеет очень простой в использовании API - https://dadata.ru/api/clean/ .

Первое, что нужно сделать - зарегистрироваться в сервисе и получить API-Ключ и Секретный ключ. 

Весь код нормализации адресов:

Процедура НормализоватьАдреса()
	КлючиДоступа = Новый Структура;
	КлючиДоступа.Вставить("APIКлюч"      , "<Ваш API-Ключ>");
	КлючиДоступа.Вставить("СекретныйКлюч", "<Ваш Секретный ключ>");
	
	ТаблицаДанных = ПолучитьСтруктуруТаблицыАдресов();
	
    // заполнение таблицы исходными адресами
	ДобавитьАдресДляНормализации(ТаблицаДанных, "мск сухонска 11/-89");
	ДобавитьАдресДляНормализации(ТаблицаДанных, "москва Сухонская улица 11 89");
	
	НормализоватьАдресаТаблицы(ТаблицаДанных, КлючиДоступа);
    // TODO: дальнейшая работа с ТаблицаДанных
КонецПроцедуры


Функция ПолучитьСтруктуруТаблицыАдресов()
	ТаблицаДанных = Новый ТаблицаЗначений;
	ТаблицаДанных.Колонки.Добавить("ИсходныйАдрес");
	
	// Поля таблицы, совпадающие по имени с полями ответа сервиса.
	ТаблицаДанных.Колонки.Добавить("result");                   // Нормализованный адрес
	ТаблицаДанных.Колонки.Добавить("postal_code");              // Индекс
	ТаблицаДанных.Колонки.Добавить("region_with_type");         // Регион
	ТаблицаДанных.Колонки.Добавить("city_with_type");           // Город
	ТаблицаДанных.Колонки.Добавить("settlement_with_type");     // Населенный пункт
	ТаблицаДанных.Колонки.Добавить("city_district_with_type");  // Район
	ТаблицаДанных.Колонки.Добавить("street_with_type");         // Улица
	ТаблицаДанных.Колонки.Добавить("house");                    // Дом
	ТаблицаДанных.Колонки.Добавить("flat");                     // Квартира
	ТаблицаДанных.Колонки.Добавить("qc");                       // Код проверки	 
	
	Возврат(ТаблицаДанных);
КонецФункции

Процедура ДобавитьАдресДляНормализации(ТаблицаДанных, Адрес)
	Строка = ТаблицаДанных.Добавить();
	Строка.ИсходныйАдрес = Адрес;
КонецПроцедуры

Процедура НормализоватьАдресаТаблицы(ТаблицаДанных, КлючиДоступа)
	Для Каждого СтрокаАдреса Из ТаблицаДанных Цикл 
		ОтветСервиса = ЗапросСервисаНормализации(СтрокаАдреса.ИсходныйАдрес, КлючиДоступа);
		
		Чтение = Новый ЧтениеJSON;
		Чтение.УстановитьСтроку(ОтветСервиса);
		Данные = ПрочитатьJSON(Чтение)[0];
		Чтение.Закрыть();
		
		ЗаполнитьЗначенияСвойств(СтрокаАдреса, Данные);
	КонецЦикла;
КонецПроцедуры
	
Функция ЗапросСервисаНормализации(Адрес, КлючиДоступа)
	Заголовки = Новый Соответствие;
	Заголовки.Вставить("Content-Type" , "application/json");
	Заголовки.Вставить("Authorization", "Token " + КлючиДоступа.APIКлюч);
	Заголовки.Вставить("X-Secret"     , КлючиДоступа.СекретныйКлюч);
	
	Запрос = Новый HTTPЗапрос("/api/v2/clean/address", Заголовки);
	Запрос.УстановитьТелоИзСтроки("[""" + Адрес + """]", КодировкаТекста.UTF8, 
								  ИспользованиеByteOrderMark.НеИспользовать);
	Соединение = Новый HTTPСоединение("dadata.ru", 443,,,,,
	                                  Новый ЗащищенноеСоединениеOpenSSL(Неопределено, Неопределено),);
	Ответ = Соединение.ОтправитьДляОбработки(Запрос);
	ОтветСервера = Ответ.ПолучитьТелоКакСтроку(КодировкаТекста.UTF8);
		
	Возврат(ОтветСервера);
КонецФункции

Приведенный код будет работать на платформе 8.3.6 и более, поскольку в нем применены функции для работы с JSON (http://v8.1c.ru/o7/201410json/). Если версия платформы меньше - нужно использовать сторонний парсер.

Описание работы кода:

Основная функция в которой происходит заполнение таблицы адресов и их нормализация - НормализоватьАдреса.

Создается таблица значений, в которой будут хранится исходные и обработанные адреса  (функция ПолучитьСтруктуруТаблицыАдресов). Следует заметить, что в эту таблицу можно добавить дополнительные поля, имена которых совпадают с именами свойств возвращаемого JSON-объекта - https://dadata.ru/api/clean/#response , а данных в ответе очень много, вплоть до площади квартиры и ее стоимости.  

Далее, с помощью процедуры ДобавитьАдресДляНормализации заполняем таблицу исходными адресами.

Последнее, что нужно сделать - вызвать процедуру НормализоватьАдресаТаблицы и передать ей в качестве аргументов таблицу с исходными адресами и ключи доступа к сервису.

Для каждой строки, указанной в таблице делается POST-запрос (https://dadata.ru/api/v2/clean/address) , в заголовках указываются ключи доступа, в теле запроса - исходный адрес, а в качестве результата сервис возвращает JSON-массив с одним элементом, в свойствах которого содержится вся необходимая информация.

Вот и всё. За бортом остались: обработка всевозможных ошибок, хранение адресов.

См. также

Комментарии
1. Ivan Kuznietsov (Ivon) 610 15.03.17 11:43 Сейчас в теме
2. Easy Way (EasyWay) 15.03.17 14:06 Сейчас в теме
(1) - да, но вообще нет.
Яндекс плохо дружит с "лишней" информацией в адресе - всякие "домофоны", "этажи" и прочее, не являющееся географическими именованиями.
Дадата в этом смысле более интеллектуальна.
Оставьте свое сообщение