QR-код от А до Я: структура, версии, коррекция ошибок и генерация на Python

QR-код от А до Я: структура, версии, коррекция ошибок и генерация на Python Полезное

QR-код (Quick Response, «быстрый отклик») — это двумерный матричный штрихкод: квадрат из черных и белых модулей, в котором данные записаны сразу по двум осям, поэтому сканер читает их в любом положении. От линейного штрихкода он отличается тем, что кодирует не по одной оси, а по площади, и потому вмещает на порядки больше информации. Ниже разберем, из каких блоков состоит символ, как работает коррекция ошибок Рида-Соломона, чем различаются версии и уровни коррекции, как сгенерировать код на Python и как не попасться на фишинг через QR.

Что такое QR-код и откуда он взялся

Формат разработала в 1994 году японская компания Denso Wave (подразделение группы Denso) для маркировки автомобильных деталей на конвейере. Ведущим инженером проекта был Масахиро Хара. Название QR Code — зарегистрированный товарный знак Denso Wave, но сам стандарт открыт и описан в ISO/IEC 18004, поэтому кодами свободно пользуются все.

Быстрое считывание и большая емкость по сравнению с линейными кодами (например, UPC) вывели формат далеко за пределы автопрома: учет и отслеживание товаров, идентификация деталей, платежи, реклама и ссылки, электронные билеты. В России по QR работает и Система быстрых платежей.

Важно не путать два уровня. Штрихкод (линейный, 1D) хранит данные вдоль одной оси — обычно это короткий номер, по которому запись ищут в базе. QR-код (матричный, 2D) хранит данные по площади и может нести сам текст: ссылку, реквизиты, строку контакта. Это разные форматы, а не «новая версия» одного и того же.

Как устроен QR-код: структура символа

Изображение QR-кода — не случайный узор. Это сетка из модулей (черный = 1, белый = 0) с фиксированными служебными блоками. Основные из них:

  • Позиционные маркеры (finder patterns) — три больших «квадрата в квадрате» по углам, кроме правого нижнего. По ним сканер находит код в кадре и определяет его ориентацию и наклон.
  • Выравнивающие маркеры (alignment patterns) — меньшие квадраты внутри поля. Помогают выправить геометрию при съемке под углом или на изогнутой поверхности. В самой маленькой версии 1 их нет, они появляются начиная с версии 2 и дальше их становится больше.
  • Синхрополосы (timing patterns) — линия чередующихся черных и белых модулей между позиционными маркерами. Задает сетку координат, чтобы сканер понимал, где границы каждого модуля.
  • Зоны формата и версии — служебные модули, где записаны уровень коррекции ошибок и маска, а у больших версий — и номер версии.
  • Тихая зона (quiet zone) — пустое белое поле вокруг кода (обычно 4 модуля). Без него сканер хуже отделяет код от фона.
  • Область данных и коррекции — все остальное поле, где лежат закодированные данные вперемешку с проверочными байтами Рида-Соломона.

Упрощенно: сначала данные переводятся в биты по выбранному режиму, к ним добавляются проверочные байты, вся последовательность раскладывается по модулям, а служебные блоки помогают сканеру эту раскладку прочитать. Это модель «на пальцах»: реальный стандарт добавляет еще маскирование (чтобы не было больших одноцветных пятен) и чередование блоков.

Коррекция ошибок Рида-Соломона: почему код читается поврежденным

Главная причина живучести QR — помехоустойчивое кодирование Рида-Соломона. К данным добавляются избыточные проверочные байты, вычисленные по алгебраическим правилам. Если часть модулей затерта, закрыта логотипом или засвечена, декодер по этим проверочным байтам восстанавливает исходные данные — до определенной доли повреждений.

Именно поэтому в центр кода можно вставить логотип: он «съедает» часть модулей, но при достаточном уровне коррекции данные все равно восстановятся. Здесь важно не переоценивать модель: Рид-Соломон восстанавливает потери в пределах заложенной избыточности; если повреждено больше, чем позволяет уровень коррекции, код не прочитается.

Сколько именно можно потерять, задает уровень коррекции.

Версии и емкость

Размер QR-кода задается версией — их 40. Версия 1 — это сетка 21×21 модуль, и каждая следующая версия добавляет по 4 модуля к стороне, так что версия 40 — это 177×177 модулей. Чем выше версия, тем больше данных влезает, но тем плотнее и крупнее становится символ.

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

Режим Что кодирует Макс. емкость (версия 40, уровень L)
Цифровой (numeric) цифры 0-9 7089 цифр
Буквенно-цифровой (alphanumeric) 0-9, A-Z (заглавные), пробел и $ % * + - . / : 4296 символов
Байтовый (byte) произвольные байты, по умолчанию ISO 8859-1 2953 байта
Кандзи (kanji) японские иероглифы в Shift JIS 1817 символов

Цифры пакуются плотнее всего, поэтому их влезает больше. Приведенные числа — это максимум: он достигается только на самой большой версии 40 и самом слабом уровне коррекции L. Для URL и текста на кириллице обычно используется байтовый режим (кириллица кодируется в UTF-8 несколькими байтами на символ), и реальная емкость будет заметно ниже табличного максимума.

Ориентир для маленькой версии 1 при уровне L: около 41 цифры, 25 буквенно-цифровых символов или 17 байт — то есть короткая ссылка уже требует версии повыше.

Уровни коррекции L, M, Q, H

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

Уровень Восстановление (примерно) Когда брать
L (Low) до ~7% чистая печать, экран, длинные данные
M (Medium) до ~15% универсальный вариант по умолчанию
Q (Quartile) до ~25% печать, возможны загрязнения/износ
H (High) до ~30% код с логотипом, тяжелые условия

