如何按子元素使用 BeautifulSoup 4 对无序列表进行排序
How do I sort an unordered list using BeautifulSoup 4 by a child element
我是 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。列表需要按照第三个span的内容排序,with 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
:
开始
>>> 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()
function and the 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 编码和 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。列表需要按照第三个span的内容排序,with 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
:
>>> 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()
function and the 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)