Как определить оператора и регион по номеру телефона



    Полезно знать, какого телефонного оператора выбрал клиент и в каком регионе. Тогда можно разбить клиентскую базу географически и не звонить ночами. Или проводить промоакции вместе с операторами. А некоторые наши заказчики экономят: для каждого оператора выбирают самого дешевого СМС-агрегатора.

    Поэтому «Дадата» с давних пор находит оператора по номеру телефона. Алгоритм простой — сделай и пользуйся. Разве что в 2013 году, когда отменили «мобильное рабство», пришлось чуть скорректировать процесс. В этой статье расскажу, как у нас все работает.

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

    Разбить телефонный номер на части


    Первое, что нужно сделать, — разобрать телефон на три части:

    • код страны — для России это +7 или 8;
    • код оператора — 3–5 цифр (на самом деле это не код оператора, а код зоны нумерации. Дальше расскажу);
    • собственно номер телефона — 5–7 цифр.


    Код зоны нумерации у мобильного номера всегда состоит из трех цифр. У стационарного — из трех, четырех или пяти. Чтобы определить регион по номеру телефона, в любом случае достаточно трех первых цифр

    Если не знать, какая часть номера из каких цифр состоит, дальше будет непросто. Выход — поставить маску, чтобы пользователи сразу вводили номера по частям. Или разбирать уже после, с задачей справится библиотека Google для работы с телефонами.

    Найти оператора по номеру телефона, заглянув в план нумерации


    Главный российский документ, согласно которому операторы получают телефонные номера, — это план нумерации Россвязи. В нем ясно прописали все расклады: какие коды и номера каким операторам в каких регионах принадлежат. Что приятно, справочник Россвязи свободно лежит на странице rossvyaz.ru/deyatelnost/resurs-numeracii/vypiska-iz-reestra-sistemy-i-plana-numeracii.


    Справочник выложили сразу в двух форматах, что очень мило со стороны Россвязи. Обновляют раз в месяц

    Внутри плана нумерации четыре файла: три для стационарных номеров и один — для мобильных. Разница между содержимым файлов заметна, если посмотреть на префиксы в названиях:

    • «ABC-» означает, что в файле лежат стационарные номера. Собственно, «ABC» — обозначение зон нумерации, определенных по географическому принципу;
    • «DEF-» — это файл с мобильными номерами. Префикс означает зоны нумерации, определенные не географически.

    Уже здесь ясно: справочник подскажет не только оператора, но и тип телефона:

    • у стационарных код начинается с 3, 4 или 8;
    • у мобильных — с 9.

    Есть еще прямые мобильные — внешне их не отличить от стационарных. Знаю только один способ раскрыть прямой мобильный: найти его оператора и сравнить со списком мобильных операторов России. Но даже так без гарантии, потому что некоторые операторы мобильной связи в то же время — и операторы фиксированной.
    Номер +7 495 999-99-34 может быть прямым мобильным. А может быть, это офис покупает у сотового оператора фиксированную связь — знает только сам оператор.

    Но я отвлекся, вернемся к плану нумерации. Внутри файлов все очень просто — таблица на шесть полей.


    Порядок полей таков: ABC- или DEF-код зоны, начало диапазона, конец диапазона, емкость, оператор, регион. Емкость — просто количество номеров, входящих в диапазон

    Искать по плану нумерации регион и оператора — одно удовольствие.

    1. Вытаскиваем код зоны нумерации из нужного номера.
    2. По коду выбираем из справочника подходящие диапазоны.
    3. Оставляем от телефона только то, что следует после кода зоны — собственно номер.
    4. Ищем, в какой диапазон попадает номер.
    5. Успех! Мы нашли для телефона регион, оператора и тип.


    Номер +7 301 220-22-47 принадлежит ООО «Мобилон Телекоммуникации». Регион — Республика Бурятия

    Порой телефон не укладывается ни в один диапазон. Это значит, что номер неправильный. Исключений нет. На этом, надо сказать, прокалывается упомянутая библиотека Google — она одобряет внешне верный телефон, не сопоставляя с планом нумерации.

    Раньше на этом шаге изыскания заканчивались. Но в 2013 году власти приняли закон об отмене «мобильного рабства». С тех пор номера телефонов прибиты к операторам не так уж крепко.

    Теперь закон разрешает: меняйте оператора, сохранив номер. Но, внимание, только внутри региона. Нельзя вытащить телефон из региона, предусмотренного Россвязью. Как нельзя и «сломать» диапазоны распределения номеров. Поэтому, если интересен лишь регион телефонного номера, дальше можно не читать. А я продолжу и расскажу, как найти сменившие операторов номера.

    Получить доступ к базе перенесенных номеров


    Это шаг бюрократический, соответственно, самый сложный. А для большинства компаний и вовсе непроходимый, к сожалению.

    Номера, сменившие оператора, хранят в базе данных перенесенных абонентских номеров (БДПН). Ее оператор — уже не Россвязь, но Центральный научно-исследовательский институт связи.

    Проблема в том, что получить доступ к БДПН очень непросто. Конечно, если вы не мобильный оператор, субъект национальной платежной системы, силовая структура или другое ведомство.


    Все, что может рядовой пользователь — вручную проверить конкретный номер

    Мы получили доступ к БДПН в далеком 2013 году как «иная организация». Но с тех пор правила, похоже, ужесточили. Даже бланк заявления сильно изменился, свежий лежит на сайте Института связи. Поэтому посоветовать здесь ничего не могу, увы.

    UPD. В комментариях пишут, что с доступом все не так сурово: «Работал в энергосбытовой компании, без проблем за несколько дней получил доступ к бдпн. <...> заполнил заявление, подписал директором, поставил печать и отправил по электронике. Потом созвонился для верности».

    Проверить, не ушел ли номер к другому оператору


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

    Институт связи выкладывает БДПН на SFTP-сервер, в три папки.


    В первой папке лежит полный справочник. Во второй — инкрементальные обновления. А третья хранит номера, вернувшиеся «владельцам»

    • В папке PortAllNew лежит полная версия справочника. Это один файл, который обновляют ежедневно в 00:00 по Москве;
    • PortIncrementNew — для инкрементальных обновлений. Каждые два часа сюда добавляют файл с последними перенесенными номерами;
    • ReturnIncrementNew — это номера, которые вернули «владельцам» согласно плану нумерации Россвязи. Здесь тоже каждые два часа появляется новый файл.

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


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

    Формат файлов — CSV, сжатые в ZIP. Внутри — проще некуда.


    В файлах справочника три поля: номер, оператор и количество строк. Причем третье поле заполняют только для первой строки — изящное решение

    Историю в БДПН не хранят: каждый день на сервер выкладывают все перенесенные номера. Если телефона нет в полном справочнике или в инкрементах за день, значит, он подчиняется плану Россвязи. Поэтому для наших целей папка с вернувшимися номерами — ReturnIncrementNew — не нужна.

    Каждый день мы загружаем с сервера свежую БДПН. Для этого годится любой SFTP-клиент: на Windows подойдет WinSCP или FileZilla FTP Client, на Ubuntu — Nautilus. Классическая ошибка при этом: оставить в настройках дефолтный FTP вместо положенного SFTP. Так можно долго промучиться, не повторяйте наших ошибок.

    Когда справочник перенесенных номеров под рукой, осталось поискать в нем исследуемый номер. Варианта два:

    • номер нашелся в БДПН. В этом случае мы меняем оператора из плана Россвязи на нового;
    • номера нет в БДПН — оставляем оператора как есть.

    Вот и вся премудрость.

    А можно разобрать номер «Дадатой»


    Еще вариант — отдать номер телефона «Дадате», сервис вытащит из него полезное.


    Распозна́ем оператора, исправим опечатки в номере, покажем страну, регион и часовой пояс. Наконец, отделим стационарник от мобильного

    Обрабатываем номера по 10 копеек за штуку, если отдавать файлами или через API. Руками по одному телефону — бесплатно и даже без регистрации. Все это — на dadata.ru.
    HFLabs
    Повышаем качество клиентских данных для бизнеса

    Комментарии 33

      0
      бесплатно и даже без регистрации
      можно и на сайте ЦНИИ пробить номер.
        0
        По одному — конечно, никаких проблем. Я дал ссылку в тексте, вот на всякий: zniis.ru/bdpn/check
          0
          Уж не сочтите за рекламу, но тут можно по-многу: xinit.ru/group_def
          Единственное, база обновляется раз в одну-две недели, зато без капчи и списками.
        0
        Работал в энергосбытовой компании, без проблем за несколько дней получил доступ к бдпн, но формат выкладки в виде csv конечно не очень современно. Да ещё было время у них с кодировкой там косяки были
          0
          Поделитесь, пожалуйста, как получали. Стандартно, через заявление с сайта?
          +1
          Да, заполнил заявление, подписал директором, поставил печать и отправил по электронике. Потом созвонился для верности
            0

            На википедии есть статья со всеми странами операторами где первые 3-5 цифр это код оператора.


            https://www.wikiwand.com/en/List_of_mobile_telephone_prefixes_by_country

              0
              Чувствую себя идиотом, но оператора для своего кода 987 найти не могу.
                0
                Виноват, видать информация устарела или не обновляется.
                  0
                  это МТС
                    0
                    Это я понимаю :) Имел в виду, что по ссылке его нет.
                  0
                  Это не в википедии, а на основе данных википедии. И да, актуальность этих данных под большим вопросом.
                  0
                  Спасибо, отличный сервис.

                  Обратил внимание, что это довольно долгая операция. Это нормально, или мне не повезло просто?
                    0
                    Скорее всего, не повезло :) Может быть, долго рендерился HTML или еще что-то такое. Запрос к базе мгновенный.
                    0
                    Достаточно несложно автоматизируется запрос данных на сайте Россвязи и парсинг результатов запроса. Правда для большого количества номеров не использовал. Да и формат запроса может поменяться в любой момент (один раз уже пришлось переделать).
                    0
                    Может быть кому пригодится, оставлю это здесь.

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

                    SQL
                    CREATE TABLE `lb_phone_code` (
                      `id` int(11) NOT NULL,
                      `operator` varchar(30) NOT NULL COMMENT 'Код оператора',
                      `operator_name` varchar(200) NOT NULL COMMENT 'Имя оператора',
                      `region` int(11) NOT NULL COMMENT 'Регион',
                      `code_start` bigint(20) NOT NULL COMMENT 'Начало номерной емкости',
                      `code_end` bigint(20) NOT NULL COMMENT 'Конец номерной емкости',
                      `code_count` bigint(20) NOT NULL
                    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
                    
                    
                    ALTER TABLE `lb_phone_code`
                      ADD PRIMARY KEY (`id`);
                    
                    ALTER TABLE `lb_phone_code`
                      MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
                    


                    В code_count записываем разницу между code_end — code_start + 1, сами номера храним в международном формате — 11 чисел, начиная с 7 (для России в частности) без всяких плюсов и пр., как того требует smpp-протокол и как принято у операторов. Получаем что-то подобное:

                    Screenshot


                    Интересным является поле Код оператора — это логическое разбиение операторов по признаку — тут уже каждый сам для себя определяет — в открытом доступе я этого не нашел (если у кого есть инфа — буду благодарен, мы же вычисляем по своей базе, опыту, запрашиваем информацию у других источников через api/вручную). Именно оно в проекте и важно (для определения цены :)

                    Php-класс для определения оператора для списка номеров:

                    PHP
                    <?php
                    
                    namespace app\admin\modules\phonecode\models;
                    
                    use Yii;
                    use app\admin\modules\utils\Module as Utils;
                    
                    /**
                     * This is the model class for table "lb_phone_code".
                     *
                     * @property int $id
                     * @property string $operator Код оператора
                     * @property string $operator_name Имя оператора
                     * @property int $region Регион
                     * @property int $code_start Начало номерной емкости
                     * @property int $code_end Конец номерной емкости
                     */
                    class LbPhoneCode extends \yii\db\ActiveRecord
                    {
                    
                        static private $list = null; // кеш емкости телефонов и операторов
                    
                        /**
                         * @inheritdoc
                         */
                        public static function tableName()
                        {
                            return 'lb_phone_code';
                        }
                    
                        /**
                         * @inheritdoc
                         */
                        public function rules()
                        {
                            return [
                                [['operator', 'operator_name', 'region', 'code_start', 'code_end'], 'required'],
                                [['region', 'code_start', 'code_end'], 'integer'],
                                [['operator'], 'string', 'max' => 30],
                                [['operator_name'], 'string', 'max' => 200],
                            ];
                        }
                    
                        /**
                         * @inheritdoc
                         */
                        public function attributeLabels()
                        {
                            return [
                                'id' => 'ID',
                                'operator' => 'Код оператора',
                                'operator_name' => 'Имя оператора',
                                'region' => 'Регион',
                                'code_start' => 'Начало номерной емкости',
                                'code_end' => 'Конец номерной емкости',
                            ];
                        }
                    
                        /**
                         * @inheritdoc
                         * @return LbPhoneCodeQuery the active query used by this AR class.
                         */
                        public static function find()
                        {
                            return new LbPhoneCodeQuery(get_called_class());
                        }
                    
                        public static function getInfo($phone) {
                    
                            /*if (empty($phone = Utils::getPhone($phone, -2))) {
                    
                                return null;
                            }
                    
                            $query = LbPhoneCode::find();
                    
                            $query->andWhere(['<=', 'code_start', $phone]);
                            $query->andWhere(['>=', 'code_end', $phone]);
                    
                            return $query->one();*/
                    
                            // используем кеш, а также новый формат телефона с кодом страны
                    
                            if (empty($phone = Utils::getPhone($phone))) {
                    
                                return null;
                            }
                    
                            $id = false;
                            /*$operator =*/ self::getOperator($phone, '', $id);
                    
                            if ($id !== false) {
                    
                                return self::findOne($id);
                            }
                    
                            return null;
                        }
                    
                        private static function initPhoneCodeList() {
                    
                            if (self::$list === null) {
                    
                                $query = "SELECT code_start s, code_end e, operator o, id i FROM lb_phone_code order by code_count DESC";
                                $l = Yii::$app->db->createCommand($query)->queryAll();
                    
                                $min = $max = 0;
                                self::$list = [];
                    
                                foreach ($l as $item) {
                    
                                    $item['s'] = (int) $item['s'];
                                    $item['e'] = (int) $item['e'];
                    
                                    // вычисляем сразу лимиты, что бы не искать вне диаппазона
                                    if ($min === 0 || $min > $item['s']) { $min = $item['s']; }
                                    if ($max === 0 || $max < $item['e']) { $max = $item['e']; }
                    
                                    // начало диаппазона делаем ключом, чтобы не хранить лишнюю информацию,
                                    // т. к. ключ все равно создается
                                    self::$list['range'][$item['s']] = [
                                        $item['e'],
                                        $item['o'],
                                        $item['i'], // добавим связь по id, чтобы найти полную инфу, какая строка сработала
                                    ];
                                }
                    
                                self::$list['min'] = $min;
                                self::$list['max'] = $max;
                            }
                    
                            return self::$list;
                        }
                    
                        /**
                         * @param $phone - номер телефона, оператора которого ищем, не валидируется на корректность
                         * @param string $default_operator
                         * @param null $return_id - будет помещен id найденного оператора, либо false
                         * @return string
                         */
                        public static function getOperator($phone, $default_operator = '', & $return_id = null) {
                    
                            self::initPhoneCodeList();
                            $phone = (int) $phone;
                    
                            if ($phone >= self::$list['min'] && $phone <= self::$list['max']) {
                    
                                foreach (self::$list['range'] as $start => $item) {
                    
                                    if ($phone < $start || $phone > $item[0]) {
                    
                                        continue;
                                    }
                    
                                    if ($return_id !== null) { $return_id = $item[2]; }
                    
                                    return $item[1];
                                }
                            }
                    
                            if ($return_id !== null) { $return_id = false; }
                    
                            return $default_operator;
                        }
                    
                        /**
                         * @param $phone_list - список телефонов (будет изменен полученными результатами)
                         * @param string $default_operator - оператор по умолчанию, если не найден в базе
                         * @param bool $use_keys - телефоны указаны как ключи или как значение
                         * @param null $field_name - в какое поле помещать результат, если null, то заменят значение
                         */
                        public static function getOperatorByList( & $phone_list, $default_operator = '', $use_keys = false, $field_name = null) {
                    
                            if (empty($phone_list)) {
                    
                                return;
                            }
                    
                            if ( ! is_array($phone_list)) {
                    
                                $phone_list = (array) $phone_list;
                            }
                    
                            self::initPhoneCodeList();
                    
                            // если нам передан просто массив телефонов, то делаем их ключами, чтобы
                            // напротив них разместить результат (оператора)
                            if ( ! $use_keys) {
                    
                                $phone_list = array_flip($phone_list);
                            }
                    
                            foreach ($phone_list as $key => $_) {
                    
                                $phone = (int) $key;
                                $not_found = true;
                    
                                if ($phone >= self::$list['min'] && $phone <= self::$list['max']) {
                    
                                    foreach (self::$list['range'] as $start => $item) {
                    
                                        if ($phone < $start || $phone > $item[0]) {
                    
                                            continue;
                                        }
                    
                                        if ($field_name === null) {
                    
                                            $phone_list[$key] = $item[1];
                                        } else {
                    
                                            $phone_list[$key][$field_name] = $item[1];
                                        }
                    
                                        $not_found = false;
                    
                                        break;
                                    }
                                }
                    
                                if ($not_found) {
                    
                                    if ($field_name === null) {
                    
                                        $phone_list[$key] = $default_operator;
                                    }
                                    else {
                    
                                        $phone_list[$key][$field_name] = $default_operator;
                                    }
                    
                                }
                            }
                        }
                    }
                    
                    



                    Подгружаем номерные емкости в оперативу — сортируем по номерной емкости по убыванию, перебираем все емкости с учетом границ — просто сравнивая номера как числа.

                    При обновлении базы — номер сменил оператора — ищем старый диапазон и разбиваем его на два — до номера и после номера. Затем создаем новый — левая и правая границы равны номеру, емкость — 1. Релоадим кеш.

                    P. S.: Тот самый нужный код оператора при обновлении базы — ищем по названию нового оператора в базе — если есть таковой, то присваиваем его, в противном случае — помечаем как 'none' — и отправляем уведомление админу и менеджеру, что требуется вмешательство.
                      0

                      Определял регионы и коды еще в 2003 году.
                      В поиске набивал mtt def
                      До отмены мобильного рабства проблем вообще никаких не было

                        0

                        Когда-то можно было скачать CSV, но сегодня актуальные данные для скачивания найти нельзя.

                        0
                        Тогда можно разбить клиентскую базу географически и не звонить ночами

                        Не будет работать если человек уехал из Сибири в Москву и не сменил номер.
                          0
                          Что есть, то есть. Но тут уж вовсе ничего не сделаешь
                          0
                          спасибо за инфу
                            0
                            Пользуйтесь на здоровье
                            0
                            А разве «мобильное рабство» не ограничивает регион?
                            Я сталкивался неоднократно. Нельзя со своим номером уйди в другой федеральный округ.
                              0
                              Ограничивает, в статье об этом есть.
                              Теперь закон разрешает: меняйте оператора, сохранив номер. Но, внимание, только внутри региона. Нельзя вытащить телефон из региона, предусмотренного Россвязью. Как нельзя и «сломать» диапазоны распределения номеров.
                                0
                                Нельзя вытащить телефон из региона, предусмотренного Россвязью. Как нельзя и «сломать» диапазоны распределения номеров.

                                А зачем тогда вообще доступ к базе перенесенных телефонных номеров?
                                Меняется оператор, ну и ладно. Регион же не меняется.
                                  0
                                  Потому что в некоторых случаях нужен оператор, а не только регион :)

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

                                  Или, для большого бизнеса, делать кобрендинговые акции с оператором.
                              0
                              В своей подобной статье писал как именно это можно сделать, есть ссылка на исходный код.
                                0
                                Вчера получил звонок с номера +7-088-761-хххх, он был по делу, местный, подмосковный. Вроде как стационарный. Это что такой за оператор?
                                  0
                                  Посоветовались с коллегами — очень странная история. По документам такой формат невозможен, в жизни никогда с подобным не сталкивались. Мистика
                                    +1
                                    Как вариант — подмена номера. Может, ошибочно применённая подмена (и не отфильтрованная оператором) — должна была быть 9 вместо 0, например.

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

                                  Самое читаемое