python html解析
作为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 ,这使您更轻松地了解如何从此刻下来。 KWrite的另一个好处是,如果使用正则表达式做出了错误的选择,则可以撤消它。
但是我认为,肯定有比这更好的东西,所以我转向了regex,或更具体地说是Python的re
模块。 这个新脚本的相关部分如下所示:
match
=
re .
findall
( r
'src="(.*)/>'
, 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="(.*)/>
,它被称为greedy ,这意味着它不一定会在遇到的/>
的第一个实例处停止。我应该补充一点,我也尝试过src="(.*)"
,这实际上并没有更好。不是一名正则表达式专家 (刚刚完成了这项工作),我寻找各种想法来改善这一点并没有帮助。
经过一系列其他事情,甚至使用Perl尝试了HTML::Parser
,我最终尝试将其与我为Scribus编写的一些脚本的情况进行比较,这些脚本逐个字符地分析文本框架的内容,然后进行一些分析。行动。 出于我的目的,我最终想到的是对所有这些方法的改进,并且根本不需要正则表达式或HTML解析器。 让我们回到我展示的示例img
标签。
我决定回到src=
部分。 一种方法是等待s
的出现,然后查看下一个字符是r
,下一个c
和下一个=
。 如果是这样,宾果游戏! 然后,我需要在两组双引号之间进行选择。 问题是挂在它们上面的结构。 查看代表一行HTML文本的字符串的一种方法是:
for c in all_text:
但是逻辑太混乱了,无法挂在前一个c
,前一个c
,前一个c
和前一个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
的保持字符串中,直到到达以下字符"
为止。 我们将字符串加上newline
添加到列表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
啊,干净得多,这只花了几秒钟。 我可以跳出另外七个索引点来剪切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中列出的某些文件可能引用了TOC中没有的文件,是的,我确实找到了一些。
最后,我要说的是,这比图像搜索要简单得多,而我已经开发的流程对此有很大帮助。
翻译自: https://opensource.com/article/18/1/parsing-html-python
python html解析