Cansada de ser feliz

Bienvenidos a mi flujo de conciencia

Abrir hojas de cálculo de Google desde Python

| Comments

A veces nuestros clientes nos mandan sus hojas de cálculo en los formatos xls o csv, para que podamos sacar los datos de allá y guardarlos en nuestra base de datos. Creo que muchos han enfrentado ese problema, cuando tenemos que escribir un formulario para subir el archivo, validar la extención y el formato, a menudo la codificación de caracteres, tamaño máximo del archivo, etc. Es tan aburrido pelear con todo eso, que para facilitar la vida decidí usar las hojas de cálculo de Google (Google Spreadsheet). Además es muy fácil procesarlas desde el código de Python.

Para eso uso una librería llamada gspread. gspread necesita los credenciales para poder acceder los archivos de Google Drive, que se puede obtener desde la consola para desarrolladores de Google.

1
2
3
4
5
6
7
8
9
10
11
12
13
GOOGLE_CREDENTIALS_SERVICE_ACCOUNT = {
  "type": "service_account",
  "project_id": "...",
  "client_id": "...-compute@developer.gserviceaccount.com",
  "private_key_id": "...",
  "private_key": "...",
  "client_email": "...-compute@developer.gserviceaccount.com",
  "client_xlsid": "...",
  "auth_uri": "https://accounts.google.com/o/oauth2/auth",
  "token_uri": "https://accounts.google.com/o/oauth2/token",
  "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
  "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/...-compute%40developer.gserviceaccount.com"
}

Especificamos en la varible scope, que queremos acceso a Google Spreadsheet.

1
2
import gspread
scope = ['https://spreadsheets.google.com/feeds']

y autorizamos gspread:

1
2
3
4
5
6
7
from oauth2client.service_account import ServiceAccountCredentials

credentials = ServiceAccountCredentials._from_parsed_json_keyfile(
    keyfile_dict=GOOGLE_CREDENTIALS_SERVICE_ACCOUNT,
    scopes=scope,
)
gc = gspread.authorize(credentials)

Avisamos a nuestro usuario que debe compartir su hoja de cálculo con el correo que está en GOOGLE_CREDENTIALS_SERVICE_ACCOUNT['client_email'] y copiar el enlace del documento a nuesto formulario:

Teniendo el enlace, podemos abrir el documento desde Python:

1
sht = gc.open_by_url(form.spreadsheet_url.data)

Ahora se puede obtener la primera hoja y el listado de valores:

1
2
worksheet = sht.get_worksheet(0)
data = worksheet.get_all_values()

La variable data va a tener listado de listados, que corresponden a lineas y columnas de la hoja.

El código completo con el manejo de errores posibles:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import gspread
from gspread.exceptions import SpreadsheetNotFound, NoValidUrlKeyFound
from oauth2client.service_account import ServiceAccountCredentials
from oauth2client.client import HttpAccessTokenRefreshError

try:
    scope = ['https://spreadsheets.google.com/feeds']
    credentials = ServiceAccountCredentials._from_parsed_json_keyfile(
        keyfile_dict=GOOGLE_CREDENTIALS_SERVICE_ACCOUNT,
        scopes=scope,
    )
    gc = gspread.authorize(credentials)
    sht = gc.open_by_url(form.spreadsheet_url.data)
    worksheet = sht.get_worksheet(0)
    data = worksheet.get_all_values()
    # do something
    flash(u'Thank you! We are processing your file.', 'success')
except SpreadsheetNotFound:
    flash(u'Trying to open non-existent or inaccessible spreadsheet. '
          u'Please, make sure that your file is shared with {}.'.format(
              GOOGLE_CREDENTIALS_SERVICE_ACCOUNT['client_email']
          ), 'danger')
except NoValidUrlKeyFound:
    flash(u'Please, make sure that your URL is correct.', 'danger')
except HttpAccessTokenRefreshError:
    flash(u'Error (with HTTP status) '
          u'trying to refresh an expired access token', 'danger')

Comments