我目前正在使用Python中的抓取脚本,我想将以下HTML响应映射到多列表或字典中(这没关系)。
我目前的代码是:
from bs4 import BeautifulSoup
from urllib.request import Request, urlopen
req = Request("https://my.site.com/crawl", headers={'User-Agent': 'Mozilla/5.0'})
webpage = urlopen(req)
soup = BeautifulSoup(webpage, 'html.parser')
ul = soup.find('ul', {'class': ''})
运行此操作后,我得到以下结果存储在 ul 中:
<ul>
<li><a class="reference" href="#ref1">Data1</a></li>
<li><a class="reference" href="#ref2">Data2</a>
<ul>
<li><a class="reference" href="#ref3">Data3</a></li>
<li><a class="reference" href="#ref4">Data4</a>
<ul>
<li><a class="reference" href="#ref5"><span class="pre">Data5</span></a></li>
<li><a class="reference" href="#ref6"><span class="pre">Data6</span></a></li>
.
.
.
</ul>
</li>
</ul>
</li>
<li><a class="reference" href="#ref7">Data7</a>
<ul>
<li><a class="reference" href="#ref8"><span class="pre">Data8</span></a></li>
<li><a class="reference" href="#ref9"><span class="pre">Data9</span></a></li>
.
.
.
</ul>
</li>
<li><a class="reference" href="#ref10">Data10</a>
<ul>
<li><a class="reference" href="#ref11"><span class="pre">Data11</span></a></li>
<li><a class="reference" href="#ref12">Data12</a></li>
</ul>
</li>
</ul>
由于这是一个外部网站,我无法控制列表中元素的ID或类。
似乎我无法理解这一点,是否有一种简单的方法将数据排列到列表或字典中?:
dict = {'Data1': {'href': 'ref1'},
'Data2': {'href': 'ref2', {
'Data3': {'href': 'ref3'},
'Data4': {'href': 'ref4', {
'Data5': {'href': 'ref5'},
'Data6': {'href': 'ref6'},
.
.
. }
}
}
}
}
我觉得这是一个繁琐的过程,但我没有看到任何其他方法。
非常感谢任何让我朝着正确方向前进的帮助!
干杯!
答案 0 :(得分:1)
只需递归ul
元素,提取所有包含文字的li
元素的文本,如果有<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
这需要所有直接li
元素;如果有<a>
元素,则该锚元素的文本将变为键,并且我们将标记属性的副本存储为值(忽略任何class
属性)。如果<ul>
标记旁边还有 a
元素,则会递归解析并将其作为children
键添加到<a>
的属性字典中1}}标签。
对于您的样本输入,这会产生:
>>> from pprint import pprint
>>> pprint(parse_ul(soup.ul))
{'Data1': {'href': '#ref1'},
'Data10': {'children': {'Data11': {'href': '#ref11'},
'Data12': {'href': '#ref12'}},
'href': '#ref10'},
'Data2': {'children': {'Data3': {'href': '#ref3'},
'Data4': {'children': {'Data5': {'href': '#ref5'},
'Data6': {'href': '#ref6'}},
'href': '#ref4'}},
'href': '#ref2'},
'Data7': {'children': {'Data8': {'href': '#ref8'}, 'Data9': {'href': '#ref9'}},
'href': '#ref7'}}
答案 1 :(得分:1)
没有琐碎的方法可以做到这一点,但并不是那么麻烦。
例如,您可以递归地执行此操作,如下所示:
def make_data(ul):
d = {}
for a in ul.find_all('a'):
d[a.text] = {'href': a.attrs['href']}
lis = ul.find_all('li', recursive=False)
children = {}
for li in lis:
child = li.ul
if child:
children[li.a.attrs['href']] = make_data(child)
if children:
d['children'] = children
return d
(我必须给每个children
个词组一个键,因为你真正想要的结构不是一个有效的词典。)
当然,您想要,例如,添加一些错误处理,但这应该足以让您入门。
答案 2 :(得分:0)
我真的很喜欢Martijn Pieters parse_ul(),但是我有一些代码不符合该解析器的规则,在单个<ul></ul>
中有一个双<li> .. </li>
,最后一部分是<a ... > text </a>
前缀。
例如<li><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()使其接受此偏差并输出该偏差?
{'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'}
}