Как стать автором
Обновить

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

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

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

Пардон, перепутал с ZNIIS. Я сам с Россвязи и беру данные в автоматическом режиме.

Может быть кому пригодится, оставлю это здесь.

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

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' — и отправляем уведомление админу и менеджеру, что требуется вмешательство.

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

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

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

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

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

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

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