Проценты — это доля кодовых слов, которую способен восстановить декодер, приближенные значения из стандарта. Практическое правило: если планируете логотип в центре или код будет пачкаться и тереться — берите Q или H; если данных много, а условия чистые — L или M.

Как сгенерировать QR-код на Python

Онлайн-генераторов много, но для своих задач (автоматизация, встраивание в приложение, пакетная генерация) удобнее код. В Python для этого есть библиотека qrcode, которая рисует изображение через Pillow.

Установка (в комментарии — что делает команда):

# ставим саму библиотеку и Pillow для сохранения в PNG
pip install "qrcode[pil]"

Минимальный рабочий пример — создать код по ссылке и сохранить в файл:

import qrcode

# qrcode.make сам подберет версию и возьмет уровень коррекции по умолчанию
img = qrcode.make("https://otus.ru/")
img.save("otus_qr.png")
# результат: в текущей папке появится файл otus_qr.png с QR-кодом на otus.ru

Когда нужен контроль над версией, уровнем коррекции и размером — используют класс QRCode:

import qrcode

qr = qrcode.QRCode(
    version=None,                # None + fit=True -> версия подбирается автоматически
    error_correction=qrcode.constants.ERROR_CORRECT_H,  # уровень H - под логотип
    box_size=10,                 # размер одного модуля в пикселях
    border=4,                    # тихая зона: 4 модуля по стандарту
)
qr.add_data("https://otus.ru/")
qr.make(fit=True)

img = qr.make_image(fill_color="black", back_color="white")
img.save("otus_qr_h.png")
print("version:", qr.version)   # напечатает подобранную версию, например: version: 2

Что здесь происходит по шагам: error_correction выбирает уровень коррекции (константы ERROR_CORRECT_L/M/Q/H); box_size задает, во сколько пикселей превратится один модуль; border — ширину тихой зоны; fit=True подбирает минимальную версию под объем данных. Итоговую версию можно прочитать из qr.version уже после make().

Оговорка про среду: код проверялся логически по документации библиотеки qrcode; в песочнице публикации пакет qrcode может быть не установлен, поэтому вывод (version: 2) приведен как ожидаемый, а не снятый прогоном. Конкретная подобранная версия зависит от длины данных и уровня коррекции: под уровнем H та же ссылка потребует версии выше, чем под L.

Безопасность: фишинг через QR-код (квишинг)

У QR есть обратная сторона: человек не видит глазами, куда ведет код, — адрес открывается только после сканирования. Этим пользуются мошенники. Атаку называют квишинг (quishing, от QR + phishing): поверх настоящего кода на парковочном автомате, счете или объявлении наклеивают чужой, ведущий на поддельную платежную страницу или на сайт, ворующий данные.

Как снизить риск:

  • Проверяйте адрес до перехода. Камера и большинство сканеров показывают URL перед открытием — прочитайте домен целиком, а не только начало.
  • Настораживайтесь, если код просит срочно оплатить, ввести данные карты или логин от банка.
  • Осматривайте физический носитель: наклейка поверх основного кода — типичный признак подмены.
  • Не устанавливайте приложения по QR из непроверенных источников — ставьте из официального магазина.

Технически подделать нельзя «взломать» сам формат — подменяют носитель или адрес назначения. Поэтому защита — на стороне внимательности пользователя и проверки конечного URL.

Выводы

  • QR-код — это матричный (2D) штрихкод: данные записаны по площади, поэтому емкость на порядки больше, чем у линейного штрихкода, а читается он в любом положении.
  • Символ состоит из служебных блоков (позиционные и выравнивающие маркеры, синхрополосы, зоны формата, тихая зона) и области данных с проверочными байтами Рида-Соломона, которые восстанавливают код при повреждении.
  • Емкость зависит сразу от версии (1-40, от 21×21 до 177×177 модулей), уровня коррекции (L/M/Q/H) и режима кодирования; приведенные максимумы достигаются только на версии 40 и уровне L.
  • Уровень коррекции — это баланс надежности и объема: L (~7%) под чистые условия, H (~30%) под логотип и износ.
  • На Python код генерируется библиотекой qrcode: qrcode.make(...) для простого случая и класс QRCode для контроля версии, уровня и размера.

Где применяется / связь с практикой

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

Генерация QR-кодов — типичная прикладная задача, на которой удобно тренировать базовые навыки Python: работа с библиотеками, установка пакетов, сохранение файлов, параметры функций и объектов. Тот же набор умений нужен для автоматизации рутины, парсинга и небольших утилит. Системно эти основы (синтаксис, структуры данных, работа с библиотеками) разбирает курс Python Basic. Если хочется сначала посмотреть формат и уровень занятий без оплаты, загляните на открытые уроки Otus — там разбирают практические темы в прямом эфире.

FAQ

Можно ли зашифровать в QR-коде картинку или большой файл?
Практически нет. Даже максимум версии 40 — это около 2953 байт в байтовом режиме, чего хватит на короткий текст или ссылку, но не на изображение. Файлы кодируют косвенно: в QR кладут ссылку на файл, а сам файл хранится в сети.

Почему QR-код читается, даже если он частично поцарапан или закрыт логотипом?
За счет проверочных байтов Рида-Соломона: они позволяют декодеру восстановить утраченные модули в пределах выбранного уровня коррекции (до ~7% при L и до ~30% при H). Если повреждено больше этой доли, код не прочитается.

Есть ли у QR-кода срок годности и можно ли его изменить после печати?
У самого статического кода срока годности нет: он читается, пока данные в нем целы. Но зашитый в него адрес поменять уже нельзя — для этого используют динамические QR, где код ведет на промежуточную ссылку-редирект, а конечный адрес меняют на стороне сервиса.

OTUS Журнал
Скидка 5% 14-20 сентября на курсы (popup)