【问题标题】:Googlesheet APIv4 getting empty cellsGoogle Sheet API v4 获取空单元格
【发布时间】:2016-07-18 17:25:03
【问题描述】:

我有一个 googlesheet,其中一列可能不包含任何信息。在遍历行并查看该列时,如果该列为空白,则它不会返回任何内容。更糟糕的是,如果我得到一整行并包括那个常见的,比如得到 5 列,当任何一列为空时,我只取回 4 列。如果我得到一行列并且列中的一个单元格为空,如何返回 NULL 或空字符串?

// Build a new authorized API client service.
Sheets service = GoogleSheets.getSheetsService();
range = "Functional Users!A3:E3";
response = service.spreadsheets().values().get(spreadsheetId, range).execute();
values = response.getValues();
cells = values.get(0);

我在一行中得到 5 个单元格。 cells.size() 应该总是返回五个。但是,如果 5 个单元格中的任何一个为空白,它将返回更少的单元格。只说 B3 的单元格是空的。 cells.size() 将是 4。下一次迭代,我得到 A4:E4 并且单元格 D4 为空。同样,cells.size() 将为 4。无法知道缺少哪个单元格。如果 A4 AND D4 AND E4 为空,则 cells.size() 将为 2。

如何让它返回 5 个单元格而不考虑空单元格?

