python parsing python-dateutil

Cómo analizar varias fechas de un bloque de texto en Python(u otro idioma)



parsing python-dateutil (4)

Tengo una cadena que tiene varios valores de fecha y quiero analizarlos todos. La cadena es un lenguaje natural, así que lo mejor que he encontrado hasta ahora es dateutil .

Desafortunadamente, si una cadena tiene varios valores de fecha, dateutil genera un error:

>>> s = "I like peas on 2011-04-23, and I also like them on easter and my birthday, the 29th of July, 1928" >>> parse(s, fuzzy=True) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/lib/pymodules/python2.7/dateutil/parser.py", line 697, in parse return DEFAULTPARSER.parse(timestr, **kwargs) File "/usr/lib/pymodules/python2.7/dateutil/parser.py", line 303, in parse raise ValueError, "unknown string format" ValueError: unknown string format

¿Alguna idea sobre cómo analizar todas las fechas de una cadena larga? Idealmente, se crearía una lista, pero puedo manejarlo yo mismo si lo necesito.

Estoy usando Python, pero en este punto, es probable que otros idiomas estén bien, si hacen el trabajo.

PD: supongo que podría dividir recursivamente el archivo de entrada en el medio e intentarlo, intentarlo de nuevo hasta que funcione, pero es un truco.


¿Por qué no escribir un patrón de expresión regular que cubra todas las formas posibles en que puede aparecer una fecha y luego lanzar la expresión regular para explorar el texto? Supongo que no hay docenas de docenas de modales para expresar una fecha en una cadena.

El único problema es reunir el máximo de expresiones de fecha.


Creo que si pones las "palabras" en una matriz, debería hacer el truco. Con eso puedes verificar si es una fecha o no, y poner una variable.

Una vez que tenga la fecha, debe usar la biblioteca de la biblioteca datetime .


Mientras estaba desconectado, me molestó la respuesta que publiqué aquí ayer. Sí, hizo el trabajo, pero fue innecesariamente complicado y extremadamente ineficiente.

¡Aquí está la edición de fondo de sobre que debería hacer un trabajo mucho mejor!

import itertools from dateutil import parser jumpwords = set(parser.parserinfo.JUMP) keywords = set(kw.lower() for kw in itertools.chain( parser.parserinfo.UTCZONE, parser.parserinfo.PERTAIN, (x for s in parser.parserinfo.WEEKDAYS for x in s), (x for s in parser.parserinfo.MONTHS for x in s), (x for s in parser.parserinfo.HMS for x in s), (x for s in parser.parserinfo.AMPM for x in s), )) def parse_multiple(s): def is_valid_kw(s): try: # is it a number? float(s) return True except ValueError: return s.lower() in keywords def _split(s): kw_found = False tokens = parser._timelex.split(s) for i in xrange(len(tokens)): if tokens[i] in jumpwords: continue if not kw_found and is_valid_kw(tokens[i]): kw_found = True start = i elif kw_found and not is_valid_kw(tokens[i]): kw_found = False yield "".join(tokens[start:i]) # handle date at end of input str if kw_found: yield "".join(tokens[start:]) return [parser.parse(x) for x in _split(s)]

Ejemplo de uso:

>>> parse_multiple("I like peas on 2011-04-23, and I also like them on easter and my birthday, the 29th of July, 1928") [datetime.datetime(2011, 4, 23, 0, 0), datetime.datetime(1928, 7, 29, 0, 0)]

Probablemente vale la pena señalar que su comportamiento se desvía ligeramente de dateutil.parser.parse cuando se trata de cadenas vacías / desconocidas. Dateutil devolverá el día actual, mientras que parse_multiple devuelve una lista vacía que, IMHO, es lo que uno esperaría.

>>> from dateutil import parser >>> parser.parse("") datetime.datetime(2011, 8, 12, 0, 0) >>> parse_multiple("") []

PS Acabo de ver la respuesta actualizada de MattH que hace algo muy similar.


Mirándolo, la forma menos complicada sería modificar el parser para tener una opción de múltiples fuzzy.

parser._parse toma su cadena, la _timelex con _timelex y luego compara los tokens con los datos definidos en parserinfo .

Here , si un token no coincide con nada en parserinfo , el análisis fallará a menos que fuzzy sea ​​True.

Lo que sugiero que permita que no haya coincidencias mientras no tenga tokens de tiempo procesados, luego, cuando encuentre una no coincidencia, procese los datos analizados en ese punto y comience a buscar los tokens de tiempo nuevamente.

No debería tomar demasiado esfuerzo.

Actualizar

Mientras esperas a que tu parche se enrolle ...

Esto es un poco pirateado, utiliza funciones no públicas en la biblioteca, pero no requiere modificar la biblioteca y no es de prueba y error. Es posible que tenga falsos positivos si tiene tokens solitarios que se pueden convertir en flotadores. Es posible que necesite filtrar los resultados un poco más.

from dateutil.parser import _timelex, parser a = "I like peas on 2011-04-23, and I also like them on easter and my birthday, the 29th of July, 1928" p = parser() info = p.info def timetoken(token): try: float(token) return True except ValueError: pass return any(f(token) for f in (info.jump,info.weekday,info.month,info.hms,info.ampm,info.pertain,info.utczone,info.tzoffset)) def timesplit(input_string): batch = [] for token in _timelex(input_string): if timetoken(token): if info.jump(token): continue batch.append(token) else: if batch: yield " ".join(batch) batch = [] if batch: yield " ".join(batch) for item in timesplit(a): print "Found:", item print "Parsed:", p.parse(item)

Rendimientos:

Found: 2011 04 23 Parsed: 2011-04-23 00:00:00 Found: 29 July 1928 Parsed: 1928-07-29 00:00:00

Actualización para Dieter

Dateutil 2.1 parece estar escrito para ser compatible con python3 y utiliza una biblioteca de "compatibilidad" llamada six . Algo no está bien con esto y no se trata de objetos str como texto.

Esta solución funciona con dateutil 2.1 si pasa cadenas como unicode o como objetos tipo archivo:

from cStringIO import StringIO for item in timesplit(StringIO(a)): print "Found:", item print "Parsed:", p.parse(StringIO(item))

Si desea establecer la opción en parserinfo, cree una instancia de parserinfo y pásela al objeto de análisis. P.ej:

from dateutil.parser import _timelex, parser, parserinfo info = parserinfo(dayfirst=True) p = parser(info)