Регулярные выражения в Python: как работать с модулем re

Как работать с регулярными выражениями в Python: подключить модуль re, записать шаблон в r-строке и выбрать функцию под задачу.

Внутри шпаргалка по синтаксису, search, match, findall, sub, split и compile с реальным выводом кода, флаги, кириллица и шесть частых ошибок новичков.

Статью написал:
Ваня Буявец, продюсер, основатель Checkroi
Ваня Буявец
Основатель Checkroi, продюсер, эксперт в выборе онлайн-курсов
Все 2947 статей автора Подписаться на Телеграм-канал
Одобрено экспертом:
Наташа Буявец, основатель Checkroi, эксперт по онлайн-курсам
Наташа Буявец
Основательница Checkroi, продюсер Youtube-каналов, эксперт по онлайн-курсам
Все 3601 экспертное мнение Подписаться на Телеграм-канал
Обложка: Регулярные выражения в Python: как работать с модулем re

Регулярные выражения в Python работают через встроенный модуль re. Подключите его строкой import re, запишите шаблон в r-строке и вызовите нужную функцию: re.search() найдёт первое совпадение, re.findall() соберёт все, re.sub() заменит найденное, re.split() разрежет строку. Например, re.findall(r"\d+", text) вытащит из текста все числа.

Ставить ничего не нужно: модуль входит в стандартную библиотеку. Если вы только знакомитесь с языком, сначала загляните в обзор языка Python и первые простые программы, а потом возвращайтесь сюда. Пройти язык целиком, с практикой и проверкой заданий, помогут курсы по Python. Ниже шпаргалка по синтаксису, все функции модуля с выводом интерпретатора, флаги, готовые шаблоны для частых задач и частые ошибки новичков.

CheckroiCheckroiПодборка курсов по Python566 курсов • 51 школаСравните цены, школы, программу и найдите выгодные предложения по обучениюСравнить→

Как подключить модуль re и найти первое совпадение

Рой ищет совпадения в тексте на ноутбуке

Регулярное выражение — это шаблон, которому должна соответствовать строка. В шаблоне \d+ символ \d значит «любая цифра», а + значит «один раз или больше». Вместе это «число из одной или нескольких цифр».

  1. Импортируйте модуль: import re.
  2. Запишите шаблон в сырой строке с префиксом r: r"\d+".
  3. Вызовите функцию поиска и передайте ей шаблон и текст.
  4. Проверьте результат: функция вернёт объект Match или None, если ничего не нашлось.
import re

text = "Заказ №4512 оплачен 14.10.2026"
m = re.search(r"\d+", text)
print(m)
print(m.group(), m.start(), m.end())
# <re.Match object; span=(7, 11), match='4512'>
# 4512 7 11

В объекте Match лежит найденный фрагмент (group()) и его позиция в строке: start() и end(). Конец отсчитывается как в срезах, то есть text[7:11] даст ту же подстроку '4512'.

Зачем нужна r-строка

В обычной строке Python сам обрабатывает обратную косую черту. Последовательность "\b" превращается в один служебный символ backspace, и до модуля re доходит уже он, а не граница слова.

import re

print(len("\b"), len(r"\b"))
print(re.findall("\bкот\b", "кот и котлета"))
print(re.findall(r"\bкот\b", "кот и котлета"))
# 1 2
# []
# ['кот']

Без префикса r шаблон молча не нашёл ни одного «кота». Пишите все регулярные выражения в r-строках, даже если в шаблоне пока нет обратной черты: так вы не получите сюрприз, когда добавите \b или \d.

Синтаксис регулярных выражений: шпаргалка

Шаблон собирается из обычных символов, которые ищутся как есть, и метасимволов со специальным смыслом. Полный список есть в разделе о синтаксисе в документации Python, а ниже то, что нужно в повседневной работе.

Символы и классы

