Enlace de imágenes en una tienda WordPress con un fichero csv

/ octubre 31, 2020/ Ciencia de información y datos, Porfolio

Puedes ver y descargar el cuaderno en https://github.com/Erebyel/Wp_img_upload

Quiero compartir y describir el proceso que seguí en uno de los últimos trabajos que he realizado. No pretende ser una fórmula, pero sí que puede servir cómo guía para enfrentarse a un problema de este tipo, ajustándolo a los retos particulares que suponga el caso concreto.

Sé que, por ahí, todavía hay muchos diseñadores gráficos que, a la hora de montar una tienda en WordPress, se ven bastante apurados a la hora de subir y añadir las fotos de los productos en la tienda (sobre todo, si son cantidades importantes).

Como cada cliente es un mundo y facilitan la información estructurada a su manera, se puede optar por limpiar y estructurar correctamente la información dentro del archivo y luego subirlo o, en base al archivo que han enviado, ir añadiendo los productos uno a uno. La opción rápida, si sabes cómo gestionar el .csv para que la tienda de WordPress la entienda correctamente, es tratar el archivo y luego cargarlo en la tienda; lo que requiere de saber de un poco de codificación de archivos, de cómo necesita la tienda que esté puesto cada campo, etc.

Problema

Sé que, en este caso, podría haber hablado del tratamiento de los datos que realicé hace meses para conseguir un .csv que no contuviera errores a la hora de importarlo a WordPress, pero fue hace meses y ya está en su sitio; realizar todo el proceso nuevamente solo para explicarlo… bueno, igual para el siguiente. Por eso, y como es un caso muy reciente (de esta semana) que está fresquito todavía en mi cabeza para poder explicarlo, me centro en la actualización de registros en una tienda WordPress para añadir imágenes.

Normalmente, cuando hablamos de compañías un poco más grandes donde tienen bases de datos y sistemas con cierto grado de automatización, si se ha hecho correctamente, las imágenes deberían almacenarse con una referencia unívoca que la relacione con el producto. Este sería un caso fácil porque ese número, normalmente, habrá actuado como SKU a la hora de dar de alta el producto en la web y tendrá un formato estandarizado (podría usarse, por ejemplo, el código de barras de cada producto).

Cuando la empresa todavía es joven, no ha llevado un proceso de estandarización de referencias o trabaja con productos que no utilizan un código de barras específico, es habitual encontrarse con casos particulares que pueden hacer casi imposible hacer ciertos trabajos de forma más o menos automática.

En el caso que voy a desarrollar a continuación, el conjunto de 525 imágenes llegó nombrado con el nombre de los productos, lo que suponía ciertos problemas para el proceso:

  1. No existía una relación unívoca de la imagen con el producto al que correspondía.
  2. Las URL de las imágenes contenían caracteres que serían sustituidos (ñ, acentos, etc.) y complicarían la automatización de las URL.

Estos eran los dos problemas principales a los que me enfrentaba, el único punto a favor es que no había que retorcer mucho más el problema, porque no existían varias imágenes por producto (cosa que lo hubiese complicado).

Al menos tenía cierta pauta en los nombres de los productos, así que comencé por lo más sencillo: exportar la tienda de WordPress y descargarla para poder buscar alguna forma de automatizar el proceso.

import pandas as pd
df = pd.read_csv('gatos_ejemplo.csv', sep=';')

Estaba claro que el campo Nombre sería clave para trabajar y enlazar la mayor cantidad de imágenes, pero seguía teniendo el problema de las URL, que podía solucionarlo si renombraba los archivos por el SKU, que normalmente seguía ciertas normas y no solía contener caracteres no admitidos en la codificación de la URL. Aquí me encontré con el tercer problema:

  1. No hay SKU estandarizada y había productos que no tenían ninguna asignada.

Por suerte, cuando subes un producto a una tienda WordPress, esta se referencia con un ID que puede actuar igual que un SKU y que, además, es completamente numérico: ¡perfecto! Así que decidí usarlo como referencia para las imágenes.

Presentado el problema, sigamos con la ejecución del trabajo que realicé.

Datos facilitados para el ejemplo

Por supuesto, la LPDP (y el sentido común) hace que no vaya a facilitar los datos originales con los que estuve trabajando; aunque sí que me referiré a él para hacer anotaciones que creo que serán de utilidad. Para poder gestionar el ejemplo, he decidido preparar un conjunto de datos específico y ficticio: un conjunto de imágenes con, bueno, fotografías de mis gatas.

También he construido un conjunto de datos extraño en el que todavía no tengo claro si estoy vendiendo gatos o las fotos de mis gatas. Aclaro que, dentro de lo absurdo, esto solo sirve para ejemplificar a muy pequeña escala con el trabajo con el que me encontré.

Mirando qué extensiones tienen las fotografías

Es importante comprobar que las extensiones de las imágenes son todas iguales y no tienes mezcladas imágenes en .jpg y .png. Si no las tienes todas en formato .png sería recomendable que, antes de seguir, te tomaras un segundo para realizar la conversión (porque usas conversor automático, ¿verdad?). Al menos, en mi caso, estaban todas (en el problema, en .png y, en este ejemplo, estarán todas en .jpg).

