Foros del Web » Programando para Internet » Python »

Escribir en .docx

Estas en el tema de Escribir en .docx en el foro de Python en Foros del Web. Buenas noches, necesito escribir en un .docx, no necesito nada especial (tablas, listas, etc.), sólo voy a escribir texto y enlaces. En un tema de ...
  #1 (permalink)  
Antiguo 26/11/2011, 14:45
Avatar de _cronos2
Colaborador
 
Fecha de Ingreso: junio-2010
Mensajes: 2.062
Antigüedad: 13 años, 10 meses
Puntos: 310
Escribir en .docx

Buenas noches, necesito escribir en un .docx, no necesito nada especial (tablas, listas, etc.), sólo voy a escribir texto y enlaces.
En un tema de stackoverflow recomendaban el módulo python-docx de mikemaccana. Para ello necesitaba lxml y PIL, con PIL no hubo problemas pero lxml me fue imposible de instalar: primero le faltaba el archivo vcvarsall.bat (creo que se llamaba así) para lo que tuve que instalar el MS Visual Studio, y después me daba un error Python en el compilador de Visual Studio, traté de revisar el código pero lo di por perdido.
¿Qué me recomiendan para escribir en archivos .docx? Repito que no necesito nada especial, es texto plano y enlaces.
Saludos y gracias (:
__________________
" Getting older’s not been on my plans
but it’s never late, it’s never late enough for me to stay. "
Cigarettes - Russian Red
  #2 (permalink)  
Antiguo 26/11/2011, 16:22
Avatar de razpeitia
Moderador
 
Fecha de Ingreso: marzo-2005
Ubicación: Monterrey, México
Mensajes: 7.321
Antigüedad: 19 años, 1 mes
Puntos: 1360
Respuesta: Escribir en .docx

Bajate el lxml de esta pagina.

Son módulos precompilados para windows.
  #3 (permalink)  
Antiguo 27/11/2011, 09:30
Avatar de _cronos2
Colaborador
 
Fecha de Ingreso: junio-2010
Mensajes: 2.062
Antigüedad: 13 años, 10 meses
Puntos: 310
Respuesta: Escribir en .docx

Me fue de maravilla, no hay nada como los .exe para que Windows entre en razón
Ahora ya lo tengo todo funcionando, pero me encuentro con el problema de que no sé cómo insertar un link. He probado así:
Código Python:
Ver original
  1. import docx
  2. ...
  3. url = 'http://www.google.es'
  4. documentbody.append( docx.makeelement('a', url, 'w', { 'href' : url }) )
Con un a href, que es como se haría en HTML, pero parece que esto no resulta en XML En el ejemplo que da en GitHub no sale ningún link, y tampoco hay docs por ningún lado. He mirado en las docs de lxml pero al fin y al cabo el link lo tengo que crear con el módulo docx porque estoy trabajando con él, digo yo.
Saludos y gracias :D
__________________
" Getting older’s not been on my plans
but it’s never late, it’s never late enough for me to stay. "
Cigarettes - Russian Red
  #4 (permalink)  
Antiguo 27/11/2011, 10:37
Avatar de razpeitia
Moderador
 
Fecha de Ingreso: marzo-2005
Ubicación: Monterrey, México
Mensajes: 7.321
Antigüedad: 19 años, 1 mes
Puntos: 1360
Respuesta: Escribir en .docx

Investigando un poco tendrás que hacer una función que agrege un link.

No te aseguro que sea una tarea simple.
  #5 (permalink)  
Antiguo 01/12/2011, 17:09
Avatar de _cronos2
Colaborador
 
Fecha de Ingreso: junio-2010
Mensajes: 2.062
Antigüedad: 13 años, 10 meses
Puntos: 310
Respuesta: Escribir en .docx

Bien, pues después de investigar he conseguido descubrir varias cosas. La primera es que los .docx son en realidad una especie de "zips" con varias carpetas donde hay archivos XML. Los archivos principales son dos:
1.- "word/document.xml": Estructura del .docx
2.- "word/_rels/document.rels.xml": Guarda las relationships del documento
Entonces los enlaces se ponen en el archivo 1 así:
Código XML:
Ver original
  1. <w:hyperlink r:id="rIdX">
  2. <w:r w:rsidRPr="008D7595">
  3. <w:rPr>
  4. <w:rStyle w:val="Hipervnculo"/>
  5. </w:rPr>
  6. <w:t>Texto</w:t>
  7. </w:r>
  8. </w:hyperlink>
Excepto el <w:t>, el resto de hijos del hyperlink no sé para qué sirven aunque se puede intuir, y en principio no las voy a usar, con poner el texto me sirve.
Después, en el archivo 2, se ponen los Relationships:
Código XML:
Ver original
  1. <Relationship Target="http://www.google.es" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink" Id="rIdX" TargetMode="External"/>
X es el número del id.
Entonces edité el módulo docx y en la función wordrelationships, que es la que añade todos los Relationships antes de guardar el documento, añadí un parámetro para poder pasarle un array de links y que los añadiera también al archivo 2. Lo puse así:
Código Python:
Ver original
  1. def wordrelationships(relationshiplist, links = []):
  2.     relationships = etree.fromstring(
  3.     '''<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
  4.        </Relationships>'''
  5.     )
  6.    
  7.     count = 1
  8.     for relationship in relationshiplist:
  9.         # Relationship IDs (rId) start at 1.
  10.         relationships.append(
  11.             makeelement('Relationship', attributes = {
  12.                 'Id' : 'rId' + str(count),
  13.                 'Type' : relationship[0],
  14.                 'Target' : relationship[1]
  15.             }, nsprefix = None)
  16.         )
  17.         count += 1
  18.    
  19. # A partir de aquí el código es mío
  20.     namespace = '{http://schemas.openxmlformats.org/officeDocument/2006/relationships}' # nsprefix = 'r'
  21.     if links: # links != []    
  22.         for obj in links:
  23.             relationships.append(
  24.                 makeelement('Relationship', attributes = {
  25.                     'Id' : 'rId' + str(count),
  26.                     'Type' : 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink',
  27.                     'Target' : obj['target'],
  28.                     'TargetMode' : obj['mode']
  29.                 }, attrnsprefix = 'w', tagtext = obj['text'])
  30.             )
  31.            
  32.             obj['hyperlink'].set(namespace + 'id', 'rId' + str(count))
  33.             count += 1
  34.    
  35.     return relationships
Y aquí surgen varios problemas, como era de esperar
A) En vez de hacer un bucle para relationshiplist y otro para links, parece ser que el de links se queda dentro del otro, y por cada Relationship "estándar" que añade, me añade otra vez el Relationship de mi link.
B) Al crear los elementos pongo tanto nsprefix como attrnsprefix 'w', puesto que en una prueba que hice a mano, al inspeccionar los archivos, el prefijo de los tags es "w:", sin embargo en el archivo generado sale "ns0:".
C) Se supone que cuando se guarda el archivo todo el árbol XML se convierte a string para después escribirlo en el nuevo documento, y se hace mediante el etree de lxml:
Código Python:
Ver original
  1. # Serialize our trees into out zip file
  2.     treesandfiles = {document:'word/document.xml',
  3.                      coreprops:'docProps/core.xml',
  4.                      appprops:'docProps/app.xml',
  5.                      contenttypes:'[Content_Types].xml',
  6.                      websettings:'word/webSettings.xml',
  7.                      wordrelationships:'word/_rels/document.xml.rels'}
  8.     for tree in treesandfiles:
  9.         log.info('Saving: '+treesandfiles[tree])
  10.         treestring = etree.tostring(tree, pretty_print=True)
  11.         docxfile.writestr(treesandfiles[tree], treestring)
