web-dev-qa-db-fra.com

Comment utiliser python-docx pour remplacer du texte dans un document Word et enregistrer

Le module oodocx mentionné dans la même page renvoie l'utilisateur à un dossier/examples qui ne semble pas être là.
J'ai lu la documentation de python-docx 0.7.2, ainsi que tout ce que j'ai pu trouver dans Stackoverflow sur le sujet, alors s'il vous plaît, croyez que j'ai fait mes "devoirs".

Python est le seul langage que je connaisse (débutant +, peut-être intermédiaire), donc n'assumez aucune connaissance de C, Unix, xml, etc.

Tâche: ouvrez un document ms-Word 2007+ contenant une seule ligne de texte (pour simplifier les choses) et remplacez tout mot "clé" du dictionnaire qui apparaît dans cette ligne de texte par sa valeur de dictionnaire. Fermez ensuite le document en gardant le reste identique.

Ligne de texte (par exemple) "Nous nous attarderons dans les chambres de la mer."

from docx import Document

document = Document('/Users/umityalcin/Desktop/Test.docx')

Dictionary = {‘sea’: “ocean”}

sections = document.sections
for section in sections:
    print(section.start_type)

#Now, I would like to navigate, focus on, get to, whatever to the section that has my
#single line of text and execute a find/replace using the dictionary above.
#then save the document in the usual way.

document.save('/Users/umityalcin/Desktop/Test.docx')

Je ne vois rien dans la documentation qui me permette de le faire - peut-être que c'est là mais je ne le comprends pas parce que tout n'est pas précisé à mon niveau.

J'ai suivi d'autres suggestions sur ce site et j'ai essayé d'utiliser des versions antérieures du module ( https://github.com/mikemaccana/python-docx ) qui est censé avoir "des méthodes comme remplacer, advReplace "comme suit: j'ouvre le code source dans l'interpréteur python, et j'ajoute ce qui suit à la fin (c'est pour éviter les conflits avec la version 0.7.2 déjà installée):

document = opendocx('/Users/umityalcin/Desktop/Test.docx')
words = document.xpath('//w:r', namespaces=document.nsmap)
for Word in words:
    if Word in Dictionary.keys():
        print "found it", Dictionary[Word]
        document = replace(document, Word, Dictionary[Word])
savedocx(document, coreprops, appprops, contenttypes, websettings,
    wordrelationships, output, imagefiledict=None) 

L'exécution de ceci produit le message d'erreur suivant:

NameError: le nom 'coreprops' n'est pas défini

J'essaie peut-être de faire quelque chose qui ne peut pas être fait, mais j'apprécierais votre aide si je manque quelque chose de simple.

Si cela importe, j'utilise la version 64 bits de Canopy d'Enthought sur OSX 10.9.3

19
user2738815

La version actuelle de python-docx n'a pas de fonction search() ou replace(). Celles-ci sont demandées assez fréquemment, mais une mise en œuvre pour le cas général est assez délicate et n'a pas encore atteint le sommet de l'arriéré.

Plusieurs personnes ont cependant réussi, en faisant ce dont elles ont besoin, en utilisant les installations déjà présentes. Voici un exemple. Cela n'a rien à voir avec les sections d'ailleurs :)

for paragraph in document.paragraphs:
    if 'sea' in paragraph.text:
        print paragraph.text
        paragraph.text = 'new text containing ocean'

Pour rechercher également dans les tableaux, vous devez utiliser quelque chose comme:

for table in document.tables:
    for cell in table.cells:
        for paragraph in cell.paragraphs:
            if 'sea' in paragraph.text:
               ...

Si vous suivez cette voie, vous découvrirez probablement assez rapidement quelles sont les complexités. Si vous remplacez le texte entier d'un paragraphe, cela supprimera toute mise en forme au niveau des caractères, comme un mot ou une phrase en gras ou en italique.

Soit dit en passant, le code de la réponse de @ wnnmaw est pour la version héritée de python-docx et ne fonctionnera pas du tout avec les versions après 0.3.0.

30
scanny

J'avais besoin de quelque chose pour remplacer les expressions régulières dans docx. J'ai pris la réponse des scannys. Pour gérer le style, j'ai utilisé la réponse de: Python docx Remplace la chaîne dans le paragraphe tout en conservant le style ajout d'un appel récursif pour gérer les tables imbriquées. et est venu avec quelque chose comme ça:

import re
from docx import Document

def docx_replace_regex(doc_obj, regex , replace):

    for p in doc_obj.paragraphs:
        if regex.search(p.text):
            inline = p.runs
            # Loop added to work with runs (strings with same style)
            for i in range(len(inline)):
                if regex.search(inline[i].text):
                    text = regex.sub(replace, inline[i].text)
                    inline[i].text = text

    for table in doc_obj.tables:
        for row in table.rows:
            for cell in row.cells:
                docx_replace_regex(cell, regex , replace)



regex1 = re.compile(r"your regex")
replace1 = r"your replace string"
filename = "test.docx"
doc = Document(filename)
docx_replace_regex(doc, regex1 , replace1)
doc.save('result1.docx')

Pour parcourir le dictionnaire:

for Word, replacement in dictionary.items():
    Word_re=re.compile(Word)
    docx_replace_regex(doc, Word_re , replacement)

Notez que cette solution ne remplacera l'expression régulière que si l'expression régulière entière a le même style dans le document.

De plus, si le texte est modifié après avoir enregistré le même style, le texte peut être dans des exécutions distinctes. Par exemple, si vous ouvrez un document qui a une chaîne "testabcd" et que vous le changez en "test1abcd" et enregistrez, même la pâte est du même style, il y a 3 exécutions distinctes "test", "1" et "abcd", dans ce cas le remplacement de test1 ne fonctionnera pas.

C'est pour suivre les changements dans le document. Pour le limiter à une seule exécution, dans Word, vous devez aller dans "Options", "Trust Center" et dans "Options de confidentialité" décocher "Stocker des nombres aléatoires pour améliorer la précision de la combinaison" et enregistrer le document.

11
szum

L'Office Dev Center dispose d'une entrée dans laquelle un développeur a publié (licence MIT pour le moment) une description de quelques algorithmes qui semblent suggérer une solution pour cela (quoique en C #, et nécessitent un portage): " Publication MS Dev Center

1
Soferio

Le problème avec votre deuxième tentative est que vous n'avez pas défini les paramètres dont savedocx a besoin. Vous devez faire quelque chose comme ça avant vous enregistrez:

relationships = docx.relationshiplist()
title = "Document Title"
subject = "Document Subject"
creator = "Document Creator"
keywords = []

coreprops = docx.coreproperties(title=title, subject=subject, creator=creator,
                       keywords=keywords)
app = docx.appproperties()
content = docx.contenttypes()
web = docx.websettings()
Word = docx.wordrelationships(relationships)
output = r"path\to\where\you\want\to\save"
0
wnnmaw