Как извлечь номера телефонов из текста — парсинг и нормализация

Марина Козлова · · 8 мин чтения

Когда нужен парсинг номеров

Представьте: вы получили результаты web-скрапинга — 20 страниц с контактами компаний. Всё вперемешку: адреса, email, телефоны, описания. Номера записаны как попало: где-то +7 (999) 123-45-67, где-то 8-999-1234567, где-то просто 9991234567.

Ваша задача — извлечь все телефоны и привести к единому формату. Руками? На это уйдёт день. А если страниц не 20, а 200?

Парсинг телефонных номеров нужен, когда:

Хороший парсер должен:

  1. Распознавать все варианты записи (с пробелами, скобками, дефисами, плюсом)
  2. Отличать телефоны от случайных последовательностей цифр (номера домов, счетов)
  3. Приводить к единому формату (например, 79991234567)
  4. Игнорировать невалидные номера

Российские номера — какими они бывают

Прежде чем парсить, нужно понять, что искать. Российские мобильные номера имеют чёткую структуру:

Итого: 11 цифр (с семёркой) или 10 цифр (если код страны опущен).

Вот как один и тот же номер может выглядеть в разных источниках:

+7 (999) 123-45-67
+7 999 123 45 67
+7-999-123-45-67
7 (999) 123-45-67
7-999-123-45-67
7 999 123 45 67
79991234567
8 (999) 123-45-67
8-999-123-45-67
89991234567
(999) 123-45-67
999-123-45-67
9991234567

Все эти варианты описывают один номер. Задача парсера — распознать все форматы и привести к стандартному виду: 79991234567.

Проблемные случаи

Не каждая последовательность из 10—11 цифр — телефон:

Качественный парсер проверяет не только длину, но и валидность кода оператора. В России мобильные коды начинаются с 9 (900—999).

Regex-паттерны — классический подход

Подходит для: простых случаев, когда текст относительно чистый
Время: 10—15 минут на написание
Сложность: средняя (нужно знать регулярные выражения)

Регулярные выражения (regex) — классический способ поиска паттернов в тексте. Вот базовый пример для Python:

import re

text = """
Контакты:
Иван: +7 (999) 123-45-67
Мария: 8-900-765-43-21
Офис: 79161234567
"""

# Паттерн для поиска номеров
pattern = r'\+?[78][-\s]?\(?(\d{3})\)?[-\s]?(\d{3})[-\s]?(\d{2})[-\s]?(\d{2})'

matches = re.findall(pattern, text)

for match in matches:
    # match — кортеж из групп (999, 123, 45, 67)
    phone = '7' + ''.join(match)
    print(phone)  # 79991234567

Паттерн \+?[78][-\s]?\(?(\d{3})\)?... означает:

Ограничения:

Для быстрого прототипа — подходит, но для production лучше использовать специализированные библиотеки.

Библиотека phonenumbers от Google

Подходит для: надёжного парсинга с валидацией
Время: 5 минут на интеграцию
Сложность: низкая (если знаете Python)

Библиотека phonenumbers от Google — де-факто стандарт для работы с телефонными номерами. Она знает форматы всех стран, проверяет валидность, нормализует запись.

Установка:

pip install phonenumbers

Пример использования:

import phonenumbers
from phonenumbers import phonenumberutil

text = """
Контакты компании:
Отдел продаж: +7 (999) 123-45-67
Бухгалтерия: 8 900 765 43 21
Склад: 9161234567
"""

# Ищем все возможные номера
for match in phonenumberutil.PhoneNumberMatcher(text, "RU"):
    phone = match.number

    # Проверяем валидность
    if phonenumbers.is_valid_number(phone):
        # Форматируем в E.164 (международный формат)
        formatted = phonenumbers.format_number(
            phone,
            phonenumbers.PhoneNumberFormat.E164
        )
        print(formatted)  # +79991234567

Результат:

+79991234567
+79007654321
+79161234567

Преимущества:

Ограничения:

Если вы пишете на Python — это оптимальный выбор.

Basalt — парсинг без кода

Подходит для: тех, кто не программирует
Время: 30 секунд
Сложность: нулевая

Basalt автоматически извлекает телефоны из любого текста — CSV, Excel, TXT. Вам не нужно писать код или разбираться в regex.

Два сценария использования:

Парсинг из CSV/TXT

  1. Откройте функцию «Уникальные» (она извлекает и дедуплицирует за один проход)
  2. Загрузите файл с «грязными» данными
  3. Basalt автоматически найдёт все телефоны, приведёт к формату 7XXXXXXXXXX
  4. Получите чистый список уникальных номеров

Парсинг из Excel

  1. Откройте функцию «Парсинг Excel»
  2. Загрузите XLSX-файл
  3. Basalt просканирует все листы, все ячейки
  4. Извлечёт телефоны и ссылки (если есть)
  5. Сохранит результаты в два CSV: filename_phones.csv и filename_links.csv

