txt - leer una linea especifica de un archivo en python
Análisis estructurado y elegante de archivos de texto (9)
Necesito analizar una transcripción de una conversación de chat en vivo. Mi primer pensamiento al ver el archivo fue arrojar expresiones regulares sobre el problema, pero me preguntaba qué otros enfoques han usado las personas.
Puse elegante en el título ya que he encontrado previamente que este tipo de tarea tiene el peligro de ser difícil de mantener simplemente confiando en expresiones regulares.
Las transcripciones están siendo generadas por www.providesupport.com y enviadas por correo electrónico a una cuenta, luego extraigo un adjunto de transcripción de texto plano del correo electrónico.
La razón para analizar el archivo es extraer el texto de la conversación para luego, pero también para identificar los nombres de visitantes y operadores para que la información esté disponible a través de un CRM.
Aquí hay un ejemplo de un archivo de transcripción:
Chat Transcript
Visitor: Random Website Visitor
Operator: Milton
Company: Initech
Started: 16 Oct 2008 9:13:58
Finished: 16 Oct 2008 9:45:44
Random Website Visitor: Where do i get the cover sheet for the TPS report?
* There are no operators available at the moment. If you would like to leave a message, please type it in the input field below and click "Send" button
* Call accepted by operator Milton. Currently in room: Milton, Random Website Visitor.
Milton: Y-- Excuse me. You-- I believe you have my stapler?
Random Website Visitor: I really just need the cover sheet, okay?
Milton: it''s not okay because if they take my stapler then I''ll, I''ll, I''ll set the building on fire...
Random Website Visitor: oh i found it, thanks anyway.
* Random Website Visitor is now off-line and may not reply. Currently in room: Milton.
Milton: Well, Ok. But… that''s the last straw.
* Milton has left the conversation. Currently in room: room is empty.
Visitor Details
---------------
Your Name: Random Website Visitor
Your Question: Where do i get the cover sheet for the TPS report?
IP Address: 255.255.255.255
Host Name: 255.255.255.255
Referrer: Unknown
Browser/OS: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.2; .NET CLR 1.1.4322; InfoPath.1; .NET CLR 2.0.50727)
Al usar multilínea, las expresiones regulares comentadas pueden mitigar un poco el problema de mantenimiento. ¡Prueba y evita la línea supergerex!
Además, considere dividir la expresión regular en tareas individuales, una para cada "cosa" que desee obtener. p.ej.
visitor = text.find(/Visitor:(.*)/)
operator = text.find(/Operator:(.*)/)
body = text.find(/whatever....)
en lugar de
text.match(/Visitor:(.*)/nOperator:(.*)...whatever to giant regex/m) do
visitor = $1
operator = $2
etc.
end
Luego hace que sea fácil cambiar cómo se analiza un elemento en particular. En cuanto a analizar un archivo con muchos "bloques de chat", solo tiene una sola expresión regular que coincida con un solo bloque de chat, itere sobre el texto y pase los datos de coincidencia de esto a su grupo de otros mezcladores.
Obviamente, esto afectará el rendimiento, pero a menos que proceses archivos enormes , no me preocuparé.
No, y de hecho, para el tipo específico de tarea que describes, dudo que haya una manera "más limpia" de hacerlo que las expresiones regulares. Parece que sus archivos tienen saltos de línea incrustados, por lo que lo que haremos aquí es convertir la línea en su unidad de descomposición, aplicando expresiones regulares por línea. Mientras tanto, crea una máquina de estado pequeña y usa coincidencias de expresiones regulares para activar las transiciones en esa máquina de estados. De esta forma sabrá dónde se encuentra en el archivo y qué tipo de datos de caracteres puede esperar. Además, considere usar grupos de captura con nombre y cargar las expresiones regulares desde un archivo externo. De esa manera, si el formato de su transcripción cambia, es una simple cuestión de ajustar la expresión regular, en lugar de escribir un nuevo código específico para el análisis.
Con Perl, puedes usar Parse :: RecDescent
Es simple y tu gramática se mantendrá más adelante.
Considere usar Ragel http://www.complang.org/ragel/
Eso es lo que poderes mestizo bajo el capó. Analizar una cadena varias veces va a ralentizar las cosas dramáticamente.
Es posible que desee considerar un generador de analizador completo.
Las expresiones regulares son buenas para buscar texto en subcadenas pequeñas, pero lamentablemente carecen de la potencia suficiente si estás realmente interesado en analizar todo el archivo en datos significativos.
Son especialmente insuficientes si el contexto de la subcadena es importante.
La mayoría de las personas arrojan expresiones mágicas a todo porque eso es lo que saben. Nunca aprendieron herramientas de generación de analizadores y terminan codificando una gran cantidad de composición de reglas de producción y manejo de acciones semánticas que puedes obtener de forma gratuita con un generador de analizadores.
Los regex son geniales y todo, pero si necesitas un analizador no son un sustituto.
He usado la biblioteca de clases pyParsing de Paul McGuire y sigo impresionado con ella, ya que está bien documentada, es fácil de comenzar y las reglas son fáciles de ajustar y mantener. Por cierto, las reglas se expresan en tu código python. Ciertamente parece que el archivo de registro tiene suficiente regularidad para analizar cada línea como una unidad independiente.
Solo una publicación rápida, solo he echado un vistazo al ejemplo de su transcripción, pero recientemente también tuve que analizar el análisis de texto y esperaba evitar la ruta del análisis integrado. Pasé por Ragel, que solo he empezado a entender, pero parece ser bastante útil.
Construir un analizador ? No puedo decidir si sus datos son lo suficientemente regulares para eso, pero podría valer la pena investigarlos.
Aquí hay dos analizadores basados en la biblioteca del generador de analizadores lepl
. Ambos producen el mismo resultado.
from pprint import pprint
from lepl import AnyBut, Drop, Eos, Newline, Separator, SkipTo, Space
# field = name , ":" , value
name, value = AnyBut('':/n'')[1:,...], AnyBut(''/n'')[::''n'',...]
with Separator(~Space()[:]):
field = name & Drop('':'') & value & ~(Newline() | Eos()) > tuple
header_start = SkipTo(''Chat Transcript'' & Newline()[2])
header = ~header_start & field[1:] > dict
server_message = Drop(''* '') & AnyBut(''/n'')[:,...] & ~Newline() > ''Server''
conversation = (server_message | field)[1:] > list
footer_start = ''Visitor Details'' & Newline() & ''-''*15 & Newline()
footer = ~footer_start & field[1:] > dict
chat_log = header & ~Newline() & conversation & ~Newline() & footer
pprint(chat_log.parse_file(open(''chat.log'')))
Analizador más estricto
from pprint import pprint
from lepl import And, Drop, Newline, Or, Regexp, SkipTo
def Field(name, value=Regexp(r''/s*(.*?)/s*?/n'')):
"""''name , ":" , value'' matcher"""
return name & Drop('':'') & value > tuple
Fields = lambda names: reduce(And, map(Field, names))
header_start = SkipTo(Regexp(r''^Chat Transcript$'') & Newline()[2])
header_fields = Fields("Visitor Operator Company Started Finished".split())
server_message = Regexp(r''^/* (.*?)/n'') > ''Server''
footer_fields = Fields(("Your Name, Your Question, IP Address, "
"Host Name, Referrer, Browser/OS").split('', ''))
with open(''chat.log'') as f:
# parse header to find Visitor and Operator''s names
headers, = (~header_start & header_fields > dict).parse_file(f)
# only Visitor, Operator and Server may take part in the conversation
message = reduce(Or, [Field(headers[name])
for name in "Visitor Operator".split()])
conversation = (message | server_message)[1:]
messages, footers = ((conversation > list)
& Drop(''/nVisitor Details/n---------------/n'')
& (footer_fields > dict)).parse_file(f)
pprint((headers, messages, footers))
Salida:
({''Company'': ''Initech'',
''Finished'': ''16 Oct 2008 9:45:44'',
''Operator'': ''Milton'',
''Started'': ''16 Oct 2008 9:13:58'',
''Visitor'': ''Random Website Visitor''},
[(''Random Website Visitor'',
''Where do i get the cover sheet for the TPS report?''),
(''Server'',
''There are no operators available at the moment. If you would like to leave a message, please type it in the input field below and click "Send" button''),
(''Server'',
''Call accepted by operator Milton. Currently in room: Milton, Random Website Visitor.''),
(''Milton'', ''Y-- Excuse me. You-- I believe you have my stapler?''),
(''Random Website Visitor'', ''I really just need the cover sheet, okay?''),
(''Milton'',
"it''s not okay because if they take my stapler then I''ll, I''ll, I''ll set the building on fire..."),
(''Random Website Visitor'', ''oh i found it, thanks anyway.''),
(''Server'',
''Random Website Visitor is now off-line and may not reply. Currently in room: Milton.''),
(''Milton'', "Well, Ok. But… that''s the last straw."),
(''Server'',
''Milton has left the conversation. Currently in room: room is empty.'')],
{''Browser/OS'': ''Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.2; .NET CLR 1.1.4322; InfoPath.1; .NET CLR 2.0.50727)'',
''Host Name'': ''255.255.255.255'',
''IP Address'': ''255.255.255.255'',
''Referrer'': ''Unknown'',
''Your Name'': ''Random Website Visitor'',
''Your Question'': ''Where do i get the cover sheet for the TPS report?''})