在python的一个爬虫脚本中,我使用BeautifulSoup来解析一个html多列表,几乎与问题的含义相同{。使用BeautifulSoup寻找所有 "ul "和 "li "元素。},并由Martijn Pieters使用他的python函数parse_ul()回答。
def parse_ul(elem):
result = {}
for sub in elem.find_all('li', recursive=False):
if sub.a is None:
continue
data = {k: v for k, v in sub.a.attrs.items() if k != 'class'}
if sub.ul is not None:
# recurse down
data['children'] = parse_ul(sub.ul)
result[sub.a.get_text(strip=True)] = data
return result
我需要解析一个不符合Martijn Pieters解析器规则的多列。这个html多列包含一个双 <ul></ul>
在单个 <li> .. </li>
其中最后一节得到了一个 <a ... > text </a>
前缀 <li>
)
例如 <li><a ...></a><ul> </ul> <a..></a><ul> </ul> </li>
见下文
<ul>
<li><a class="ref" href="#ref1">Data1</a></li>
<li><a class="ref" href="#ref2">Data2</a>
<ul>
<li><a class="ref" href="#ref4">Data4</a>
<ul>
<li><a class="ref" href="#ref5"><span class="pre">Data5</span></a>/li>
<li><a class="ref" href="#ref6"><span class="pre">Data6</span></a></li>
.
.
</ul>
<!-- a-tag without preceding <li> tag -->
<a class="ref" href="#ref4a">Data4a</a>
<ul>
<li><a class="ref" href="#ref5a"><span class="pre">Data5a</span></a></li>
<li><a class="ref" href="#ref6a"><span class="pre">Data6a</span></a></li>
.
.
</ul>
</li>
</ul>
</li>
.
.
</ul>
我不知道如何改变parse_ul(),使它接受这个偏差并输出这个?Martijn Pieters的解析器是辉煌的,一定也有一个辉煌的解决方案,我的问题:-)
{'Data1': {'href': '#ref1'},
'Data2': {'children': {'Data4': {'children': {'Data5': {'href': '#ref5'},
'Data6': {'href': '#ref6'}}},
'href': '#ref4'},
{'Data4a': {'children':{'Data5a': {'href': '#ref5a'},
'Data6a': {'href': '#ref6a'}}},
'href': '#ref4a'},
'href': '#ref2'}
}
下面的脚本。
from bs4 import BeautifulSoup
import pprint
pp = pprint.PrettyPrinter(indent=4) # Init pritty print (pprint)
soup = BeautifulSoup(html_contents, 'lxml')
menu_dict = parse_ul(soup.ul)
pp.pprint(menu_dict)
将产生以下输出,其中缺少了第二部分包含在 <a..></a><ul> </ul>
:
{'Data1': {'href': '#ref1'},
'Data2': {'children': {'Data4': {'children': {'Data5': {'href': '#ref5'},
'Data6': {'href': '#ref6'}}},
'href': '#ref4'},
'href': '#ref2'}
}
一个可能的解决方法是在解析前修改HTML代码,这样可以找到所有的 "ul "和 "li "元素。
这样就可以找到所有的 "ul "和 "li "元素 <a>
标签与以前的兄弟姐妹 <ul>
并将其插入到适当的位置。
txt = '''<ul>
<li><a class="ref" href="#ref1">Data1</a></li>
<li><a class="ref" href="#ref2">Data2</a>
<ul>
<li><a class="ref" href="#ref4">Data4</a>
<ul>
<li><a class="ref" href="#ref5"><span class="pre">Data5</span></a>/li>
<li><a class="ref" href="#ref6"><span class="pre">Data6</span></a></li>
</ul>
<!-- a-tag without preceding <li> tag -->
<a class="ref" href="#ref4a">Data4a</a>
<ul>
<li><a class="ref" href="#ref5a"><span class="pre">Data5a</span></a></li>
<li><a class="ref" href="#ref6a"><span class="pre">Data6a</span></a></li>
</ul>
</li>
</ul>
</li>
</ul>'''
from bs4 import BeautifulSoup
soup = BeautifulSoup(txt, 'html.parser')
# find "lone" <A>+<UL> tags, wrap them in new <LI> tag and append the li tag after parent <LI>:
for a in soup.select('a'):
prev = a.find_previous_sibling()
if prev and prev.name == 'ul':
parent_li = a.parent
next_ul = a.find_next('ul')
new_li = soup.new_tag("li")
new_li.append(a)
new_li.append(next_ul)
parent_li.insert_after(new_li)
# this is unchanged code from your question:
def parse_ul(elem):
result = {}
for sub in elem.find_all('li', recursive=False):
if sub.a is None:
continue
data = {k: v for k, v in sub.a.attrs.items() if k != 'class'}
if sub.ul is not None:
# recurse down
data['children'] = parse_ul(sub.ul)
result[sub.a.get_text(strip=True)] = data
return result
from pprint import pprint
pprint(parse_ul(soup.ul))
打印:
{'Data1': {'href': '#ref1'},
'Data2': {'children': {'Data4': {'children': {'Data5': {'href': '#ref5'}},
'href': '#ref4'},
'Data4a': {'children': {'Data5a': {'href': '#ref5a'},
'Data6a': {'href': '#ref6a'}},
'href': '#ref4a'}},
'href': '#ref2'}}