Запись Что означает Пример
. любой символ, кроме перевода строки к.т найдёт «кот», «кит»
\d / \D цифра / не цифра \d\d найдёт «42»
\w / \W буква, цифра или _ / всё остальное \w+ найдёт слово целиком
\s / \S пробельный символ / не пробельный \s+ найдёт серию пробелов
[abc] один символ из набора [,;] найдёт запятую или точку с запятой
[^abc] любой символ, кроме перечисленных [^0-9] то же, что \D
[а-яё] диапазон символов строчная русская буква
\., \+, \( сам символ, а не его спецзначение \d+\.\d+ найдёт «3.14»

Квантификаторы

Запись Сколько раз Пример
* 0 и больше \d* подойдёт и пустой строке
+ 1 и больше \d+ число любой длины
? 0 или 1 руб\.? найдёт «руб» и «руб.»
{n} ровно n \d{6} индекс из шести цифр
{n,m} от n до m \d{2,4} от двух до четырёх цифр
*?, +? как можно меньше (ленивый вариант) <.+?> один тег, а не весь абзац

Якоря и группы

Запись Что делает
^ / $ начало / конец строки (с флагом re.MULTILINE — каждой строки текста)
\b граница слова: \bкот\b не зацепит «котлету»
(...) группа: часть совпадения, которую можно достать отдельно
(?P<name>...) именованная группа
(?:...) группа без сохранения, только для объединения
a|b или a, или b
(?=...) проверка «дальше идёт …», в совпадение не входит
Канал основателя Checkroi Вани Буявца3 700 человек читают мой Телеграм-канал про нейросетиСобрал промпты для Claude Code и ChatGPT, разборы ИИ-инструментов и лайфхаки по продвижению бизнеса в одном местеПрисоединиться

Функции модуля re: что выбрать под задачу

Функций в модуле немного, и путают их в основном по тому, что они возвращают. Сводка:

Функция Что ищет Что возвращает
re.search() первое совпадение в любом месте строки Match или None
re.match() совпадение только с начала строки Match или None
re.fullmatch() совпадение всей строки целиком Match или None
re.findall() все совпадения список строк или кортежей
re.finditer() все совпадения итератор объектов Match
re.sub() / re.subn() замена по шаблону новая строка / кортеж «строка, число замен»
re.split() разбиение по шаблону список строк
re.compile() ничего, готовит шаблон объект Pattern с теми же методами

re.search, re.match и re.fullmatch

Главная ловушка модуля: re.match() ищет совпадение только в начале строки. Если число стоит в середине, match вернёт None, а search его найдёт.

import re

s = "Цена 990 руб., скидка 15%"
print(re.search(r"\d+", s).group())
print(re.match(r"\d+", s))
print(re.match(r"Цена", s).group())
print(re.fullmatch(r"\d{6}", "123456"))
print(re.fullmatch(r"\d{6}", "1234567"))
# 990
# None
# Цена
# <re.Match object; span=(0, 6), match='123456'>
# None

Для проверки формата (индекс, код из СМС, номер заказа) берите re.fullmatch(): она требует, чтобы шаблону соответствовала вся строка, и седьмая лишняя цифра уже не пройдёт.

В Python 3.15 у re.match() появилось второе имя, re.prefixmatch(), а старое помечено как мягко устаревшее. Разработчики объясняют это тем, что в других языках словом match называют поиск в любом месте строки, то есть то, что в Python делает search(). Удалять match() не собираются: так сказано в документации модуля re.

Практический вывод такой. Если код должен работать и на 3.14, и на более старых версиях, оставляйте match(). Если проект живёт только на Python 3.15 и новее, prefixmatch() читается понятнее. Как обновить интерпретатор, есть в инструкции по установке Python.

re.findall и re.finditer

findall() сразу отдаёт список найденных строк, это самый короткий путь «вытащить всё». finditer() нужен, когда кроме текста важна позиция или нужны группы каждого совпадения: он возвращает объекты Match по одному и не собирает весь список в памяти.

import re

log = "ERROR 12:01 диск; WARN 12:05 память; ERROR 12:09 сеть"
print(re.findall(r"\d\d:\d\d", log))
for m in re.finditer(r"ERROR (\d\d:\d\d)", log):
    print(m.group(1), m.span())
# ['12:01', '12:05', '12:09']
# 12:01 (0, 11)
# 12:09 (37, 48)

re.sub и re.subn: замена по шаблону

Функция re.sub(шаблон, замена, строка) возвращает новую строку. В замене можно сослаться на группы шаблона как \1, \2 и так далее, а вместо строки можно передать функцию: она получит каждый Match и вернёт текст для подстановки.

import re

phone = "Звоните: +7 (912) 345-67-89"
print(re.sub(r"\D", "", phone))
print(re.sub(r"(\d{2})\.(\d{2})\.(\d{4})", r"\3-\2-\1", "Срок до 31.12.2026"))

def double(m):
    return str(int(m.group()) * 2)

print(re.sub(r"\d+", double, "Цены: 100 и 250"))
print(re.subn(r"\s+", " ", "много   лишних    пробелов"))
# 79123456789
# Срок до 2026-12-31
# Цены: 200 и 500
# ('много лишних пробелов', 2)

re.subn() делает то же самое и дополнительно сообщает, сколько замен случилось. Посимвольные замены можно потренировать на шифре Цезаря: там тоже приходится обходить каждый символ строки и решать, чем его заменить.

re.split: разбить строку по нескольким разделителям

Обычный str.split() режет строку по одному разделителю. re.split() принимает шаблон, поэтому справляется со смесью запятых, точек с запятой и пробелов за один вызов.

import re

print(re.split(r"[,;\s]+", "python, java;go  rust"))
print(re.split(r"(-)", "2026-10-15"))
print(re.split(r"\s", "a b c d", maxsplit=2))
# ['python', 'java', 'go', 'rust']
# ['2026', '-', '10', '-', '15']
# ['a', 'b', 'c d']

Если поставить разделитель в скобки, он останется в результате. Аргумент maxsplit ограничивает число разрезов; передавайте его по имени, потому что позиционная передача maxsplit и flags объявлена устаревшей с Python 3.13.

re.compile: один шаблон на много строк

re.compile() превращает шаблон в объект Pattern, у которого есть те же search(), findall(), sub() и другие методы. Он пригодится, когда одна регулярка нужна в цикле или в нескольких функциях.

import re

date_re = re.compile(r"(\d{2})\.(\d{2})\.(\d{4})")
for line in ["старт 01.09.2026", "без даты", "финиш 25.12.2026"]:
    m = date_re.search(line)
    if m:
        print(m.groups())
print(date_re.pattern)
# ('01', '09', '2026')
# ('25', '12', '2026')
# (\d{2})\.(\d{2})\.(\d{4})

Ради скорости компилировать шаблоны не обязательно. Модуль сам кеширует последние использованные шаблоны, и программам, где одновременно работает несколько регулярок, по словам документации, можно не беспокоиться о компиляции. Главный выигрыш в читаемости: у шаблона появляется имя.

Группы и объект Match

Скобки в шаблоне выделяют части совпадения. Метод group(0) возвращает совпадение целиком, group(1) первую группу, groups() все группы кортежем. Именованные группы (?P<имя>...) избавляют от подсчёта скобок.

import re

m = re.search(r"(?P<user>[\w.]+)@(?P<domain>[\w.-]+)", "пишите на ivan.petrov@mail.ru")
print(m.group(0))
print(m.group("user"), m.group(2))
print(m.groupdict())
# ivan.petrov@mail.ru
# ivan.petrov mail.ru
# {'user': 'ivan.petrov', 'domain': 'mail.ru'}

У именованной группы остаётся и номер, поэтому m.group(2) и m.group("domain") вернут одно и то же. groupdict() собирает все именованные группы в словарь, его можно сразу передать дальше в код.

Как группы меняют результат findall

Здесь новички чаще всего получают «не тот» результат. Без групп findall() возвращает совпадения целиком, с одной группой только её содержимое, а с несколькими группами список кортежей.

import re

text = "a1=10, b2=20"
print(re.findall(r"\w\d=\d+", text))
print(re.findall(r"(\w)\d=(\d+)", text))
print(re.findall(r"(?:\w)\d=(\d+)", text))
# ['a1=10', 'b2=20']
# [('a', '10'), ('b', '20')]
# ['10', '20']
  • Нужен весь фрагмент: уберите скобки или оберните весь шаблон в одну группу.
  • Скобки нужны только для | или квантификатора: пишите (?:...), и группа не попадёт в результат.

Флаги: re.IGNORECASE, re.MULTILINE, re.DOTALL и другие

Флаг передаётся последним аргументом и меняет поведение шаблона. Несколько флагов объединяются побитовым оператором |, например re.I | re.M.

Флаг Короткое имя Что меняет
re.IGNORECASE re.I регистр букв не важен
re.MULTILINE re.M ^ и $ работают на каждой строке текста
re.DOTALL re.S точка совпадает и с переводом строки
re.VERBOSE re.X в шаблоне можно ставить пробелы и комментарии
re.ASCII re.A \w, \d, \s понимают только латиницу и ASCII-цифры
import re

print(re.findall(r"python", "Python и PYTHON", re.IGNORECASE))
text = "первая\nвторая\nтретья"
print(re.findall(r"^\w+", text))
print(re.findall(r"^\w+", text, re.MULTILINE))
print(re.search(r"<b>.+</b>", "<b>жирный\nтекст</b>"))
print(re.search(r"<b>.+</b>", "<b>жирный\nтекст</b>", re.DOTALL).group())
# ['Python', 'PYTHON']
# ['первая']
# ['первая', 'вторая', 'третья']
# None
# <b>жирный
# текст</b>

Без re.MULTILINE якорь ^ нашёл только первое слово всего текста. Без re.DOTALL точка не прошла через перевод строки внутри тега, и search вернул None.

re.VERBOSE: длинный шаблон с комментариями

Длинную регулярку через месяц не прочитает даже автор. С флагом re.VERBOSE пробелы в шаблоне игнорируются, а после # можно писать пояснения, как в обычном коде.

import re

price = re.compile(r"""
    (\d+(?:\s\d{3})*)   # 45 900: цифры и тройки через пробел
    \s?                 # необязательный пробел
    (₽|руб)             # валюта
""", re.VERBOSE)
print(price.findall("Курс стоит 45 900 ₽, рассрочка 3 825 руб в месяц"))
# [('45 900', '₽'), ('3 825', 'руб')]

Пробел, который должен быть частью шаблона, в таком режиме пишется явно, через \s или [ ].

Регулярные выражения и кириллица

В Python 3 строки хранятся в Юникоде, и \w по умолчанию ловит русские буквы так же, как латинские. С флагом re.ASCII русские слова из результата пропадают.

import re

print(re.findall(r"\w+", "Привет, world_2026!"))
print(re.findall(r"\w+", "Привет, world_2026!", re.ASCII))
print(re.findall(r"[а-яё]+", "Ёлка и ель", re.IGNORECASE))
print(re.findall(r"[а-я]+", "Ёлка и ель", re.IGNORECASE))
# ['Привет', 'world_2026']
# ['world_2026']
# ['Ёлка', 'и', 'ель']
# ['лка', 'и', 'ель']

Последняя строка показывает частую ошибку: диапазон [а-я] не включает букву «ё», потому что в таблице Юникода она стоит отдельно от остального алфавита. Пишите [а-яё], а для заглавных [А-ЯЁ] или флаг re.IGNORECASE.

Если текст пришёл байтами (из сети или из файла, открытого в режиме "rb"), сначала превратите его в строку методом decode(). Строковый шаблон к байтам не применяется, Python выдаст TypeError: cannot use a string pattern on a bytes-like object. Как байты и текст переходят друг в друга, хорошо видно на примере кодирования Base64.

Ваня БуявецКанал основателя Checkroi Вани БуявцаЗабирайте промпты и обучение по нейросетям в моём Телеграм-каналеБольше 3 700 человек уже применяют Claude Code, ChatGPT и другие нейросети в работе, учёбе, бизнесе и жизниПерейти в канал

Жадные и ленивые квантификаторы

Бусины, нанизанные на нить короткими и длинными группами

Квантификаторы * и + жадные: они захватывают как можно больше символов. На тексте с повторяющимися метками это даёт одно огромное совпадение вместо нескольких маленьких.

import re

html = "<p>один</p><p>два</p>"
print(re.findall(r"<p>.*</p>", html))
print(re.findall(r"<p>.*?</p>", html))
print(re.findall(r"<p>(.*?)</p>", html))
# ['<p>один</p><p>два</p>']
# ['<p>один</p>', '<p>два</p>']
# ['один', 'два']

Знак ? после квантификатора делает его ленивым: .*? остановится на первом подходящем </p>. Сам HTML регулярками лучше не разбирать: вложенные теги и атрибуты быстро ломают любой шаблон, для этого есть парсеры вроде html.parser из стандартной библиотеки.

Примеры регулярных выражений для частых задач

Четыре шаблона, которые пригодятся в обработке анкет, логов и выгрузок. Больше задач для разминки есть в подборке 30 задач по Python для начинающих.

Достать домены из списка email

Якорь $ привязывает группу к концу адреса, а строки без «@» просто отсеиваются.

import re

emails = ["anna@yandex.ru", "oleg@gmail.com", "bad.address", "kate@mail.ru"]
domains = [m.group(1) for e in emails if (m := re.search(r"@([\w.-]+)$", e))]
print(domains)
# ['yandex.ru', 'gmail.com', 'mail.ru']

Привести телефоны к одному формату

Сначала re.sub(r"\D", "", n) оставляет только цифры, потом fullmatch проверяет, что их 11 и номер мобильный. Запись ... if ok else ... в конце называется тернарным оператором.

import re

numbers = ["+7 912 345-67-89", "8 (912) 345 67 89", "912-345-67", "+79123456789"]
for n in numbers:
    digits = re.sub(r"\D", "", n)
    ok = re.fullmatch(r"[78]9\d{9}", digits)
    print(n, "->", "+7" + digits[1:] if ok else "не номер")
# +7 912 345-67-89 -> +79123456789
# 8 (912) 345 67 89 -> +79123456789
# 912-345-67 -> не номер
# +79123456789 -> +79123456789

Проверить сложность пароля

Три проверки (?=...) смотрят вперёд и ничего не съедают: в строке должна быть цифра, заглавная и строчная латинская буква. Длину от восьми символов задаёт .{8,}.

import re

pwd_re = re.compile(r"(?=.*\d)(?=.*[A-Z])(?=.*[a-z]).{8,}")
for p in ["qwerty12", "Qwerty12", "Qw1"]:
    print(p, bool(pwd_re.fullmatch(p)))
# qwerty12 False
# Qwerty12 True
# Qw1 False

Искать текст пользователя как есть

Если шаблон собирается из чужой строки (поисковый запрос, имя файла), символы вроде + и скобок сломают регулярку или изменят её смысл. re.escape() экранирует их за вас.

import re

query = "c++ (beta)"
print(re.escape(query))
print(re.findall(re.escape(query), "курсы c++ (beta) и c++ (beta)"))
# c\+\+\ \(beta\)
# ['c++ (beta)', 'c++ (beta)']

Частые ошибки и что с ними делать

Рой отмечает исправленные ошибки в чек-листе

Типичные ошибки при работе с модулем re, их причины и решения:

  1. AttributeError: ‘NoneType’ object has no attribute ‘group’. Поиск ничего не нашёл и вернул None, а код сразу вызвал group(). Проверяйте результат перед использованием: if m:.
  2. re.error: missing ), unterminated subpattern. В шаблоне не закрыта скобка. Позиция в сообщении указывает, где открылась незакрытая группа.
  3. Шаблон с \b ничего не находит. Забыт префикс r, и Python превратил \b в backspace до того, как шаблон попал в модуль.
  4. match() возвращает None, хотя совпадение в строке есть. Оно не в начале строки. Нужен search().
  5. findall() отдаёт кортежи вместо строк. В шаблоне несколько групп. Лишние замените на (?:...).
  6. Слова с «ё» выпадают из результата. В диапазоне написано [а-я], нужно [а-яё].
