从云端存储重写具有云功能的csv文件并将其发送到BigQuery

时间:2019-12-16 21:22:46

标签: python python-3.x google-cloud-functions

我正在研究一个小的云函数python脚本,以重写来自存储的csv文件(跳过某些列)并将其发送到BigQuery。

我脚本的BigQuery部分如下:

def bq_import(request):
    job_config.skip_leading_rows = 1
# The source format defaults to CSV, so the line below is optional.
    job_config.source_format = bigquery.SourceFormat.CSV
    uri = "gs://url.appspot.com/fil.csv"

    load_job = bq_client.load_table_from_uri(
        uri, dataset_ref.table('table'), job_config=job_config
    )  # API request

    load_job.result()  # Waits for table load to complete.

    destination_table = bq_client.get_table(dataset_ref.table('table'))

我找到了此脚本,该脚本允许我跳过一些列来重写csv:

def remove_csv_columns(input_csv, output_csv, exclude_column_indices):
    with open(input_csv) as file_in, open(output_csv, 'w') as file_out:
        reader = csv.reader(file_in)
        writer = csv.writer(file_out)
        writer.writerows(
            [col for idx, col in enumerate(row)
             if idx not in exclude_column_indices]
            for row in reader)

remove_csv_columns('in.csv', 'out.csv', (3, 4))

因此,我基本上需要使这两个脚本在我的云功能中一起工作。但是,我不确定如何处理remove_csv_columns函数,尤其是output_csv变量。我应该创建一个空的虚拟csv文件吗?还是数组或类似的东西?如何即时改写此csv文件?

我认为我的最终脚本应该像这样,但是缺少某些内容...

uri = "gs://url.appspot.com/fil.csv"

def remove_csv_columns(uri, output_csv, exclude_column_indices):
    with open(input_csv) as file_in, open(output_csv, 'w') as file_out:
    reader = csv.reader(file_in)
    writer = csv.writer(file_out)
    writer.writerows(
            [col for idx, col in enumerate(row)
            if idx not in exclude_column_indices]
            for row in reader)

def bq_import(request):
    job_config.skip_leading_rows = 1
# The source format defaults to CSV, so the line below is optional.
    job_config.source_format = bigquery.SourceFormat.CSV
    csv_file = remove_csv_columns('in.csv', 'out.csv', (3, 4))

    load_job = bq_client.load_table_from_uri(
        csv_file, dataset_ref.table('table'), job_config=job_config
    )  # API request

    load_job.result()  # Waits for table load to complete.

    destination_table = bq_client.get_table(dataset_ref.table('table'))

基本上,我认为我需要通过remove_csv_columns来在bq_import函数中定义我的cvs文件,但是我不确定该如何做。

顺便说一下,我正在学习python,但我不是开发人员专家。谢谢。

1 个答案:

答案 0 :(得分:1)

您的代码有很多错误,我将在更正中尝试弄清楚

uri = "gs://url.appspot.com/fil.csv"

我不知道您的函数是如何触发的,但是通常要处理的文件包含在this for event from GCS之类的request对象中。使用存储桶和名称动态构建您的uri

def remove_csv_columns(uri, output_csv, exclude_column_indices):
    with open(input_csv) as file_in, open(output_csv, 'w') as file_out:

注意:您将uri用作函数参数名称,并使用input_csv以读取模式打开输入文件。这里的代码崩溃是因为input_csv不存在!

这里还有另一句话。 uri是函数参数名称,仅在函数内部和外部关系中知道,但调用方(填写此值)除外。它与您在uri = "gs://url.appspot.com/fil.csv"

之前定义的全局变量绝对没有链接。
    reader = csv.reader(file_in)
    writer = csv.writer(file_out)
    writer.writerows(
            [col for idx, col in enumerate(row)
            if idx not in exclude_column_indices]
            for row in reader)

def bq_import(request):
    job_config.skip_leading_rows = 1
# The source format defaults to CSV, so the line below is optional.
    job_config.source_format = bigquery.SourceFormat.CSV
    csv_file = remove_csv_columns('in.csv', 'out.csv', (3, 4))

您的输入文件是静态的。阅读我关于动态uri构建的评论。

看一下函数remove_csv_columns:它什么也不返回,它只是在out.csv中写一个新文件。因此,您的csv_file在这里什么也不代表。另外,这个功能做什么?读取in.csv文件并写入out.csv文件(通过删除列)。您必须将文件传递给此功能

顺便说一句,您必须从Cloud Storage下载文件并将其存储在本地。在Cloud Function中,只有/tmp是可写的。因此您的代码应该像这样

# create storage client
storage_client = storage.Client()
# get bucket with name
bucket = storage_client.get_bucket('<your bucket>')
# get bucket data as blob
blob = bucket.get_blob('<your full file name, path included')
# convert to string
data = blob.download_as_string()
# write the file
with open('/tmp/input.csv', 'w') as file_out:
  file_out.write(data )
remove_csv_columns('/tmp/input.csv', '/tmp/out.csv', (3, 4))

继续您的代码

    load_job = bq_client.load_table_from_uri(
        csv_file, dataset_ref.table('table'), job_config=job_config
    )  # API request

函数load_table_from_uri将文件从存在于Cloud Storage中的文件加载到BigQuery中。在这里,这不是您的目标,您想将本地创建的文件out.csv加载到函数中。正确的电话是load_job = bq_client.load_table_from_file(open('/tmp/out.csv', 'rb'), job_config=job_config)

    load_job.result()  # Waits for table load to complete.

    destination_table = bq_client.get_table(dataset_ref.table('table'))

然后,考虑清理/tmp目录以释放内存,请注意Cloud Function超时,在requirements.txt文件中导入正确的库(至少是Cloud Storage和BigQuery依赖项),然后最后拿起roles of your cloud function


但是,这只是为了提高Python代码和技能。无论如何,此功能都没有用。

确实,如前所述,具有云功能,您只能在/tmp目录上进行写操作。它是一个内存中文件系统,云功能仅限于2Gb内存(包括文件和执行代码占用的内存)。顺便说一句,输入文件的大小不能超过800Mb,对于小文件,更容易。

  • 将您的原始文件加载到BigQuery的临时表中
  • 像这样对您希望的列执行INSERT SELECT BigQuery查询
INSERT INTO `<dataset>.<table>`
SELECT * except (<column to ignore>) from `<dataset>.<temporary table>`
  • 删除您的临时表

由于您的文件很小(小于1GB),并且由于BigQuery免费层是5TB的已扫描数据(您只需支付所扫描的数据而不是进行处理,就可以免费执行所有SQL转换),因此更容易可以将数据处理到BigQuery中,而不是使用Python。

函数处理时间会更长,您可以为函数而不是BigQuery支付处理时间。