Hay que entender que, a veces, para un volumen pequeño de datos que gestionar, no compensa dedicar un tiempo a pensar y hacerlo más automático, esto solo serviría para volúmenes donde la tarea se convertiría en un infierno de días monótonos haciendo lo mismo.

Obviamente, no vamos a incluir el listado de imágenes en una lista de forma manual, la base de todo esto es ser muy eficientes ahora par ser después muy, muy perezosos 😉

from os import scandir, getcwd
def ls(ruta = getcwd()):
    return [arch.name for arch in scandir(ruta) if arch.is_file()]
img = ls('./c_gatos/')

Con esto podemos comenzar a trabajar y buscar una solución que nos haga más rápido el trabajo. En este punto, es bueno preguntarse lo que queremos conseguir. En mi caso concreto: renombrar las imágenes según el ID del producto en el csv.

Igual, tanto lío para 13 fotos no merece la pena, pero cuando son 525 el coste de tiempo es brutal; por lo que, perder un ratito comprobando si podía quitarme algo de trabajo comparando los nombres de las fotos con los nombres del csv era razonable y muy recomendable.

Para poder comparar el nombre de los archivos con el del conjunto de datos, lo primero que debía hacer era quitar la extensión del archivo y buscar que el trabajo manual se redujera al mínimo.

import re

l_nombres = []

for i in img:
    i = re.sub(r'.jpg', '', i).lower().strip()
    l_nombres.append(i)
df_nombres = []

for n in df['Nombre']:
    df_nombres.append(n.lower().strip())
coincidencia_nombres = []

for n in l_nombres:
    if n in df_nombres:
        coincidencia_nombres.append(n)
    else:
        continue

Para poder hacerme una idea de lo que ocurría y de si merecía la pena seguir por este camino, hice una comparación de los nombres de los archivos con los que aparecían en el csv; en este ejemplo está claro que no, pero en el real, con el que tenía que cruzar 525 fotos con 522 registros me ahorró tener que renombrar 433 imágenes.

Podría haber limado y mejorado el código un poco, lo reconozco, pero parte de este ejercicio es mostrar el desarrollo hacia la solución que llevé a cabo, lo que significa, dejar algunas cosillas… raras.

El siguiente paso para conseguir renombrar las imágenes era aislar lo que quiero relacionar y, bueno, ejecutar la acción a realizar; para ello, aislé las dos columnas del conjunto que me interesaban y lo convertí en una lista de tuplas NombreID; igual haberlo hecho como un diccionario hubiese sido más lógico, pero en el momento se me antojó una tupla, ¿vale?

Volví a cribar los resultados del csv para que volvieran a coincidir con los que había detectado en coincidencia_nombres y le añadí a todo la extensión de la imagen nuevamente.

df2 = df[['Nombre', 'ID']]
df2 = [tuple(x) for x in df2.to_records(index=False)]
comparacion = []
for item in df2:
    if item[0].lower() in coincidencia_nombres:
        comparacion.append(item)
imagenes = []
for x in comparacion:
    tupla = []
    for i in x:
        f = str(i).lower() + '.jpg'
        tupla.append(f)
    imagenes.append(tupla)

Con esta información solo quedaba renombrar los archivos, aproveché también para separarlos de las imágenes que no habían pasado por el proceso moviéndolas a una nueva carpeta.

import shutil
loc_descarga = './c_gatos/'
loc_destino = './c_gatos/renombradas/'
for x in imagenes:
    shutil.move(loc_descarga + x[0], loc_destino + x[1])

Una vez terminé el proceso, ya tenía en una carpeta diferente todas las imágenes que se habían autorreconocido y cambiado el nombre; en mi caso, quedaron unas 100 y decidí que tocaba hacerlas a mano ya que eran problemas de transcripción o de cambio en el orden de palabras, así que llegué a la conclusión de que no servía de nada huir del problema de esas 100 ni buscar otra manera, y comencé con la tarea. Por supuesto, antes me hice una lista de los archivos que no tenían una relación con los elementos de la base de datos. Para ello:

  1. Realicé una nueva lista con los nombres de la carpeta de coincidencias
  2. Eliminé el .png y lo convertí en numérico
  3. Hice lo propio con los ID del conjunto haciendo una lista de elementos discriminados
  4. Utilicé la tupla que había creado antes de (‘Nombre’, ‘ID’) para recuperar aquellos que aparecieran en la lista de elementos discriminados.
img2 = ls('./c_gatos/renombradas/')
l_nombres2 = []

for i in img2:
    i = int(re.sub(r'.jpg', '', i).lower().strip())
    l_nombres2.append(i)
df_nombres2 = []

for n in df['ID']:
    if n not in l_nombres2:
        df_nombres2.append(n)
comparacion = []
for item in df2:
    if item[1] in df_nombres2:
        comparacion.append(item)

Con los datos ya filtrados, tocó dedicar un ratito a renombrar las imágenes.