import re

m = re.match(r"\d+", "abc")
print(m)
try:
    print(m.group())
except AttributeError as e:
    print("AttributeError:", e)
# None
# AttributeError: 'NoneType' object has no attribute 'group'
import re

try:
    re.compile(r"(\d+")
except re.error as e:
    print("re.error:", e)
# re.error: missing ), unterminated subpattern at position 0

Регулярка зависла: катастрофический перебор

Вложенные квантификаторы вроде (a+)+ на строке, которая почти подходит, заставляют модуль перебирать все способы разбить её на части. Время растёт экспоненциально: каждые два лишних символа примерно вчетверо.

import re, time

for n in (20, 22, 24, 26):
    s = "a" * n + "!"
    t = time.perf_counter()
    re.fullmatch(r"(a+)+", s)
    print(n, round(time.perf_counter() - t, 2))
t = time.perf_counter()
re.fullmatch(r"a+", "a" * 26 + "!")
print("a+", round(time.perf_counter() - t, 4))
# 20 0.04
# 22 0.11
# 24 0.36
# 26 1.45
# a+ 0.0001

Секунды на вашем компьютере будут другими, рост тот же. Лечится это упрощением шаблона (здесь хватит a+) или атомарной группой (?>...): выйдя из неё, модуль уже не возвращается внутрь перебирать другие варианты. Атомарные группы и захватывающие квантификаторы вроде a++ появились в Python 3.11.

