python - 如何使用 BeautifulSoup 4 按子元素对无序列表进行排序

标签 python sorting python-3.x beautifulsoup html-lists

我是 Python 编码和 BeautifulSoup4 的新手。我有一个需要排序的 HTML 列表,该列表遵循以下模式:

<div id="mgioLangSelector">
<ul id="mgioLangList">
<li><a href="" class="mgio-autonym"><span class="mgioAutonymNative" lang="am">አማርኛ</span><span class="mgioAutonymSeperator"> / </span><span class="mgioAutonymEnglish">Amharic</span</a></li>
<li><a href="" class="mgio-autonym"><span class="mgioAutonymNative" lang="hr">hrvatski</span><span class="mgioAutonymSeperator"> / </span>        <span class="mgioAutonymEnglish">Croatian</span</a></li>
<li><a href="" class="mgio-autonym"><span class="mgioAutonymNative" lang="cs">čeština</span><span class="mgioAutonymSeperator"> / </span><span class="mgioAutonymEnglish">Czech</span</a></li>
<li><a href="" class="mgio-autonym"><span class="mgioAutonymNative" lang="vi">tiếng Việt</span><span class="mgioAutonymSeperator"> / </span><span class="mgioAutonymEnglish">Vietnamese</span</a></li>
<li><a href="" class="mgio-autonym"><span class="mgioAutonymNative" lang="sq">shqip</span><span class="mgioAutonymSeperator"> / </span><span class="mgioAutonymEnglish">Albanian</span</a></li>
</ul>
</div>

我需要就地对列表进行排序并保存生成的 HTML。该列表需要按第三个跨度的内容排序,class = mgioAutonymEnglish

我怀疑我需要将 sorted() 与适当的键函数一起使用,但结果是空白。

我尝试过以下代码:

from bs4 import BeautifulSoup
from lxml import etree
soup = BeautifulSoup(open("interimResults.html"), 'lxml', from_encoding="utf-8")
matches = soup.find_all("span", attrs={"class": "mgioAutonymEnglish"})
sorted(matches, key=lambda elem: elem.text)

这将对范围的内容进行排序,但不会对原始列表中的列表进行排序。我认为我需要更改 lambda 函数,但我目前不知所措。

我需要做什么或更改才能成功对列表进行排序,然后将这些更改保存在 HTML 文档中?

最佳答案

这实际上比您想象的要复杂一些,因此逐步完成它会有所帮助。

让我们从您的开始:

>>> soup
<html><body>
...
<div id="mgioLangSelector">
<ul id="mgioLangList">
<li><a class="mgio-autonym" href=""><span class="mgioAutonymNative" lang="am">አማርኛ</span><span class="mgioAutonymSeperator"> / </span><span class="mgioAutonymEnglish">Amharic</span></a></li>
<li><a class="mgio-autonym" href=""><span class="mgioAutonymNative" lang="hr">hrvatski</span><span class="mgioAutonymSeperator"> / </span> <span class="mgioAutonymEnglish">Croatian</span></a></li>
<li><a class="mgio-autonym" href=""><span class="mgioAutonymNative" lang="cs">čeština</span><span class="mgioAutonymSeperator"> / </span><span class="mgioAutonymEnglish">Czech</span></a></li>
<li><a class="mgio-autonym" href=""><span class="mgioAutonymNative" lang="vi">tiếng Việt</span><span class="mgioAutonymSeperator"> / </span><span class="mgioAutonymEnglish">Vietnamese</span></a></li>
<li><a class="mgio-autonym" href=""><span class="mgioAutonymNative" lang="sq">shqip</span><span class="mgioAutonymSeperator"> / </span><span class="mgioAutonymEnglish">Albanian</span></a></li>
</ul>
</div>
...
</body></html>

首先要做的是获取ul:

ul = soup.find(attrs={"id": "mgioLangList"})

现在我们可以extract()其中的所有 li 元素并将它们存储在列表中:

items = [li.extract() for li in ul.find_all("li")]

为了对列表进行排序,我们需要一个键。你的思路是对的,但实际上它需要看起来像这样:

items.sort(key=lambda e: e.find(attrs={"class": "mgioAutonymEnglish"}).string)

现在我们有了 li 元素的排序列表,我们可以将它们插入到文档中。可能不太明显的是 ul 不仅仅包含 li 元素 - 它们由换行符分隔,但换行符仍然存在:

>>> ul
<ul id="mgioLangList">





</ul>

...事实上,它们仍然是六个独立的 '\n' 字符串:

>>> ul.contents
['\n', '\n', '\n', '\n', '\n', '\n']

为了将排序后的 li 元素列表插入到这些换行符之间,我们可以使用内置 zip()函数和 insert_after()方法:

for linebreak, li in reversed(list(zip(ul.contents, items))):
    linebreak.insert_after(li)

请注意,由于我们通过在迭代时插入元素来修改 ul.contents,因此有必要这样做 in reverse这样 for 循环就不会自己绊倒。

所以...

>>> soup
<html><body>
...
<div id="mgioLangSelector">
<ul id="mgioLangList">
<li><a class="mgio-autonym" href=""><span class="mgioAutonymNative" lang="sq">shqip</span><span class="mgioAutonymSeperator"> / </span><span class="mgioAutonymEnglish">Albanian</span></a></li>
<li><a class="mgio-autonym" href=""><span class="mgioAutonymNative" lang="am">አማርኛ</span><span class="mgioAutonymSeperator"> / </span><span class="mgioAutonymEnglish">Amharic</span></a></li>
<li><a class="mgio-autonym" href=""><span class="mgioAutonymNative" lang="hr">hrvatski</span><span class="mgioAutonymSeperator"> / </span> <span class="mgioAutonymEnglish">Croatian</span></a></li>
<li><a class="mgio-autonym" href=""><span class="mgioAutonymNative" lang="cs">čeština</span><span class="mgioAutonymSeperator"> / </span><span class="mgioAutonymEnglish">Czech</span></a></li>
<li><a class="mgio-autonym" href=""><span class="mgioAutonymNative" lang="vi">tiếng Việt</span><span class="mgioAutonymSeperator"> / </span><span class="mgioAutonymEnglish">Vietnamese</span></a></li>
</ul>
</div>
...
</body></html>

这是整个事情:

ul = soup.find(attrs={"id": "mgioLangList"})
items = [li.extract() for li in ul.find_all("li")]
items.sort(key=lambda e: e.find(attrs={"class": "mgioAutonymEnglish"}).string)
for linebreak, li in reversed(list(zip(ul.contents, items))):
    linebreak.insert_after(li)

关于python - 如何使用 BeautifulSoup 4 按子元素对无序列表进行排序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30158116/

相关文章:

python - 在python中使用numpy.linalg.eig后对特征值和相关特征向量进行排序

用于数字和字母数字字符串的 JavaScript 数组排序函数

php - 按键显示数组顺序的最佳方式

python - 列出 QMainWindow 的所有快捷方式

python - 如果列值中包含列表值,则在列上过滤数据框。 Pandas

python - 如何 : Flask Use Global Logger

python - 分离实例错误: SQLAlchemy wants to refresh the DateTime attribute of an expunged instance

python - 调用变量时运行进程

python - 如何有效地 vstack 一系列大型 numpy 数组 block ?

python - 使用 python 中的一个衬垫将配置文件加载到字典中