it-swarm.dev

Como importar automaticamente dados do arquivo CSV ou XLS carregado para o Planilhas Google

Eu tenho um sistema de banco de dados herdado (não acessível pela Web) em um servidor que gera relatórios CSV ou XLS para uma pasta do Google Drive. Atualmente, estou abrindo esses arquivos manualmente na interface da web do Drive e convertendo-os no Google Sheets.

Prefiro que isso seja automático, para que eu possa criar trabalhos que acrescentem/transformem e representem graficamente os dados em outras planilhas.

É possível gerar um arquivo .gsheet nativo? Ou existe uma maneira de converter CSV ou XLS em .gsheet programaticamente depois de salvá-lo no Google Drive no Google Apps ou por meio de um script/utilitário baseado no Windows?

32
youcantexplainthat

É possível importar programaticamente dados de um arquivo csv no Drive para uma planilha existente do Google usando o Script do Google Apps, substituindo/anexando dados conforme necessário.

Abaixo está um código de exemplo. Ele pressupõe que: a) você tem uma pasta designada no Drive onde o arquivo CSV é salvo/carregado; b) o arquivo CSV é nomeado "report.csv" e os dados nele delimitados por vírgulas; e c) os dados CSV são importados para uma planilha designada. Veja os comentários no código para obter mais detalhes.

function importData() {
  var fSource = DriveApp.getFolderById(reports_folder_id); // reports_folder_id = id of folder where csv reports are saved
  var fi = fSource.getFilesByName('report.csv'); // latest report file
  var ss = SpreadsheetApp.openById(data_sheet_id); // data_sheet_id = id of spreadsheet that holds the data to be updated with new report data

  if ( fi.hasNext() ) { // proceed if "report.csv" file exists in the reports folder
    var file = fi.next();
    var csv = file.getBlob().getDataAsString();
    var csvData = CSVToArray(csv); // see below for CSVToArray function
    var newsheet = ss.insertSheet('NEWDATA'); // create a 'NEWDATA' sheet to store imported data
    // loop through csv data array and insert (append) as rows into 'NEWDATA' sheet
    for ( var i=0, lenCsv=csvData.length; i<lenCsv; i++ ) {
      newsheet.getRange(i+1, 1, 1, csvData[i].length).setValues(new Array(csvData[i]));
    }
    /*
    ** report data is now in 'NEWDATA' sheet in the spreadsheet - process it as needed,
    ** then delete 'NEWDATA' sheet using ss.deleteSheet(newsheet)
    */
    // rename the report.csv file so it is not processed on next scheduled run
    file.setName("report-"+(new Date().toString())+".csv");
  }
};


// http://www.bennadel.com/blog/1504-Ask-Ben-Parsing-CSV-Strings-With-Javascript-Exec-Regular-Expression-Command.htm
// This will parse a delimited string into an array of
// arrays. The default delimiter is the comma, but this
// can be overriden in the second argument.

function CSVToArray( strData, strDelimiter ) {
  // Check to see if the delimiter is defined. If not,
  // then default to COMMA.
  strDelimiter = (strDelimiter || ",");

  // Create a regular expression to parse the CSV values.
  var objPattern = new RegExp(
    (
      // Delimiters.
      "(\\" + strDelimiter + "|\\r?\\n|\\r|^)" +

      // Quoted fields.
      "(?:\"([^\"]*(?:\"\"[^\"]*)*)\"|" +

      // Standard fields.
      "([^\"\\" + strDelimiter + "\\r\\n]*))"
    ),
    "gi"
  );

  // Create an array to hold our data. Give the array
  // a default empty first row.
  var arrData = [[]];

  // Create an array to hold our individual pattern
  // matching groups.
  var arrMatches = null;

  // Keep looping over the regular expression matches
  // until we can no longer find a match.
  while (arrMatches = objPattern.exec( strData )){

    // Get the delimiter that was found.
    var strMatchedDelimiter = arrMatches[ 1 ];

    // Check to see if the given delimiter has a length
    // (is not the start of string) and if it matches
    // field delimiter. If id does not, then we know
    // that this delimiter is a row delimiter.
    if (
      strMatchedDelimiter.length &&
      (strMatchedDelimiter != strDelimiter)
    ){

      // Since we have reached a new row of data,
      // add an empty row to our data array.
      arrData.Push( [] );

    }

    // Now that we have our delimiter out of the way,
    // let's check to see which kind of value we
    // captured (quoted or unquoted).
    if (arrMatches[ 2 ]){

      // We found a quoted value. When we capture
      // this value, unescape any double quotes.
      var strMatchedValue = arrMatches[ 2 ].replace(
        new RegExp( "\"\"", "g" ),
        "\""
      );

    } else {

      // We found a non-quoted value.
      var strMatchedValue = arrMatches[ 3 ];

    }

    // Now that we have our value string, let's add
    // it to the data array.
    arrData[ arrData.length - 1 ].Push( strMatchedValue );
  }

  // Return the parsed data.
  return( arrData );
};