import re, time

s = "a" * 26 + "!"
t = time.perf_counter()
print(re.fullmatch(r"(?>a+)+", s))
print(round(time.perf_counter() - t, 4))
# None
# 0.0

Как проверить регулярное выражение перед запуском

Отлаживать шаблон в рабочем скрипте долго. Быстрее собрать его отдельно:

  1. Откройте regex101 и выберите слева диалект Python.
  2. Вставьте шаблон и несколько строк для проверки, включая те, что не должны совпадать.
  3. Справа сервис объяснит каждый кусок шаблона и подсветит группы.
  4. Перенесите готовый шаблон в код в r-строке и прогоните на реальных данных.

Для системного разбора темы у разработчиков Python есть отдельное руководство по регулярным выражениям, а как устроена документация на python.org и как в ней искать, мы разбирали в отдельной статье.

Где освоить Python системно

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

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

КурсШколаСтоимость со скидкойВ рассрочкуДлитель­ностьОбзор курса от Checkroi
Профессия «Python-разработчик»
Перейти на сайт курса
SkillboxSkillbox157 335 ₽5987 ₽/мес.10 месяцевОбзор курса
Fullstack-разработчик на Python
Перейти на сайт курса
НетологияНетология137 011 ₽7125 ₽/мес.21 месяцОбзор курса
ДО Профессия Python-разработчик
Перейти на сайт курса
GeekBrainsGeekBrains149 001 ₽3167 ₽/мес.10 месяцевОбзор курса
Автоматизированное тестирование на Python
Перейти на сайт курса
SkillboxSkillbox118 666 ₽4108 ₽/мес.9 месяцевОбзор курса
Программирование на Python ПРО
Перейти на сайт курса
SkillboxSkillbox119 600 ₽ - 17 месяцевОбзор курса

