2025, Dec 04 15:03

Исправляем строку подключения Azure Blob и добавляем CSV в Excel: tab1 первым, tab5 предпоследним

Исправляем DNS‑ошибку из‑за неверной строки подключения Azure Blob. Показано, как загрузить два CSV в Excel с pandas, openpyxl и задать порядок листов tab1, tab5.

При автоматизации обновлений Excel в Azure Blob Storage даже небольшой сбой в настройках способен остановить весь процесс. Частая причина — некорректная строка подключения, из‑за которой ломается DNS‑разрешение; затем добавляется неясность с тем, как прикрепить данные из CSV как новые листы на заданные позиции. Ниже — предметный разбор: как проявляется сбой, в чём корень проблемы и рабочий способ вставить два CSV в существующую книгу так, чтобы один стал первым листом (tab1), а другой — предпоследним (tab5).

В чём проблема

Конвейер создаёт два файла CSV и пытается добавить их в существующую книгу Excel с помощью pandas и openpyxl. Исходный код читает книгу и затем добавляет содержимое CSV как новые листы. Однако запуск падает с ошибкой DNS, вызванной некорректной строкой подключения.

Failed to resolve 'odsblobcontainer.blob.odsblobcontainer.blob.core.windows.net'

Это происходит из‑за неверного формата строки подключения: в конечной точке blob оказывается дублированное имя хоста. SDK не может разрешить получившийся URL, поэтому этап скачивания Excel‑объекта даже не начинается.

Пример с ошибкой

Фрагмент ниже демонстрирует поведение. Он работает с Azure Blob Storage: читает одну книгу и два CSV, пытается записать tab1 и tab5, а затем пере‑загрузить файл. Ошибка конфигурации скрыта в строке подключения.

from azure.storage.blob import BlobServiceClient
import pandas as pd
from io import BytesIO
from datetime import date
run_stamp = date.today().strftime("%Y%m%d")
conn_str = "DefaultEndpointsProtocol=https;AccountName=MyAccount;AccountKey=accountkey;EndpointSuffix=myendpoint.blob.core.windows.net/myproj"
cont_id = "MyContainer/Prj1"
xls_blob = "My Excel.xlsx"
csv_blob_a = "My_csv1_" + run_stamp + ".csv"
csv_blob_b = "My_csv2_" + run_stamp + ".csv"
svc = BlobServiceClient.from_connection_string(conn_str)
xls_client = svc.get_blob_client(container=cont_id, blob=xls_blob)
xls_mem = BytesIO(xls_client.download_blob().readall())
csv_client_a = svc.get_blob_client(container=cont_id, blob=csv_blob_a)
buf_a = BytesIO(csv_client_a.download_blob().readall())
frame_a = pd.read_csv(buf_a)
csv_client_b = svc.get_blob_client(container=cont_id, blob=csv_blob_b)
buf_b = BytesIO(csv_client_b.download_blob().readall())
frame_b = pd.read_csv(buf_b)
with pd.ExcelWriter(xls_mem, mode="a", engine="openpyxl") as xls_writer:
    frame_a.to_excel(xls_writer, sheet_name="tab1", index=False)
    frame_b.to_excel(xls_writer, sheet_name="tab5", index=False)
xls_mem.seek(0)
xls_client.upload_blob(xls_mem, overwrite=True)

Почему это падает

Ошибка DNS возникает из‑за того, как из строки подключения строится endpoint. Значения соединяются неверно, и в адресе blob появляется повторяющееся имя хоста. Правильный формат строки подключения прост и должен выглядеть строго так:

DefaultEndpointsProtocol=https;AccountName=<Your storage account name>;AccountKey=<Your storage account key>;EndpointSuffix=core.windows.net

Как только строка подключения исправлена, SDK корректно резолвит endpoint, и остальная цепочка — скачивание книги, запись CSV как новых листов и обратная загрузка — выполняется без препятствий.

Рабочее решение

Код ниже использует корректно оформленную строку подключения, читает книгу через openpyxl, загружает CSV в DataFrame’ы pandas, удаляет существующие листы tab1 и tab5 (если они были), записывает обновлённые данные, переупорядочивает листы так, чтобы tab1 стал первым, а tab5 — предпоследним, и загружает обновлённую книгу в тот же blob.

from azure.storage.blob import BlobServiceClient
import pandas as pd
from io import BytesIO
from openpyxl import load_workbook
from openpyxl.utils.dataframe import dataframe_to_rows
from datetime import date
run_stamp = date.today().strftime("%Y%m%d")
conn_str = "DefaultEndpointsProtocol=https;AccountName=<storage account>;AccountKey=<key>;EndpointSuffix=core.windows.net"
cont_id = "<container name>"
base_path = "prj1/"
xls_blob = base_path + "My Excel.xlsx"
csv_blob_a = base_path + f"My_csv1_{run_stamp}.csv"
csv_blob_b = base_path + f"My_csv2_{run_stamp}.csv"
svc = BlobServiceClient.from_connection_string(conn_str)
xls_client = svc.get_blob_client(container=cont_id, blob=xls_blob)
xls_stream = BytesIO(xls_client.download_blob().readall())
wb = load_workbook(xls_stream)
def load_csv(blob_name):
    obj = svc.get_blob_client(container=cont_id, blob=blob_name)
    mem = BytesIO(obj.download_blob().readall())
    return pd.read_csv(mem)
frame_a = load_csv(csv_blob_a)
frame_b = load_csv(csv_blob_b)
for nm in ["tab1", "tab5"]:
    if nm in wb.sheetnames:
        wb.remove(wb[nm])
ws1 = wb.create_sheet("tab1")
ws5 = wb.create_sheet("tab5")
for row in dataframe_to_rows(frame_a, index=False, header=True):
    ws1.append(row)
for row in dataframe_to_rows(frame_b, index=False, header=True):
    ws5.append(row)
order = wb.sheetnames.copy()
order.remove("tab1")
order.remove("tab5")
final_order = ["tab1"] + order[:-1] + ["tab5", order[-1]]
wb._sheets = [wb[s] for s in final_order]
out_buf = BytesIO()
wb.save(out_buf)
out_buf.seek(0)
xls_client.upload_blob(out_buf, overwrite=True)

Почему это важно

Проблемы с подключением к хранилищу легко перепутать с другими сбоями, когда симптом появляется далеко от первопричины. Неправильная строка подключения проявляется как ошибка DNS и выглядит как сетевой сбой, но лечится настройкой, а не инфраструктурой. Точная конфигурация особенно важна, когда оркестрация должна гарантированно расставлять листы по местам и стабильно обновлять существующие книги в Blob Storage.

Выводы

В первую очередь проверьте формат строки подключения. Когда связь настроена, явно управляйте созданием листов и их порядком: пусть tab1 становится первым, а tab5 — расположится перед последним листом. Выполнять весь поток в памяти через BytesIO вместе с pandas и openpyxl — простой способ обновлять книги в Azure Blob Storage без временных файлов. При такой настройке конвейер аккуратно объединяет CSV‑выгрузки с Excel‑файлом именно там, где нужно.