Выгрузка из CRM: как прочитать и очистить CSV без потерь
За один проход подготовьте CSV из CRM к работе: проверьте разделитель и заголовки, уберите пустые строки и лишние пробелы, сохраните исходные идентификаторы. Показываем безопасный сценарий на Python 3.12.

За один проход вы подготовите CSV-выгрузку из CRM к анализу или передаче в другую систему: проверите структуру, удалите пустые строки и пробелы по краям ячеек, не затрагивая исходный файл. На небольшом экспорте это обычно занимает 20–40 минут; ниже — последовательность и готовый скрипт для Python 3.12.
Что нужно до начала
Понадобятся сама выгрузка и компьютер с Python 3.12. Скрипт использует только стандартную библиотеку — ставить сторонние пакеты не нужно. Работайте с копией: экспорт часто содержит клиентские данные, а автоматическая очистка может изменить значения, которые вы хотели сохранить.
- Скачайте файл из CRM и сохраните оригинал отдельно, например в папке
raw. Не открывайте и не пересохраняйте его в Excel до проверки: табличный редактор может изменить даты и длинные числа. - Уточните формат экспорта: CSV это или XLSX, какая кодировка и какой символ разделяет столбцы. Для CSV из русскоязычных настроек часто используют точку с запятой, но это нужно проверить в самом файле.
- Подготовьте папку для результата и убедитесь, что в ней нет файла с тем же именем, которое вы укажете для очищенной выгрузки.
Скрипт ниже предназначен именно для CSV. Если CRM выгрузила XLSX, сначала экспортируйте данные в CSV средствами CRM или используйте отдельный инструмент для чтения Excel-книг: CSV-парсер не прочитает файл XLSX.
Пошаговая очистка CSV
- Посмотрите на первые строки файла. Откройте копию в текстовом редакторе, который показывает исходное содержимое, а не только таблицу. Проверьте, где заголовок, чем разделены значения и заключены ли ячейки в кавычки. Например, строка
company_id;email;created_atуказывает на точку с запятой. Проверка: вы можете назвать разделитель и видите строку заголовков. - Решите, какие поля нельзя преобразовывать. ID клиента, номер заказа, телефон и почтовый индекс часто выглядят как числа, но это строки: ведущие нули и знаки «+» могут быть важны. Даты с часовым поясом тоже нельзя приводить к одному формату наугад. Проверка: вы выписали поля, значения которых нужно сохранить буквально.
- Создайте скрипт. Сохраните код ниже в файл
clean_crm_csv.py. Он принимает разделитель как аргумент, читает UTF-8 с возможной BOM-меткой, проверяет заголовки и число полей в каждой записи, пропускает полностью пустые строки и удаляет пробелы только по краям ячеек. Источник не перезаписывается. - Запустите обработку. Для файла с точкой с запятой выполните команду
python clean_crm_csv.py export.csv cleaned.csv --delimiter ";". Если в вашей системе команда Python называетсяpython3, используйте её. Для запятых передайте--delimiter ",". Проверка: программа сообщила, что записала результат, а рядом появилсяcleaned.csv. - Сверьте результат с исходником. Откройте обе копии в редакторе или импортируйте их в тестовую таблицу, явно указав разделитель и типы столбцов. Сравните число записей, заголовки, несколько ID с нулями в начале, телефоны и даты. Проверка: число строк изменилось только за счёт удаления пустых записей, а контрольные значения совпадают.
Скрипт рассчитан на CSV разумного размера и не удаляет дубликаты, не нормализует телефоны и не меняет даты. Это намеренно: одинаковые email не всегда означают одного клиента, а запись времени без часового пояса нельзя безопасно преобразовать без правил вашей CRM.
import argparse
import csv
import os
import sys
from pathlib import Path
def main():
parser = argparse.ArgumentParser(
description="Проверить и консервативно очистить CSV-выгрузку CRM"
)
parser.add_argument("source", type=Path, help="Исходный CSV-файл")
parser.add_argument("destination", type=Path, help="Новый файл результата")
parser.add_argument(
"--delimiter",
required=True,
choices=[",", ";"],
help="Разделитель столбцов: запятая или точка с запятой",
)
args = parser.parse_args()
source = args.source
destination = args.destination
temporary = destination.with_name(destination.name + ".tmp")
if source.resolve() == destination.resolve():
parser.error("Исходный файл и файл результата должны быть разными")
if destination.exists() or temporary.exists():
parser.error("Файл результата или временный файл уже существует")
records_written = 0
try:
with source.open("r", encoding="utf-8-sig", newline="") as input_file:
reader = csv.reader(
input_file, delimiter=args.delimiter, strict=True
)
try:
header = next(reader)
except StopIteration:
raise ValueError("Файл пуст")
header = [name.strip() for name in header]
if not header or any(not name for name in header):
raise ValueError("В заголовке есть пустое имя столбца")
if len(header) != len(set(header)):
raise ValueError("После очистки заголовки содержат дубликаты")
with temporary.open("w", encoding="utf-8", newline="") as output_file:
writer = csv.writer(
output_file,
delimiter=args.delimiter,
lineterminator="\n",
)
writer.writerow(header)
for record_number, row in enumerate(reader, start=2):
if not row or all(not cell.strip() for cell in row):
continue
if len(row) != len(header):
raise ValueError(
f"Запись {record_number}: ожидалось полей "
f"{len(header)}, получено {len(row)}"
)
writer.writerow([cell.strip() for cell in row])
records_written += 1
os.replace(temporary, destination)
except (OSError, UnicodeError, csv.Error, ValueError) as error:
temporary.unlink(missing_ok=True)
print(f"Ошибка: {error}", file=sys.stderr)
return 1
print(f"Готово: записей {records_written}, файл {destination}")
return 0
if __name__ == "__main__":
raise SystemExit(main())
В результате заголовки и значения лишаются пробелов по краям, но внутренние пробелы и сами данные остаются. Если пробелы в поле могут быть значимыми, не запускайте скрипт до проверки таких столбцов. Имя результата должно быть новым: существующий файл программа не заменит.
Если не получилось
UnicodeDecodeError. Файл не читается как UTF-8. Не перебирайте кодировки вслепую: проверьте настройки экспорта в CRM или определите кодировку исходника инструментом, который её показывает. После этого замените значениеencodingв скрипте только на установленную кодировку.- «Ожидалось полей …, получено …». В записи другое число столбцов. Проверьте выбранный разделитель, кавычки и конкретную запись. Если в описании клиента встречается сам разделитель, значение должно быть корректно заключено в кавычки в исходном CSV.
- Заголовки содержат дубликаты. В CRM могли выгрузиться одноимённые поля; пробелы вокруг названий скрипт тоже убирает, поэтому, например,
emailиemailстановятся дублем. Переименуйте столбцы в настройке экспорта либо отдельно задайте однозначные имена до дальнейшей загрузки. - В таблице всё оказалось в одном столбце. При импорте выбран не тот разделитель. Повторите импорт результата и укажите ту же запятую или точку с запятой, с которой запускали скрипт.
Если проблема связана с кавычками или переносами внутри ячейки, не исправляйте файл массовой заменой символов: можно склеить записи или изменить текст обращения. Сохраните проблемный оригинал и запросите экспорт повторно.
Что проверить перед загрузкой дальше
Очищенный CSV — ещё не гарантия корректного импорта. Сначала загрузите небольшую тестовую выборку в целевую систему или её тестовый контур. Убедитесь, что поле ID не стало числом, даты не сдвинулись из-за часового пояса, пустые значения не превратились в текст None или NULL, а число записей совпадает с ожидаемым.
Сначала сохраните исходные значения, затем решайте, какие из них нормализовать. Удалить лишние пробелы легко; восстановить потерянный ноль в ID или исходное время события — часто уже нельзя.
Сноска. Схема данных — это перечень столбцов, их значений и правил: например, какое поле уникально и в каком формате хранится дата. Перед сопоставлением выгрузки с другой системой полезно получить эту схему у администратора CRM.
Что делать дальше
Сохраните рядом с выгрузкой короткую заметку: из какой CRM получен файл, когда он выгружен, какой разделитель выбран и какие преобразования выполнены. Затем сравните контрольные записи с данными в CRM и проверьте импорт на тестовой выборке. Если такие экспорты повторяются, автоматизируйте проверки заголовков и типов полей, а не добавляйте в скрипт недокументированное удаление дублей.
Разрушитель авто-разводов: Фото заказ-наряда СТО: завышения, дубли работ, лишние позиции.
Спросить за 15 ₽Источники: Документация Python 3.12: модуль csv
Частые вопросы
Можно ли открыть выгрузку в Excel и сохранить её обратно?
Что делать, если CRM выгрузила XLSX, а не CSV?
Скрипт автоматически удалит повторяющихся клиентов?
Подойдёт ли пример для другой версии Python?
Что делать, если после очистки даты или ID выглядят иначе?
Можно ли очищать таким способом персональные данные клиентов?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.