Você pode criar gatilho controlado pelo tempo em seu projeto de script para executar a função importData() regularmente (por exemplo, todas as noites às 01:00), então tudo o que você precisa fazer é colocar um novo relatório. csv na pasta designada do Drive e será processado automaticamente na próxima execução programada.

Se você absolutamente DEVE trabalhar com arquivos do Excel em vez de CSV, pode usar este código abaixo. Para que ele funcione, você deve ativar a API do Drive nos Serviços avançados do Google em seu script e no Console do desenvolvedor (consulte Como ativar os serviços avançados para obter detalhes).

/**
 * Convert Excel file to Sheets
 * @param {Blob} excelFile The Excel file blob data; Required
 * @param {String} filename File name on uploading drive; Required
 * @param {Array} arrParents Array of folder ids to put converted file in; Optional, will default to Drive root folder
 * @return {Spreadsheet} Converted Google Spreadsheet instance
 **/
function convertExcel2Sheets(excelFile, filename, arrParents) {

  var parents  = arrParents || []; // check if optional arrParents argument was provided, default to empty array if not
  if ( !parents.isArray ) parents = []; // make sure parents is an array, reset to empty array if not

  // Parameters for Drive API Simple Upload request (see https://developers.google.com/drive/web/manage-uploads#simple)
  var uploadParams = {
    method:'post',
    contentType: 'application/vnd.ms-Excel', // works for both .xls and .xlsx files
    contentLength: excelFile.getBytes().length,
    headers: {'Authorization': 'Bearer ' + ScriptApp.getOAuthToken()},
    payload: excelFile.getBytes()
  };

  // Upload file to Drive root folder and convert to Sheets
  var uploadResponse = UrlFetchApp.fetch('https://www.googleapis.com/upload/drive/v2/files/?uploadType=media&convert=true', uploadParams);

  // Parse upload&convert response data (need this to be able to get id of converted sheet)
  var fileDataResponse = JSON.parse(uploadResponse.getContentText());

  // Create payload (body) data for updating converted file's name and parent folder(s)
  var payloadData = {
    title: filename, 
    parents: []
  };
  if ( parents.length ) { // Add provided parent folder(s) id(s) to payloadData, if any
    for ( var i=0; i<parents.length; i++ ) {
      try {
        var folder = DriveApp.getFolderById(parents[i]); // check that this folder id exists in drive and user can write to it
        payloadData.parents.Push({id: parents[i]});
      }
      catch(e){} // fail silently if no such folder id exists in Drive
    }
  }
  // Parameters for Drive API File Update request (see https://developers.google.com/drive/v2/reference/files/update)
  var updateParams = {
    method:'put',
    headers: {'Authorization': 'Bearer ' + ScriptApp.getOAuthToken()},
    contentType: 'application/json',
    payload: JSON.stringify(payloadData)
  };

  // Update metadata (filename and parent folder(s)) of converted sheet
  UrlFetchApp.fetch('https://www.googleapis.com/drive/v2/files/'+fileDataResponse.id, updateParams);

  return SpreadsheetApp.openById(fileDataResponse.id);
}