Al usar el tostring, el parámetro pretty_true es True, así que debería escribirlo con saltos de línea e indentaciones. Sin embargo, cuando me genera el documento, el archivo 2 (el de Relationships) me sale todo en dos líneas:
Código XML:
Ver original
  1. <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
  2.         <Relationship Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering" Id="rId1" Target="numbering.xml"/><ns0:Relationship xmlns:ns0="http://schemas.openxmlformats.org/wordprocessingml/2006/main" ns0:TargetMode="External" ns0:Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink" ns0:Id="rId2" ns0:Target="http://www.google.es">http://www.google.es</ns0:Relationship><Relationship Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Id="rId3" Target="styles.xml"/><ns0:Relationship xmlns:ns0="http://schemas.openxmlformats.org/wordprocessingml/2006/main" ns0:TargetMode="External" ns0:Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink" ns0:Id="rId4" ns0:Target="http://www.google.es">http://www.google.es</ns0:Relationship><Relationship Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings" Id="rId5" Target="settings.xml"/><ns0:Relationship xmlns:ns0="http://schemas.openxmlformats.org/wordprocessingml/2006/main" ns0:TargetMode="External" ns0:Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink" ns0:Id="rId6" ns0:Target="http://www.google.es">http://www.google.es</ns0:Relationship><Relationship Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/webSettings" Id="rId7" Target="webSettings.xml"/><ns0:Relationship xmlns:ns0="http://schemas.openxmlformats.org/wordprocessingml/2006/main" ns0:TargetMode="External" ns0:Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink" ns0:Id="rId8" ns0:Target="http://www.google.es">http://www.google.es</ns0:Relationship><Relationship Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/fontTable" Id="rId9" Target="fontTable.xml"/><ns0:Relationship xmlns:ns0="http://schemas.openxmlformats.org/wordprocessingml/2006/main" ns0:TargetMode="External" ns0:Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink" ns0:Id="rId10" ns0:Target="http://www.google.es">http://www.google.es</ns0:Relationship><Relationship Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme" Id="rId11" Target="theme/theme1.xml"/><ns0:Relationship xmlns:ns0="http://schemas.openxmlformats.org/wordprocessingml/2006/main" ns0:TargetMode="External" ns0:Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink" ns0:Id="rId12" ns0:Target="http://www.google.es">http://www.google.es</ns0:Relationship></Relationships>