【问题讨论】:

    标签: google-sheets google-sheets-api


    【解决方案1】:

    我解决此问题的方法是将值转换为 Pandas 数据框。我在我的 Google 表格中获取了我想要的特定列,然后将这些值转换为 Pandas 数据框。将数据集转换为 Pandas 数据框后,我进行了一些数据格式化,然后将数据框转换回列表。通过将列表转换为 Pandas 数据框,可以保留每一列。 Pandas 已经为空的尾随行和列创建了空值。但是,我还需要将非尾随行转换为空值以保持一致性。

    # Authenticate and create the service for the Google Sheets API
    credentials = ServiceAccountCredentials.from_json_keyfile_name(KEY_FILE_LOCATION, SCOPES)
    http = credentials.authorize(Http())
    discoveryUrl = ('https://sheets.googleapis.com/$discovery/rest?version=v4')
    service = discovery.build('sheets', 'v4',
        http=http,discoveryServiceUrl=discoveryUrl)
    
    spreadsheetId = 'id of your sheet'
    rangeName = 'range of your dataset'
    result = service.spreadsheets().values().get(
        spreadsheetId=spreadsheetId, range=rangeName).execute()
    values = result.get('values', [])
    
    #convert values into dataframe
    df = pd.DataFrame(values)
    
    #replace all non trailing blank values created by Google Sheets API
    #with null values
    df_replace = df.replace([''], [None])
    
    #convert back to list to insert into Redshift
    processed_dataset = df_replace.values.tolist()
    

    【讨论】:

    • 转换为 pandas 数据框是个好主意。在我的例子中,它特别有用,因为源数据合并了列,使得解析更加复杂。
    • 您可以使用 fillna() 将 null 转换为 ''。这可能更有效,因为我假设空值是稀疏的。哦,实际上运行时效率可能是一样的。我不知道 pandas 是如何实现 fillna 和 replace 的。 \
    【解决方案2】:

    我已经涉足 Sheetsv4,这确实是您在读取一系列包含空数据的单元格时的行为。似乎这就是它的设计方式。如Reading data docs中所述:

    空的尾随行和列被省略。

    因此,如果您能找到一种方法来编写一个表示“空值”的字符,例如零,那么这将是一种方法。

    【讨论】:

    • 我不明白为什么会这样实现。至少它应该是一种选择。为什么要用户指定范围,然后根据用户可能无法控制的事情返回可能不同的范围?我想要一个范围,给我我要求的范围,不多不少,就像对任何其他 API 的所有其他 API 请求一样,永远。
    • @Milton 我认为谷歌希望节省响应的有效负载。同意这很愚蠢,但它在下降的过程中节省了一些字节。
    【解决方案3】:

    我在使用表格 API 的 V4 时遇到了同样的问题,但我能够使用范围末尾的额外列和 values.get API 的 valueRenderOption 参数来解决此问题

    给定三列 A、B 和 C,其中任何一个都可能包含空值,添加一个附加列 D,并在此处添加任意值,例如“空白”。

    确保捕获范围内的新列并添加附加参数,

    valueRenderOption: 'FORMATTED_VALUE'

    你应该会得到一个类似这样的调用:

    sheets.spreadsheets.values.get({
      spreadsheetId: SOME_SHEET_ID,
      range: "AUTOMATION!A:D",
      valueRenderOption: 'FORMATTED_VALUE'
    }, (err, res) => {})
    

    这应该为每个值提供一个一致的长度数组,返回一个空白字符串“”来代替空单元格值。

    【讨论】:

    • 在最后一列中有空单元格之前,我没有使用附加列。但现在我这样做了 ;-) 。指定 valueRenderOption=FORMATTED_VALUE 现在在列表中给了我一些 u'' 这正是我需要的!你拯救了我的一天。谢谢
    【解决方案4】:

    如果您从 google sheet API v4 中提取一个范围,则如果空行数据位于所选范围的开头或中间,则会包含空行数据。 仅省略范围末尾没有数据的单元格。使用此假设,您可以“填充”应用代码中的无数据单元格。

    例如,如果您选择 A1:A5 并且 A1 没有值,它仍将在行数据中返回为 {}

    如果缺少 A5,那么您将拥有一个长度为 4 的数组,因此知道要填充空的 A5。 如果 A4 和 A5 为空,那么您将拥有一个长度为 3 的数组,依此类推。

    如果没有一个范围包含数据,那么您将收到一个空对象。

    【讨论】:

      【解决方案5】:

      我能找到的唯一解决方案是编写自己的函数:

      def _safe_get(data, r, c):   
          try:
              return data[r][c]
          except IndexError:
              return ''
      
      def read(range_name, service):
          result = service[0].spreadsheets().values().get(spreadsheetId=service[1],
                                                      range=range_name).execute()
          return result.get('values', [])
      
      def safe_read(sheet, row, col, to_row='', to_col='', service=None):
              range_name = '%s!%s%i:%s%s' % (sheet, col, row, to_col, to_row)
              data = read(range_name, service)
      
          if to_col == '':
              cols = max(len(line) for line in data)
          else:
              cols = ord(to_col.lower()) - ord(col.lower()) + 1
          if to_row == '':
              rows = len(data)
          else:
              rows = to_row - row + 1
      
          return [[_safe_get(data, r, c)
                   for c in range(cols)]
                  for r in range(rows)]
      

      【讨论】:

        【解决方案6】:

        我知道这已经很晚了,但是以防万一以后遇到此问题的其他人想要解决此问题,我将分享我为解决此问题所做的工作。 我所做的是将我正在寻找的单元格范围的长度增加一个。然后在我正在阅读的谷歌电子表格中,我在额外的列中添加了一行“。”(由于所需的单元格范围已经增加,该列添加到数组中)。然后我保护了那一行句号,使其不能从“。”中更改。 这种方式为您提供了一个包含您正在寻找的所有内容的数组,包括空结果,但确实将您的数组大小增加了 1。但如果这让您感到困扰,您可以创建一个没有数组最后索引的新数组。

        【讨论】:

        • 这应该是有效的答案。
        【解决方案7】:

        如果行中的最后一个单元格有值,则该行将被完全返回 例如:

        行:

        |Nick|29 years|Minsk|
        |Mike|        |Pinsk|
        |Boby|        |     |
        

        返回:

        [
          ["Nick", "29 years", "Minsk"],
          ["Mike", "", "Pinsk"]
          ["Boby"]
        ]
        

        因此,当您添加一个包含空单元格而不是空单元格的新行时(""null)只需使用空格 " "

        然后,当您读取值时,只需将所有项目从空间 " " 映射到空 ""

        行:

        |Nick|29 years|Minsk|
        |Mike|        |Pinsk|
        |Boby|        |"  " |
        

        返回:

        [
          ["Nick", "29 years", "Minsk"],
          ["Mike", "", "Pinsk"]
          ["Boby", "", " "]
        ]
        

        【讨论】:

          【解决方案8】:

          另一个选项是遍历返回的行,检查行的长度并附加您期望返回的任何数据。我发现这比将垃圾数据添加到我的数据集更可取。

          【讨论】:

            【解决方案9】:

            我迟到了,但这里有另一种选择:

            def read_sheet(service, SPREADSHEET_ID, range) -> pd.DataFrame:
            
                result = service.spreadsheets().values().get(spreadsheetId=SPREADSHEET_ID, range=range).execute()
            
                rows = result.get('values', [])
            
                df = pd.DataFrame(rows[0:])
            
                df.columns = df.iloc[0]
            
                df = df.drop(axis=0, index=0)
            
                return df
            

            要使此解决方案起作用,您需要在要阅读的电子表格的所有列中使用标题(列名)。它将加载没有标题(列名)规范的 pandas df,将列名替换为第一行,然后将其删除。

            【讨论】:

              【解决方案10】:

              Sheets API V4,应该返回所有空白直到最后填充的列。

              这将填补空白:

              values = result.get('values', [])
              print(values[1:5]) # [['Spinach Lasagna', '10', '5', '', 'x'], ['Hot Dish', '10', '5', '', '', '', 'x'], ['Tuna-Noodle Casserole', '10', '5', '', 'x', '', '', 'x'], ['Sausage and Peppers', '10', '3', '', '', '', '', '', 'x']]
              
              n_col = 14                                # hard code
              n_col = max([len(i) for i in values])     # if last column is occupied at least once
              n_col = len(values[0])                    # if you have header
              
              values = [lst + ([''] * (n_col - len(lst))) for lst in values]
              
              print(values[1:4]) # [['Spinach Lasagna', '10', '5', '', 'x', '', '', '', '', '', '', '', '', ''], ['Hot Dish', '10', '5', '', '', '', 'x', '', '', '', '', '', '', ''], ['Tuna-Noodle Casserole', '10', '5', '', 'x', '', '', 'x', '', '', '', '', '', '']]
              
              

              【讨论】:

                【解决方案11】:

                只需添加:

                values.add("");
                

                之前:

                cells = values.get(0);
                

                这将确保您不会因为空白单元格或一行而查询空列表。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多