作为 Scribus 文件团队的长期成员,我要随时了解最新的原始码更新,以便对文件进行更新和补充。 我最近在刚升级到 Fedora 27 系统的计算机上使用 Subversion 进行检出操作时,对于下载该文件所需要的时间我感到很惊讶,文件由 HTML 页面和相关影象组成。 我恐怕该专案的文件看起来比专案本身大得多,并且怀疑其中的一些内容是 “僵尸” 文件——不再使用的 HTML 档案以及 HTML 中无法访问到的影象。
我决定为自己建立一个专案来解决这个问题。 一种方法是搜寻未使用的现有影象档案。 如果我可以扫描所有 HTML 档案中的影象引用,然后将该列表与实际影象档案进行比较,那么我可能会看到不匹配的档案。
这是一个典型的影象标签:

我对 src= 之后的第一组引号之间的部分很感兴趣。 在寻找了一些站群解决方案后,我找到一个名为 BeautifulSoup 的 Python 模组。 指令码的核心部分如下所示:
soup = BeautifulSoup(all_text, ‘html.parser’)
match = soup.findAll(“img”)
if len(match) > 0:
for m in match:
imagelist.append(str(m))
我们可以使用这个 findAll 方法来挖出图片标签。 这是一小部分输出:






到现在为止还挺好。我原以为下一步就可以搞定了,但是当我在指令码中尝试了一些字串方法时,它返回了有关标记的错误而不是字串的错误。 我将输出储存到一个档案中,并在 KWrite 中进行编辑。 KWrite 的一个好处是你可以使用正规表示式(regex)来做 “查询和替换” 操作,所以我可以用 n‘, all_text)
if len(match)>0:
for m in match:
imagelist.append(m)
它的一小部分输出如下所示:
images/cmcanvas.png” title=”Context Menu for the document canvas” alt=”Context Menu for the document canvas” />
,这被称为贪婪,意味著它不一定停止在遇到 /> 的第一个例项。我应该补充一点,我也尝试过 src=”(.*)”,这真的没有什么更好的效果,我不是一个正规表示式专家(只是做了这个),找了各种方法来改进这一点但是并没什么用。
做了一系列的事情之后,甚至尝试了 Perl 的 HTML::Parser 模组,最终我试图将这与我为 Scribus 编写的一些指令码进行比较,这些指令码逐个字元的分析文字内容,然后采取一些行动。 为了最终目的,我终于想出了所有这些方法,并且完全不需要正规表示式或 HTML 解析器。 让我们回到展示的那个 img 标签的例子。

