BeautifulSoup`get_all`发电机

Jam*_*ell 6 python parsing beautifulsoup

有没有办法find_all变成更节省内存的发电机?例如:

鉴于:

soup = BeautifulSoup(content, "html.parser")
return soup.find_all('item')
Run Code Online (Sandbox Code Playgroud)

我想改为使用:

soup = BeautifulSoup(content, "html.parser")
while True:
    yield soup.next_item_generator()
Run Code Online (Sandbox Code Playgroud)

(假设正确处理最终StopIteration例外)

内置了一些生成器,但不会在查找中产生下一个结果.find只返回第一个项目.有数以千计的物品,find_all吸收了大量的记忆.对于5792件商品,我看到的内存只有1GB以上的内存.

我很清楚,有更高效的解析器,如lxml,可以实现这一目标.让我们假设还有其他业务限制阻止我使用其他任何东西.

如何find_all以更有效的内存方式转换为生成器进行迭代.

ale*_*cxe 7

BeautifulSoup根据我所知,没有"查找"生成器,但我们可以结合使用SoupStrainer.children生成器.

让我们假设我们有这个示例HTML:

<div>
    <item>Item 1</item>
    <item>Item 2</item>
    <item>Item 3</item>
    <item>Item 4</item>
    <item>Item 5</item>
</div>
Run Code Online (Sandbox Code Playgroud)

我们需要从中获取所有item节点的文本.

我们可以使用SoupStrainer仅解析item标签,然后遍历.children生成器并获取文本:

from bs4 import BeautifulSoup, SoupStrainer

data = """
<div>
    <item>Item 1</item>
    <item>Item 2</item>
    <item>Item 3</item>
    <item>Item 4</item>
    <item>Item 5</item>
</div>"""

parse_only = SoupStrainer('item')
soup = BeautifulSoup(data, "html.parser", parse_only=parse_only)
for item in soup.children:
    print(item.get_text())
Run Code Online (Sandbox Code Playgroud)

打印:

Item 1
Item 2
Item 3
Item 4
Item 5
Run Code Online (Sandbox Code Playgroud)

换句话说,我们的想法是将树切割成所需的标签并使用其中一个可用的生成器,例如.children.您也可以直接使用其中一个生成器,并按生成器体内的名称或其他条件手动过滤标签,例如:

def generate_items(soup):
    for tag in soup.descendants:
        if tag.name == "item":
            yield tag.get_text()
Run Code Online (Sandbox Code Playgroud)

.descendants递归方式生成子元素,而.children只考虑节点的直接子元素.


ekh*_*oro 6

最简单的方法是使用find_next:

soup = BeautifulSoup(content, "html.parser")

def find_iter(tagname):
    tag = soup.find(tagname)
    while tag is not None:
        yield tag
        tag = tag.find_next(tagname)
Run Code Online (Sandbox Code Playgroud)