/**
 * Sample use of convertExcel2Sheets() for testing
 **/
 function testConvertExcel2Sheets() {
  var xlsId = "0B9**************OFE"; // ID of Excel file to convert
  var xlsFile = DriveApp.getFileById(xlsId); // File instance of Excel file
  var xlsBlob = xlsFile.getBlob(); // Blob source of Excel file for conversion
  var xlsFilename = xlsFile.getName(); // File name to give to converted file; defaults to same as source file
  var destFolders = []; // array of IDs of Drive folders to put converted file in; empty array = root folder
  var ss = convertExcel2Sheets(xlsBlob, xlsFilename, destFolders);
  Logger.log(ss.getId());
}

O código acima também está disponível como um Gist aqui .

35
azawaza

Você pode fazer com que o Google Drive converta automaticamente arquivos csv para o Planilhas Google anexando

?convert=true

até o final da API que você está chamando.

EDIT: Aqui está a documentação sobre os parâmetros disponíveis: https://developers.google.com/drive/v2/reference/files/insert

Além disso, ao procurar o link acima, achei que essa pergunta já foi respondida aqui:

Faça o upload do CSV para a planilha do Google Drive usando a API do Drive v2

7
Matt

(mar 2017) A resposta aceita não é a melhor solução. Ele se baseia na tradução manual usando o Apps Apps, e o código pode não ser resiliente, exigindo manutenção. Se o seu sistema legado gera automaticamente arquivos CSV, é melhor que eles entrem em outra pasta para processamento temporário (importando [upload para o Google Drive e conversão] para arquivos do Planilhas Google).

Meu pensamento é deixar a API do Drive fazer todo o trabalho pesado. A equipe API do Google Driveversão v no final de 2015 e, nessa versão, insert() alterou os nomes para create() para refletir melhor a operação do arquivo. Também não há mais sinalizador de conversão - basta especificar MIMEtypes ... imagine isso!

A documentação também foi aprimorada: agora existe um guia especial dedicado a uploads (simples, multipart e recuperável) que vem com código de exemplo em Java, Python, PHP, C #/.NET, Ruby, JavaScript /Node.js e iOS/Obj-C que importam arquivos CSV para o formato do Planilhas Google, conforme desejado.

Abaixo está uma solução alternativa para arquivos curtos ("upload simples") Python onde você não precisa do apiclient.http.MediaFileUpload classe. Esse trecho assume que seu código de autenticação funciona onde o terminal em serviço é DRIVE com um escopo de autenticação mínimo de https://www.googleapis.com/auth/drive.file.

# filenames & MIMEtypes
DST_FILENAME = 'inventory'
SRC_FILENAME = DST_FILENAME + '.csv'
SHT_MIMETYPE = 'application/vnd.google-apps.spreadsheet'
CSV_MIMETYPE = 'text/csv'

# Import CSV file to Google Drive as a Google Sheets file
METADATA = {'name': DST_FILENAME, 'mimeType': SHT_MIMETYPE}
rsp = DRIVE.files().create(body=METADATA, media_body=SRC_FILENAME).execute()
if rsp:
    print('Imported %r to %r (as %s)' % (SRC_FILENAME, DST_FILENAME, rsp['mimeType']))

Melhor ainda, em vez de fazer o upload para My Drive, você faria o upload para uma (ou mais) pasta (s) específica (s), o que significa que você adicionaria o ID da pasta pai a METADATA. (Veja também o exemplo de código em esta página .) Finalmente, não há "arquivo" .gsheet nativo - esse arquivo possui apenas um link para a planilha on-line; portanto, o que está acima é o que você deseja fazer .

Se não estiver usando o Python, você pode usar o trecho acima como pseudocódigo para portar para a linguagem do sistema. Independentemente disso, há muito menos código a ser mantido porque não há análise de CSV. A única coisa que resta é remover a pasta temporária do arquivo CSV em que o seu sistema legado gravou.

6
wescpy

No caso de alguém estar pesquisando - eu criei um utilitário para importação automatizada de arquivos xlsx para a planilha do google: xls2sheets . Pode-se fazê-lo automaticamente através da configuração do cronjob para ./cmd/sheets-refresh, o leia-me descreve tudo. Espero que seja útil.

0
Pukeko