Ahí también se puede ver lo de que me repite el link que le paso por cada Relationship que añade.
Espero que me podáis ayudar :D
Saludos (:
__________________
" Getting older’s not been on my plans
but it’s never late, it’s never late enough for me to stay. "
Cigarettes - Russian Red
  #6 (permalink)  
Antiguo 01/12/2011, 17:31
Avatar de razpeitia
Moderador
 
Fecha de Ingreso: marzo-2005
Ubicación: Monterrey, México
Mensajes: 7.321
Antigüedad: 19 años, 1 mes
Puntos: 1360
Respuesta: Escribir en .docx

De hecho tiene muy buenas razones para no reformatear, esta especificado en las aquí FAQ's de lxml
  #7 (permalink)  
Antiguo 02/12/2011, 13:21
Avatar de _cronos2
Colaborador
 
Fecha de Ingreso: junio-2010
Mensajes: 2.062
Antigüedad: 13 años, 10 meses
Puntos: 310
Respuesta: Escribir en .docx

Bueno, pues gracias a esto me acabo de dar cuenta de uno de los fallos, el texto no tiene que ir en el Relationship sino en un t dentro del hyperlink.
Y recién otro de los problemas Al crear los relationship tengo que poner nsprefix y attrnsprefix a None, no sé por qué puse "w"
Después de corregir estos errores falta averiguar por qué el bucle de mis Relationships se queda dentro del otro, y por qué el documento que yo genero tiene com prefijo ns0: y no w:
Saludos y gracias :)
Edit: Otra cosa solucionada. Me ponía un bucle dentro del otro porque en la parte del código antiguo la indentación era de 4 espacios, y en lo que puse yo era con tabulaciones. Lo cambié todo a tabulaciones y ya funciona esa parte
__________________
" Getting older’s not been on my plans
but it’s never late, it’s never late enough for me to stay. "
Cigarettes - Russian Red

Última edición por _cronos2; 02/12/2011 a las 15:57
  #8 (permalink)  
Antiguo 08/12/2011, 07:30
Avatar de _cronos2
Colaborador
 
Fecha de Ingreso: junio-2010
Mensajes: 2.062
Antigüedad: 13 años, 10 meses
Puntos: 310
Respuesta: Escribir en .docx