我决定回到 src= 这一块。 一种方法是等待 s 出现,然后看下一个字元是否是 r,下一个是 c,下一个是否 =。 如果是这样,那就匹配上了! 那么两个双引号之间的内容就是我所需要的。 这种方法的问题在于需要连续识别上面这样的结构。 一种检视代表一行 HTML 文字的字串的方法是:
for c in all_text:
但是这个逻辑太乱了,以至于不能持续匹配到前面的 c,还有之前的字元,更之前的字元,更更之前的字元。
最后,我决定专注于 = 并使用索引方法,以便我可以轻松地引用字串中的任何先前或将来的字元。 这里是搜寻部分:
index = 3
while index < linelength:
if (all_text[index] == '='):
if (all_text[index-3] == 's') and (all_text[index-2] == 'r') and (all_text[index-1] == 'c'):
imagefound(all_text, imagelist, index)
index += 1
else:
index += 1
else:
index += 1
我用第四个字元开始搜寻(索引从 0 开始),所以我在下面没有出现索引错误,并且实际上,在每一行的第四个字元之前不会有等号。 第一个测试是看字串中是否出现了 =,如果没有,我们就会前进。 如果我们确实看到一个等号,那么我们会看前三个字元是否是 s、r 和 c。 如果全都匹配了,就呼叫函式 imagefound:
def imagefound(all_text, imagelist, index):
end = 0
index += 2
newimage = ''
while end == 0:
if (all_text[index] != '"'):
newimage = newimage + all_text[index]
index += 1
else:
newimage = newimage + 'n'
imagelist.append(newimage)
end = 1
return
我们给函式传送当前索引,它代表著 =。 我们知道下一个字元将会是 ",所以我们跳过两个字元,并开始向名为 newimage 的控制字串新增字元,直到我们发现下一个 ",此时我们完成了一次匹配。 我们将字串加一个换行符(n)新增到列表 imagelist 中并返回(return),请记住,在剩余的这个 HTML 字串中可能会有更多图片标签,所以我们马上回到搜寻回圈中。
以下是我们的输出现在的样子:
images/text-frame-link.png
images/text-frame-unlink.png
images/gimpoptions1.png
images/gimpoptions3.png
images/gimpoptions2.png
images/fontpref3.png
images/font-subst.png
images/fontpref2.png
images/fontpref1.png
images/dtp-studio.png
啊,干净多了,而这只花费几秒钟的时间。 我本可以将索引前移 7 步来剪下 images/ 部分,但我更愿意把这个部分储存下来,以确保我没有剪下掉影象档名的第一个字母,这很容易用 KWrite 编辑成功 —— 你甚至不需要正规表示式。 做完这些并储存档案后,下一步就是执行我编写的另一个指令码 sortlist.py:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# sortlist.py
import os
imagelist = []
for line in open('/tmp/imagelist_parse4.txt').xreadlines():
imagelist.append(line)
imagelist.sort()
outfile = open('/tmp/imagelist_parse4_sorted.txt', 'w')
outfile.writelines(imagelist)
outfile.close()
这会读取档案内容,并储存为列表,对其排序,然后另存为另一个档案。 之后,我可以做到以下几点:
ls /home/gregp/development/Scribus15x/doc/en/images/*.png > ‘/tmp/actual_images.txt’
然后我需要在该档案上执行 sortlist.py,因为 ls 方法的排序与 Python 不同。 我原本可以在这些档案上执行比较指令码,但我更愿意以可视方式进行操作。 最后,我成功找到了 42 个影象,这些影象没有来自文件的 HTML 引用。
这是我的完整解析指令码:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# parseimg4.py
import os
def imagefound(all_text, imagelist, index):
end = 0
index += 2
newimage = ”
while end == 0:
if (all_text[index] != ‘”‘):
newimage = newimage + all_text[index]
index += 1
else:
newimage = newimage + ‘n’
imagelist.append(newimage)
end = 1
return
htmlnames = []
imagelist = []
tempstring = ”
filenames = os.listdir(‘/home/gregp/development/Scribus15x/doc/en/’)
for name in filenames:
if name.endswith(‘.html’):
htmlnames.append(name)
#print htmlnames
for htmlfile in htmlnames:
all_text = open(‘/home/gregp/development/Scribus15x/doc/en/’ + htmlfile).read()
linelength = len(all_text)
index = 3
while index < linelength:
if (all_text[index] == '='):
if (all_text[index-3] == 's') and (all_text[index-2] == 'r') and
(all_text[index-1] == 'c'):
imagefound(all_text, imagelist, index)
index += 1
else:
index += 1
else:
index += 1
outfile = open('/tmp/imagelist_parse4.txt', 'w')
outfile.writelines(imagelist)
outfile.close()
imageno = len(imagelist)
print str(imageno) + " images were found and saved"
指令码名称为 parseimg4.py,这并不能真实反映我陆续编写的指令码数量(包括微调的和大改的以及丢弃并重新开始写的)。 请注意,我已经对这些目录和档名进行了硬编码,但是很容易变得通用化,让使用者输入这些资讯。 同样,因为它们是工作指令码,所以我将输出传送到 /tmp 目录,所以一旦重新启动系统,它们就会消失。
这不是故事的结尾,因为下一个问题是:僵尸 HTML 档案怎么办? 任何未使用的档案都可能会引用影象,不能被前面的方法所找出。 我们有一个 menu.xml 档案作为联机手册的目录,但我还需要考虑 TOC(LCTT 译注:TOC 是 table of contents 的缩写)中列出的某些档案可能引用了不在 TOC 中的档案,是的,我确实找到了一些这样的档案。
最后我可以说,这是一个比影象搜寻更简单的任务,而且开发的过程对我有很大的帮助。