У меня есть PowerPoint, который я хочу открыть, изменить и сохранить как файл с другим именем. Однако я получаю KeyError.
Я попробовал этот код с пустой презентацией PowerPoint, и он отлично работает. Однако когда я использую код для открытия существующей презентации PowerPoint и пытаюсь запустить тот же код, я получаю KeyError.
KeyError: "В архиве нет элемента с именем "ppt/slides/NULL""
#Replace Source Text
import re
#s = "string. With. Punctuation?"
#s = re.sub(r'[^\w\s]','',s)
search_str = '{{{FILTER}}}'
repl_str = re.sub(r'[^\w\s]','',(str(list(dashboard_filter2.values()))))
ppt = Presentation('HispPres1.pptx')
for slide in ppt.slides:
for shape in slide.shapes:
if shape.has_text_frame:
shape.text = shape.text.replace(search_str, repl_str)
ppt.save('HispPresSourceUpdate.pptx')
Я ожидаю внесения изменений в существующий PowerPoint, найдя все экземпляры {{{FILTER}}} и заменив его указанным значением. Однако, похоже, возникла проблема с использованием моей существующей презентации PowerPoint. У меня нет этой проблемы с пустой презентацией.
Итак, мне интересно, что может привести к тому, что существующая презентация PowerPoint вызовет ошибку ??? Я планирую сделать несколько «шаблонов» для начала, и мне действительно нужно знать, есть ли какие-то жесткие правила, которых нужно придерживаться.
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
<ipython-input-42-41deffabe2f9> in <module>()
7 search_str = '{{{FILTER}}}'
8 repl_str = re.sub(r'[^\w\s]','',(str(list(dashboard_filter2.values()))))
----> 9 ppt = Presentation('HispPres1.pptx')
10
11 for slide in ppt.slides:
~\AppData\Local\Continuum\anaconda3\lib\site-packages\pptx\api.py in Presentation(pptx)
28 pptx = _default_pptx_path()
29
---> 30 presentation_part = Package.open(pptx).main_document_part
31
32 if not _is_pptx_package(presentation_part):
~\AppData\Local\Continuum\anaconda3\lib\site-packages\pptx\opc\package.py in open(cls, pkg_file)
120 *pkg_file*.
121 """
--> 122 pkg_reader = PackageReader.from_file(pkg_file)
123 package = cls()
124 Unmarshaller.unmarshal(pkg_reader, package, PartFactory)
~\AppData\Local\Continuum\anaconda3\lib\site-packages\pptx\opc\pkgreader.py in from_file(pkg_file)
34 pkg_srels = PackageReader._srels_for(phys_reader, PACKAGE_URI)
35 sparts = PackageReader._load_serialized_parts(
---> 36 phys_reader, pkg_srels, content_types
37 )
38 phys_reader.close()
~\AppData\Local\Continuum\anaconda3\lib\site-packages\pptx\opc\pkgreader.py in _load_serialized_parts(phys_reader, pkg_srels, content_types)
67 sparts = []
68 part_walker = PackageReader._walk_phys_parts(phys_reader, pkg_srels)
---> 69 for partname, blob, srels in part_walker:
70 content_type = content_types[partname]
71 spart = _SerializedPart(partname, content_type, blob, srels)
~\AppData\Local\Continuum\anaconda3\lib\site-packages\pptx\opc\pkgreader.py in _walk_phys_parts(phys_reader, srels, visited_partnames)
102 yield (partname, blob, part_srels)
103 for partname, blob, srels in PackageReader._walk_phys_parts(
--> 104 phys_reader, part_srels, visited_partnames):
105 yield (partname, blob, srels)
106
~\AppData\Local\Continuum\anaconda3\lib\site-packages\pptx\opc\pkgreader.py in _walk_phys_parts(phys_reader, srels, visited_partnames)
102 yield (partname, blob, part_srels)
103 for partname, blob, srels in PackageReader._walk_phys_parts(
--> 104 phys_reader, part_srels, visited_partnames):
105 yield (partname, blob, srels)
106
~\AppData\Local\Continuum\anaconda3\lib\site-packages\pptx\opc\pkgreader.py in _walk_phys_parts(phys_reader, srels, visited_partnames)
99 visited_partnames.append(partname)
100 part_srels = PackageReader._srels_for(phys_reader, partname)
--> 101 blob = phys_reader.blob_for(partname)
102 yield (partname, blob, part_srels)
103 for partname, blob, srels in PackageReader._walk_phys_parts(
~\AppData\Local\Continuum\anaconda3\lib\site-packages\pptx\opc\phys_pkg.py in blob_for(self, pack_uri)
107 matching member is present in zip archive.
108 """
--> 109 return self._zipf.read(pack_uri.membername)
110
111 def close(self):
~\AppData\Local\Continuum\anaconda3\lib\zipfile.py in read(self, name, pwd)
1312 def read(self, name, pwd=None):
1313 """Return file bytes (as a string) for name."""
-> 1314 with self.open(name, "r", pwd) as fp:
1315 return fp.read()
1316
~\AppData\Local\Continuum\anaconda3\lib\zipfile.py in open(self, name, mode, pwd, force_zip64)
1350 else:
1351 # Get info object for name
-> 1352 zinfo = self.getinfo(name)
1353
1354 if mode == 'w':
~\AppData\Local\Continuum\anaconda3\lib\zipfile.py in getinfo(self, name)
1279 if info is None:
1280 raise KeyError(
-> 1281 'There is no item named %r in the archive' % name)
1282
1283 return info
KeyError: "There is no item named 'ppt/slides/NULL' in the archive"
Конечно. Извини за это. Я только что обновил вопрос, чтобы включить полный KeyError.
Трудно сказать без полной трассировки стека, но по какой-то причине он думает, что вы пытаетесь найти слайд с именем NULL
, что кажется неправильным. Полная трассировка стека будет включать более длинную ошибку, показывающую, где в коде возникла ошибка. stackoverflow.com/help/mcve
Ох, хорошо. Это дает мне полную трассировку стека? import traceback traceback.print_stack()
Я ожидаю, что он напечатает длинную трассировку стека, когда ваша программа выйдет из строя, что-то вроде одного из этих: google.com/search?q=python+traceback&tbm=isch. Где вы запускаете этот код?
Извинения. Я не знал, как это называется. Я отредактировал вопрос. Спасибо за помощь.
Да, это немного острая проблема. Спецификация не предусматривает «разорванных» отношений (относящихся к несуществующей части пакета), но по крайней мере одна библиотека (на основе Java, если я правильно помню) не очищает должным образом отношения в некоторых случаях. случаях, возможно, операция удаления слайда в этом случае.
Суть объяснения такова:
slide1.xml
, и они расположены в «структуре каталогов».presentation.xml
) связана с каждой из частей слайда. Эти отношения хранятся в файле, таком как presentation.xml.rels
. Отношение обозначается строкой, например "rId3"
, и идентифицирует связанную часть по ее пути в пакете.<p:sldId r:id = "rId3"/>
). Целевая часть «просматривается» в файле .rels, чтобы найти ее путь и добраться до нее таким образом.KeyError
означает, что в файле .rels есть элемент <Relationship>
, относящийся к части ppt/slides/NULL
(вместо чего-то вроде ppt/slides/slide3.xml
). Поскольку такой части в пакете нет, поиск не выполняется.Если вы откроете файл «шаблона» в PowerPoint и сохраните его, я думаю, он восстановится сам. Возможно, вам придется изменить порядок слайдов и переместить его назад, чтобы сместить эту часть кода.
Если это не сработает, вам нужно вручную исправить пакет, удалив все неработающие ссылки и связи. opc-diag
может быть удобным для этого.
Кажется, что все элементы отношений следуют этому шаблону <Relationship Id = "x" Type = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide" Target = "slides/slide1.xml"/>\n
. Я не вижу пропущенных имен. Есть ли проблема с этим примером элемента?
Итак, спасибо Scanny за помощь. Вы совершенно правы. Поиск искал ppt/slides/slide#.xml и не нашел для него связи. Причина в том, что отношения закодированы как просто слайды/слайд#.xml (без ppt/). Я зашел в opc-diag, чтобы посмотреть, что я могу там сделать, но нашел простое решение.
В моем предыдущем коде была строка с надписью for slide in ppt.slides:
, и это была ошибка: KeyError: "There is no item named 'ppt/slides/NULL' in the archive"
. При просмотре PresentationML с помощью opc-diag я обнаружил, что связь настроена следующим образом: <Relationship Id = "x" Type = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide" Target = "slides/slide1.xml"/>\n
. Отношения не включают ppt
.
Итак, чтобы избавиться от этого поиска и привести его в соответствие с тем, как PowerPoint хранит отношения между слайдами, я изменил эти строки:
ppt = Presentation('HispPres1.pptx')
for slide in ppt.slides:
к этому
ppt = Presentation('HispPres1.pptx')
slides = ppt.slides
for slide in slides:
Хм, ну, я рад, что у вас все заработало, но я не думаю, что ваш анализ того, почему это правильно. ссылка Target = "slides/slide1.xml"
представляет собой путь родственник, и я ожидаю, что часть .rels, в которой она появляется, уже находится в «каталоге» /ppt/
. Кроме того, ваше изменение кода ничего не меняет; и slides
, и ppt.slides
относятся к объекту Slides
для представления (эта структура вызовов не связана с фактическим расположением в пакете). В любом случае, рад, что это начало работать, я просто не думаю по тем причинам, о которых вы думали :)
Ты прав на 100%. По какой-то причине это работало изначально, но я не мог повторить это. У меня был шаблон компании за тем, который не работал. Когда я удаляю шаблон компании (схема шаблона мастер-слайда), он работает. Это работает, когда я просто использую базовый шаблон PowerPoint по умолчанию.
Очистить PPTX от оборванных отношений можно через:
Файл -> Информация -> Проверить наличие проблем -> Проверить документ.
Очистите, сохраните, воспроизведите скрипт Python.
Не могли бы вы включить полную трассировку стека, полученную для KeyError?