Больше программ — в полном каталоге курсов по Python

Часто задаваемые вопросы

Как подключить регулярные выражения в Python?

Устанавливать ничего не нужно: модуль re входит в стандартную библиотеку. Напишите в начале файла import re, а шаблоны записывайте в r-строках, например re.findall(r"\d+", text).

Чем re.match() отличается от re.search()?

re.match() ищет совпадение только в начале строки, re.search() в любом её месте. Если нужное число стоит в середине текста, match() вернёт None. В Python 3.15 у match() появилось второе имя prefixmatch(), старое продолжает работать.

Как найти все совпадения регулярного выражения?

Функция re.findall() вернёт список всех найденных строк. Если нужны позиции или группы каждого совпадения, берите re.finditer(): она отдаёт объекты Match по одному.

Как искать без учёта регистра?

Передайте флаг re.IGNORECASE (короткое имя re.I) последним аргументом: re.findall(r"python", text, re.I) найдёт и Python, и PYTHON. Несколько флагов объединяются через |.

Зачем буква r перед строкой с регулярным выражением?

Префикс r делает строку сырой: Python не обрабатывает обратную косую черту, и \b или \d доходят до модуля re без изменений. Без него "\b" превращается в служебный символ, и шаблон молча ничего не находит.

Почему регулярка не находит слова с буквой ё?

Диапазон [а-я] не включает «ё»: в Юникоде она стоит отдельно от остального алфавита. Пишите [а-яё] или используйте \w, который в Python 3 по умолчанию ловит все русские буквы. Больше примеров есть в задачах по Python для начинающих.

Читайте Checkroi первым в Google
Добавьте Checkroi в избранные источники — и наши разборы курсов и обзоры школ будут показываться выше в вашей выдаче Google.
Оставить комментарий
0 комментариев
Форма комментария

Оставьте комментарий

Напишите, что думаете. Нам важно ваше мнение!