Как работает внутри:

Basalt использует многоступенчатый алгоритм:

  1. Regex-поиск — находит все последовательности, похожие на телефоны
  2. Очистка — убирает скобки, дефисы, пробелы, плюсы
  3. Нормализация — заменяет 8 на 7 в начале, дополняет 10-значные номера семёркой
  4. Валидация — проверяет длину (11 цифр), код страны (7), код оператора (9XX)
  5. Дедупликация — удаляет повторы

На выходе вы получаете только валидные российские мобильные номера в едином формате: 79991234567.

Пример результата:

Обработано: 50 000 строк
Извлечено: 12 340 номеров
Уникальных: 11 890
Дублей удалено: 450

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

Нормализация к единому формату

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

Стандартный формат: 7XXXXXXXXXX (11 цифр, начинается с семёрки)

Алгоритм нормализации:

def normalize_phone(raw_phone):
    # Шаг 1: убрать всё, кроме цифр
    digits = ''.join(filter(str.isdigit, raw_phone))

    # Шаг 2: если 11 цифр и начинается с 8 — заменить на 7
    if len(digits) == 11 and digits[0] == '8':
        digits = '7' + digits[1:]

    # Шаг 3: если 10 цифр и начинается с 9 — добавить 7 в начало
    if len(digits) == 10 and digits[0] == '9':
        digits = '7' + digits

    # Шаг 4: проверка валидности
    if len(digits) != 11:
        return None  # невалидный номер

    if digits[0] != '7':
        return None  # не российский

    if digits[1] != '9':
        return None  # не мобильный

    return digits

# Примеры
print(normalize_phone("+7 (999) 123-45-67"))  # 79991234567
print(normalize_phone("8 (999) 123-45-67"))   # 79991234567
print(normalize_phone("9991234567"))          # 79991234567
print(normalize_phone("71234567890"))         # None (код 123 невалидный)

Эта функция:

После нормализации вы можете безопасно дедуплицировать базу — одинаковые номера в разных форматах будут распознаны как дубли.

Практические советы

1. Проверяйте результат выборочно

После парсинга откройте результат и просмотрите первые 50—100 номеров. Убедитесь, что:

Если видите ошибки — уточните паттерн парсинга или используйте более строгую валидацию.

2. Сохраняйте «сырые» данные

Не удаляйте исходный файл сразу после парсинга. Иногда выясняется, что настройки были неправильные, и приходится перезапускать процесс.

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

3. Учитывайте контекст

Если парсите контакты компаний, в тексте могут встречаться:

Настройте валидацию так, чтобы извлекались только мобильные российские номера. Это уменьшит количество «ложных срабатываний».

4. Парсите построчно для больших файлов

Если файл огромный (несколько гигабайт), не загружайте его целиком в память. Читайте и обрабатывайте построчно:

import re

pattern = re.compile(r'\d{10,11}')

with open('huge_file.txt', 'r') as infile:
    with open('phones.csv', 'w') as outfile:
        for line in infile:
            matches = pattern.findall(line)
            for match in matches:
                normalized = normalize_phone(match)
                if normalized:
                    outfile.write(normalized + '\n')

Этот подход работает даже на файлах в несколько гигабайт — потребление памяти остаётся минимальным.

5. Используйте дедупликацию сразу

Парсинг часто выдаёт дубликаты — один номер может встретиться в тексте несколько раз. Удаляйте дубли сразу, используя set в Python или функцию «Уникальные» в Basalt.

Это экономит место и упрощает дальнейшую работу.

Типичные ошибки при парсинге

Слишком жадный regex

Паттерн \d{10,11} найдёт любые 10—11 цифр подряд, включая номера счетов, даты (20241231235959) и другие несвязанные данные.

Решение: добавляйте проверку формата и валидацию кода оператора.

Пробелы внутри номера

Номер может быть записан как 7 999 123 45 67 (с пробелами). Если regex не учитывает пробелы, он найдёт несколько коротких последовательностей вместо одного номера.

Решение: используйте [-\s]* в паттерне, чтобы допустить любое количество дефисов и пробелов.

Скобки в формате

Формат +7 (999) 123-45-67 содержит скобки. Если regex не учитывает их, номер не распознается.

Решение: добавьте \(? и \)? для опциональных скобок.

Дедупликация без нормализации

Если удалять дубли до нормализации, номера 79991234567 и 89991234567 останутся оба, хотя это один телефон.

Решение: сначала нормализуйте (приведите к 7XXXXXXXXXX), потом дедуплицируйте.

От хаоса к порядку

Парсинг телефонных номеров — это превращение неструктурированных данных в аккуратный список контактов. Правильный парсинг экономит часы ручной работы и исключает ошибки.

Что запомнить:

Выбирайте инструмент под задачу:

Главное — не парсите вручную. Компьютер сделает это быстрее, точнее и без ошибок. А вы сэкономите время на более важные задачи.