YUJUUUUUUUUU, al fin lo conseguí! Bueno en realidad lo conseguí ya el lunes, pero entre unas cosas y otras no he tenido tiempo para postearlo antes Al final tuve que pasar casi completamente de docx y hacerlo con lxml, que por cierto me encantó por lo sencillo y potente que es. Finalmente el código quedo así:
Código Python:
Ver original
  1. # -*- coding: utf-8 -*-
  2.  
  3. from docx import *
  4.  
  5. def gen_rid():
  6.     from random import shuffle
  7.     hex = list("0123456789ABCDEF")
  8.     shuffle(hex)
  9.     return ''.join( hex[0:8] )
  10.  
  11. def make_link(target, mode = 'External', text = ''):
  12.     hyperlink = etree.fromstring(
  13.         '''\
  14.         <w:p w:rsidR="{rid}" w:rsidRDefault="{rid}" xmlns:w="{url}">
  15.             <w:hyperlink>          
  16.                 <w:r w:rsidRPr="{rid}">
  17.                     <w:rPr>
  18.                         <w:rStyle w:val="Hipervnculo"/>
  19.                     </w:rPr>
  20.                     <w:t>{text}</w:t>
  21.                 </w:r>
  22.             </w:hyperlink>
  23.         </w:p>
  24.         '''.format(rid = rid, text = text, url = w)
  25.     )
  26.  
  27.     links.append({
  28.         'hyperlink' : hyperlink[0],
  29.         'target' : target,
  30.         'mode' : mode,
  31.         'text' : text      
  32.     })
  33.  
  34.     return hyperlink
  35.  
  36. def new():
  37.     d = etree.fromstring( '''<w:document xmlns:w="%s" xmlns:r="%s"></w:document>''' % (w, r) )
  38.     dbody = etree.SubElement( d, '{%s}body' % w)
  39.    
  40.     return d, dbody
  41.  
  42.  
  43. relationships, rid = relationshiplist(), gen_rid()
  44. links = []
  45. w, r = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'
  46.  
  47. d, dbody = new()
  48. dbody.append( make_link('http://www.google.es', 'External', 'http://www.google.es') )
  49. # ...
  50. wordrelationships = wordrelationships(relationships, links)
Y tuve que cambiar el módulo docx:
Código Python:
Ver original
  1. def wordrelationships(relationshiplist, links = []):
  2.     '''Generate a Word relationships file'''
  3.     # Default list of relationships
  4.     # FIXME: using string hack instead of making element
  5.     #relationships = makeelement('Relationships',nsprefix='pr')
  6.     relationships = etree.fromstring(
  7.     '''<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"></Relationships>'''
  8.     )
  9.    
  10.     count = 1
  11.     for relationship in relationshiplist:
  12.         # Relationship IDs (rId) start at 1.
  13.         relationships.append(
  14.             makeelement('Relationship', attributes = {
  15.                 'Id' : 'rId' + str(count),
  16.                 'Type' : relationship[0],
  17.                 'Target' : relationship[1]
  18.             }, nsprefix = None)
  19.         )
  20.         count += 1
  21.  
  22. # A partir de aquí el código es mío
  23.     r = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' # nsprefix = 'r'
  24.     if links: # links != []    
  25.         for obj in links:
  26.             relationships.append(
  27.                 makeelement('Relationship', attributes = {
  28.                     'TargetMode' : obj['mode'],
  29.                     'Id' : 'rId' + str(count),
  30.                     'Type' : '%s/hyperlink' % r,
  31.                     'Target' : obj['target']
  32.                 }, nsprefix = None)
  33.             )
  34.            
  35.             hl = obj['hyperlink']
  36.             hl.set('{%s}id' % r, 'rId' + str(count))
  37.            
  38.             count += 1
  39.    
  40.     return relationships
para poder darle soporte a los hipervínculos. Lo único que no conseguí es que se vieran como links, de color azul, sino que se siguen viendo como texto plano, pero es lo de menos.
Espero que esto le sirva a alguien algún día
Saludos y gracias por todo :D
__________________
" Getting older’s not been on my plans
but it’s never late, it’s never late enough for me to stay. "
Cigarettes - Russian Red
  #9 (permalink)  
Antiguo 08/12/2011, 11:22
Avatar de razpeitia
Moderador
 
Fecha de Ingreso: marzo-2005
Ubicación: Monterrey, México
Mensajes: 7.321
Antigüedad: 19 años, 1 mes
Puntos: 1360
Puedes agregarlo al módulo python-docx y hacer un push al proyecto
  #10 (permalink)  
Antiguo 08/12/2011, 15:08
Avatar de _cronos2
Colaborador
 
Fecha de Ingreso: junio-2010
Mensajes: 2.062
Antigüedad: 13 años, 10 meses
Puntos: 310
Respuesta: Escribir en .docx

Cierto, veré qué puedo hacer. Gracias por el apunte ;)
__________________
" Getting older’s not been on my plans
but it’s never late, it’s never late enough for me to stay. "
Cigarettes - Russian Red

Etiquetas: docx, listas
Atención: Estás leyendo un tema que no tiene actividad desde hace más de 6 MESES, te recomendamos abrir un Nuevo tema en lugar de responder al actual.
Respuesta




La zona horaria es GMT -6. Ahora son las 13:51.