![При записи csv из CF в контейнер: 'with open(filepath, "w") as MY_CSV:' приводит к "FileNotFoundError: [Errno 2] Такого файла или каталога нет:"](https://rvso.com/image/774540/%D0%9F%D1%80%D0%B8%20%D0%B7%D0%B0%D0%BF%D0%B8%D1%81%D0%B8%20csv%20%D0%B8%D0%B7%20CF%20%D0%B2%20%D0%BA%D0%BE%D0%BD%D1%82%D0%B5%D0%B9%D0%BD%D0%B5%D1%80%3A%20'with%20open(filepath%2C%20%22w%22)%20as%20MY_CSV%3A'%20%D0%BF%D1%80%D0%B8%D0%B2%D0%BE%D0%B4%D0%B8%D1%82%20%D0%BA%20%22FileNotFoundError%3A%20%5BErrno%202%5D%20%D0%A2%D0%B0%D0%BA%D0%BE%D0%B3%D0%BE%20%D1%84%D0%B0%D0%B9%D0%BB%D0%B0%20%D0%B8%D0%BB%D0%B8%20%D0%BA%D0%B0%D1%82%D0%B0%D0%BB%D0%BE%D0%B3%D0%B0%20%D0%BD%D0%B5%D1%82%3A%22.png)
Я получаю эту ошибку, FileNotFoundError: [Errno 2] No such file or directory
когда пытаюсь записать файл csv в бакет, используя csv writer, который циклически обрабатывает пакеты данных. Полное представление о функциях облака регистрирует эту ошибку:
File "/workspace/main.py", line 299, in write_to_csv_file with open(filepath, "w") as outcsv: FileNotFoundError: [Errno 2] No such file or directory: 'gs://MY_BUCKET/MY_CSV.csv' Function execution took 52655 ms, finished with status: 'crash' OpenBLAS WARNING - could not determine the L2 cache size on this system, assuming 256k ```
И это, хотя этот bucket_filepath определенно существует: я могу загрузить пустой фиктивный файл и получить его «gsutils URI» (щелкните правой кнопкой мыши по трем точкам справа от файла), и bucket_filepath будет выглядеть так же: 'gs://MY_BUCKET/MY_CSV.csv'
.
Я проверил сохранение фиктивного фрейма данных pandas вместо использования, pd.to_csv
и это сработало с тем же bucket_filepath (!).
Следовательно, должна быть другая причина, вероятно, автор не принят, или тот, with statement
кто открывает файл.
Код, который выдает ошибку, следующий. Это тот же код, работающий вне Google Cloud Function в обычном задании cron на локальном сервере. Я добавил два отладочных вывода вокруг строки, которая выдает ошибку, print("Right after opening the file ...")
больше она не отображается. Подфункция query_execute_batch()
, которая write_to_csv_file()
вызывается для каждого пакета, также отображается, но, вероятно, проблема не в этом, поскольку ошибка возникает уже в самом начале при открытии-записи файла csv.
requirements.txt
(которые затем импортируются как модули):
SQLAlchemy>=1.4.2
google-cloud-storage>=1.16.1
mysqlclient==2.1.0
pandas==1.2.3
fsspec==2021.11.1
gcsfs==2021.11.1
unicodecsv==0.14.1
И из main.py
:
def query_execute_batch(connection):
"""Function for reading data from the query result into batches
:yield: each result in a loop is a batch of the query result
"""
results = execute_select_batch(connection, SQL_QUERY)
print(f"len(results): {len(results)}")
for result in results:
yield result
def write_to_csv_file(connection, filepath):
"""Write the data in a loop over batches into a csv.
This is done in batches since the query from the database is huge.
:param connection: mysqldb connection to DB
:param filepath: path to csv file to write data
returns: metadata on rows and time
"""
countrows = 0
print("Right before opening the file ...")
with open(filepath, "w") as outcsv:
print("Right after opening the file ...")
writer = csv.DictWriter(
outcsv,
fieldnames=FIELDNAMES,
extrasaction="ignore",
delimiter="|",
lineterminator="\n",
)
# write header according to fieldnames
writer.writeheader()
for batch in query_execute_batch(connection):
writer.writerows(batch)
countrows += len(batch)
datetime_now_save = datetime.now()
return countrows, datetime_now_save
Имейте в виду, что для работы скрипта выше я импортирую, gcsfs
что делает бакет доступным для чтения-записи. В противном случае мне, вероятно, понадобится объект облачного хранилища Google, например:
storage_client = storage.Client()
bucket = storage_client.bucket(BUCKET_NAME)
а затем создать файл в этом контейнере с дополнительными функциями, но это не является целью здесь.
В следующем pd.to_csv
коде, который работает, он использует вывод фиктивного SQL-запроса SELECT 1
в качестве ввода для dataframe. Этоможетбыть сохранены в том же bucket_filepath, конечно, причина может быть не только pd.to_csv()
в этом, но и в том, что набор данных является фиктивным, а не сложными строками unicode из огромного SELECT query
. Или есть другая причина, я просто предполагаю.
if records is not None:
df = pd.DataFrame(records.fetchall())
df.columns = records.keys()
df.to_csv(filepath,
index=False,
)
datetime_now_save = datetime.now()
countrows = df.shape[0]
Я хотел бы использовать CSV-редактор, чтобы иметь возможность писать в Unicode с помощью модуля Unicodecsv и использовать пакеты.
Я мог бы перейти на пакеты ( loop + append
режим или chunksize
) в пандах, как вЗапись больших Pandas Dataframes в CSV-файл по частямчтобы избавиться от этой проблемы с путями к файлам ведра, но я бы предпочел использовать готовый код (никогда не трогайте работающую систему).
Как мне сохранить этот CSV-файл с помощью CSV-редактора, чтобы он мог открыть новый файл в контейнере в write
режиме = with open(filepath, "w") as outcsv:
?
Данная функция write_to_csv_file()
— это всего лишь крошечная часть Cloud Function, которая использует широкий спектр функций и каскадных функций. Я не могу показать здесь весь воспроизводимый случай и надеюсь, что на него можно ответить с помощью опыта или более простых примеров.
решение1
Решение удивительное. Выдолженимпортируйте и используйте gcsfs
модуль, если вы хотите записать в файл с расширением open()
.
Если вы используете pd.to_csv()
, import gcsfs
не требуется, ноgcsfs
все еще необходимо в том, requirements.txt
чтобы сделать pd.to_csv()
работу, поэтому панды, to_csv()
похоже, используют его автоматически.
Оставив сюрприз pd.to_csv()
в стороне, вот код, который отвечает на вопрос (проверено):
def write_to_csv_file(connection, filepath):
"""Write the QUERY result in a loop over batches into a csv.
This is done in batches since the query from the database is huge.
:param connection: mysqldb connection to DB
:param filepath: path to csv file to write data
return: metadata on rows and time
"""
countrows = 0
print("Right before opening the file ...")
# A gcsfs object is needed to open a file.
# https://stackoverflow.com/questions/52805016/how-to-open-a-file-from-google-cloud-storage-into-a-cloud-function
# https://gcsfs.readthedocs.io/en/latest/index.html#examples
# Side-note (Exception):
# pd.to_csv() needs neither the gcsfs object, nor its import.
# It is not used here, but it has been tested with examples.
fs = gcsfs.GCSFileSystem(project=MY_PROJECT)
fs.ls(BUCKET_NAME)
# wb needed, else "builtins.TypeError: must be str, not bytes"
# https://stackoverflow.com/questions/5512811/builtins-typeerror-must-be-str-not-bytes
with fs.open(filepath, 'wb') as outcsv:
print("Right after opening the file ...")
writer = csv.DictWriter(
outcsv,
fieldnames=FIELDNAMES,
extrasaction="ignore",
delimiter="|",
lineterminator="\n",
)
# write header according to fieldnames
print("before writer.writeheader()")
writer.writeheader()
print("after writer.writeheader()")
for batch in query_execute_batch(connection):
writer.writerows(batch)
countrows += len(batch)
datetime_now_save = datetime.now()
return countrows, datetime_now_save
Примечание
Не используйте CSV-редактор подобным образом.
Это занимает слишком много времени, вместо pd.to_csv()
с chunksize
параметром 5000, которому требуется всего 62 секунды для загрузки 700 тыс. строк и сохранения их в виде csv в бакете, CF с записывающим пакеты занимает более 9 минут, что превышает лимит времени ожидания. Поэтому я вынужден использовать pd.to_csv()
вместо этого и преобразовывать свои данные в dataframe для этого.