Trabajar de esta manera, además, permite realizar de forma sencilla dos verificaciones sobre los conjuntos de imágenes y de datos:

  1. Aquellas imágenes que no están todavía en la tienda.
  2. Productos de los que todavía no se tienen las imágenes. Esto teniendo en cuenta que sea la primera subida de imágenes al catálogo de productos; para poder averiguar esto después, debería recurrirse a los nulos dentro del campo Imágenes. Pero eso ya es otra historia.

Con esto, podemos notificar al cliente sobre las fotos que tenemos y no deberíamos (o sí, pero del que todavía no nos ha hecho llegar el resto de la información), y las que todavía nos faltarían para que todos los productos tengan al menos una.

Generar el archivo de carga para la tienda de WordPress

Una vez tengamos todos los archivos que necesitamos para nuestros productos renombrados como la tienda manda, toca generar un archivo .csv que nos permita hacer el trabajo rápido y teniéndolo todo ordenadito, con el nombre como dios manda, tenemos todo lo necesario para generarlo en un abrir y cerrar de ojos.

Actualización de tienda a través de WordPress

Hay dos cosas fundamentales y muy importantes a la hora de trabajar de esta manera:

1. Recuerda siempre, antes de cargar un nuevo .csv que actualice información, descargarte una copia que te sirva para restaurarla por si la lías parda con alguna tontería.

2. No es lo mismo añadir que actualizar, por lo que WordPress hará una cosa u otra con el archivo (normalmente, actualizará lo que ya tiene). Para actualizar, lo recomendable es solo introducir en el archivo los campos de identificación (ID) y los campos a los que afecta la actualización. Igual de importante es analizar si ha aparecido algún error en la importación, en qué productos y porqué se ha producido.

A modo de actualización, volveremos a importar las imágenes del directorio, que ya estarán renombradas por el ID y, con ello, matamos dos pájaros de un tiro:

img3 = ls('./c_gatos/renombradas/')
l_ID = []

for i in img3:
    i = re.sub(r'.jpg', '', i).lower().strip()
    l_ID.append(i)
inicio = 'https://hosting.gatos.com/wp-content/uploads/2021/11/'
fin = '.jpg'

lista_url = []
for x in l_ID:
    n = inicio + x + fin
    lista_url.append(n)
df3 = pd.DataFrame({"ID": l_ID, "Imágenes": lista_url})
df3.to_csv('act_fotos_gatos.csv', index=False)

Solo queda importar a la web el archivo .csv y dejar que trabaje; una vez finalizada la carga, ya están todas las imágenes subidas y enlazadas con sus productos. Solo queda hacer alguna comprobación aleatoria sobre los productos subidos, para confirmar que se ha realizado correctamente (pero esto, incluso si se ha hecho manualmente, también debería estar en la rutina de trabajo).

Conclusiones:

Creo que haber hecho este trabajo así supone un ahorro de tiempo y energía, no solo para el trabajo actual, sino porque cuando llegue otro problema similar, tendré una base con la que trabajar y solo necesitaré ajustar algunas partes para poder sacar el mejor rendimiento del sistema.

Mientras lo describía, por ejemplo, me he dado cuenta de algunas redundancias o temas que, como no me afectaban en este caso particular, no los planteé dentro de la solución: sustitución de tildes, eliminación de espacios en blanco, etc.; con los que igual podría haber conseguido un poco más de efectividad. De todas formas, haberme ahorrado buscar y renombrar 433 imágenes de 525, para mí, es más que suficiente; sobre todo, porque luego me encontré las excepciones que ya he descrito: productos que no tenían imágenes y imágenes que no correspondían a ningún producto.

Automatizar este tipo de trabajo, además, puede ayudar a evitar algunos errores; ya que al hacerlo manualmente, en muchos registros, el cansancio y la monotonía de la tarea puede inducir a intercambio de caracteres o a omisiones de información en la inclusión de datos.

No sé cuánto me hubiese supuesto en tiempo y cansancio mental hacer manualmente todas las imágenes; lo que sé, es que en plantear esta solución y ejecutarla tardé 3 horas, y las considero bien invertidas. Espero que también os pueda servir como base para plantearos vuestras propias soluciones y utilizar el tiempo de manera más efectiva.

Bonus anecdótico:

Imagina que trabajas manualmente con la interfaz de WordPress y, una vez has terminado de subir y enlazar las imágenes, te das cuenta de que no se visualizan correctamente y tienes que tratarlas para luego volver a subirlas, no te dará tiempo a hacerlo antes del cambio de mes y, por lo tanto, cambiará la URL cuando subas las imágenes correctas. Terrorífico, ¿verdad?

Nos ha ocurrido: las dimensiones no estaban bien.

Es cierto que en un proceso manualmente es probable que se hubiese detectado el error antes de finalizar el proceso, pero habiendo estandarizado los nombres de las imágenes y actualizar los enlaces a través de un .csv reduce el problema a:

  1. eliminar archivos de los medios,
  2. modificar y volverlos a subir,
  3. actualizar un .csv.

¿Cuánto tiempo crees que me llevará corregir el error en cuanto tenga las imágenes correctamente dimensionadas?

Compartir esta entrada

Dejar un Comentar

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

*
*