Convierte Datos Serializados de PHP a Formatos Legibles con Python


Si tienes un proyecto en WordPress y cuentas con el complemento Paid Memberships Pro (PMPro), te darás cuenta de que PMPro guarda mucha información útil de tus usuarios, como la fecha de su última visita, la cual puede ser muy valiosa para diversas tareas.

En mi caso, necesitaba exportar todos los usuarios de un proyecto y ordenarlos de forma descendente según su última visita. Fue posible descargar dicha información sin problemas, pero el detalle estaba en el formato, que venía así:

a:9:{s:4:"last";s:18:"30 September, 2024";s:8:"thisdate";N;s:4:"week";i:1;s:8:"thisweek";s:2:"40";s:5:"month";i:1;s:9:"thismonth";s:1:"9";s:3:"ytd";i:1;s:8:"thisyear";s:4:"2024";s:7:"alltime";i:1;}

El formato anterior es PHP Serialized Data, un método utilizado en PHP para convertir datos complejos (como arrays o objetos) en una cadena de texto. Esto permite almacenar estructuras de datos en bases de datos o transmitirlas entre aplicaciones. Este formato es específico de PHP y generalmente se usa para guardar configuraciones, sesiones de usuario o datos estructurados.

Para procesar esta información, desarrollamos el siguiente programa en Python, el cual voy a explicar paso a paso:


Descripción del código

Este código realiza la tarea de leer un archivo CSV con datos serializados en formato PHP (almacenados en la columna last_visit), deserializarlos, convertir una fecha contenida en esos datos a un formato estándar (YYYY-MM-DD) y luego escribir los resultados en un nuevo archivo CSV.

Importaciones

  1. csv: Para manejar archivos CSV, leerlos y escribirlos.
  2. phpserialize: Para deserializar los datos que están en formato PHP serializado.
  3. os: Para manipular rutas de archivos y nombres.
  4. datetime: Para manejar y convertir fechas.

Función deserialize_data

Esta función toma una cadena PHP serializada y realiza las siguientes acciones:

  1. Deserialización del dato:
    • Convierte la cadena PHP serializada en una estructura de Python (diccionario o similar) usando phpserialize.loads().
    • El dato serializado se pasa como bytes, por lo que se convierte de str a bytes con bytes(serialized_data, 'utf-8').
  2. Extracción de la fecha de la última visita:
    • Usa la clave b'last' (formato de clave serializada en PHP) para obtener la fecha almacenada en el campo last.
    • Si no existe, devuelve un mensaje predeterminado (b'No visit data').
  3. Conversión de la fecha al formato estándar:
    • Convierte la fecha (formato como 30 September, 2024) al formato YYYY-MM-DD usando datetime.strptime y strftime.
    • Si la fecha no es válida, devuelve el texto 'Invalid Date'.
  4. Retorno de la fecha procesada:
    • Si todo funciona, devuelve la fecha en formato YYYY-MM-DD. Si falla, devuelve un mensaje de error.

Procesamiento del archivo CSV

  1. Lectura del archivo de entrada:
    • Solicita al usuario la ruta del archivo CSV de entrada usando input().
    • Genera automáticamente el nombre del archivo de salida agregando _output.csv al final del nombre base.
  2. Lectura y escritura de datos:
    • Usa csv.DictReader para leer el archivo de entrada como un diccionario.
    • Prepara el archivo de salida con columnas específicas: user_id, user_login, user_email, last_visit.
    • Escribe cada fila procesada en el archivo de salida tras deserializar y convertir la fecha de last_visit.
  3. Manejo de excepciones:
    • Captura errores al abrir, leer o escribir archivos y deserializar datos, mostrando mensajes claros sobre el problema.

Código completo

import csv
import phpserialize
import os
from datetime import datetime

# Función para deserializar los datos de 'pmpro_visits' (PHP serializado)
def deserialize_data(serialized_data):
    try:
        deserialized = phpserialize.loads(bytes(serialized_data, 'utf-8'))
        last_visit = deserialized.get(b'last', b'No visit data').decode('utf-8')
        try:
            last_visit_date = datetime.strptime(last_visit, '%d %B, %Y').strftime('%Y-%m-%d')
        except ValueError:
            last_visit_date = 'Invalid Date'
        return last_visit_date
    except Exception as e:
        return f"Error deserializando: {e}"

# Ruta del archivo CSV de entrada
input_file_path = input("Ingrese la ruta del archivo CSV de entrada: ")

# Generar nombre del archivo de salida
base_name = os.path.splitext(os.path.basename(input_file_path))[0]
output_file_path = f"{base_name}_output.csv"

try:
    with open(input_file_path, 'r', newline='', encoding='utf-8') as input_file:
        reader = csv.DictReader(input_file)
        fieldnames = ['user_id', 'user_login', 'user_email', 'last_visit']
        with open(output_file_path, 'w', newline='', encoding='utf-8') as output_file:
            writer = csv.DictWriter(output_file, fieldnames=fieldnames)
            writer.writeheader()
            for row in reader:
                user_id = row['user_id']
                user_login = row['user_login']
                user_email = row['user_email']
                serialized_data = row['last_visit']
                last_visit_date = deserialize_data(serialized_data)
                writer.writerow({
                    'user_id': user_id,
                    'user_login': user_login,
                    'user_email': user_email,
                    'last_visit': last_visit_date,
                })
    print(f"Listo: El archivo de salida fue creado exitosamente como {output_file_path}.")
except Exception as e:
    print(f"Error: {e}")

Este programa te permitirá procesar y transformar datos serializados en formato PHP para hacerlos más manejables y útiles.


¿Tienes un cometario sobre esto? Dímelo en Twitter o Facebook.