【问题标题】:BeautifulSoup removing nested tagsBeautifulSoup 删除嵌套标签
【发布时间】:2013-10-19 21:51:51
【问题描述】:

我正在尝试使用 BeautifulSoup 制作通用刮板,我正在尝试检测标签下的直接文本可用。

考虑这个例子:

<body>
<div class="c1">
    <div class="c2">
        <div class="c3">
            <div class="c4">
                <div class="c5">
                    <h1> A heading for section </h1>
                </div>
                <div class="c5">
                    <p> Some para </p>
                </div>
                <div class="c5">
                    <h2> Sub heading </h2>
                    <p> <span> Blah Blah </span> </p>
                </div>
            </div>
        </div>
    </div>
</div>
</body>

这里我的目标是提取 (div with class c4),因为它包含所有文本内容。 c1 - c3 之前的其他 div 对我来说只是包装器。

我想出的一种识别节点的可能方法是:

if node.find(re.compile("^h[1-6]"), recursive=False) is not None:
    return node.parent.parent

但是对于这个案例来说太具体了。

是否有任何优化方法可以在一级递归中查找文本。即,如果我做类似的事情

node.find(text=True, recursion_level=1)

那么它应该返回仅考虑直接子级的文本。

到目前为止我的解决方案,不确定它是否适用于所有情况。

def check_for_text(node):
    return node.find(text=True, recursive=False)

def check_1_level_depth(node):
    if check_for_text(node):
        return check_for_text(node)

    return map(check_for_text, node.children)

对于上面的代码:node是当前正在检查的soup的一个元素,即div、span等。 请假设我正在处理 check_for_text() (AttributeError: 'NavigableString') 中的所有异常

【问题讨论】:

  • 您是否尝试过使用 CSS 选择器? soup.select(".c4")
  • 我不能使用 css 选择器。因为不同的站点会有不同的命名。
  • 注意data-mining指的是对大量数据进行复杂的统计分析。您可能指的是web-scraping,从网页中提取文本。
  • @Anony-Mousse,感谢您提供正确的标签。

标签: python python-2.7 web-scraping beautifulsoup


【解决方案1】:

我认为你需要的是这样的:

bs = BeautifulSoup(html)
all = bs.findAll()

previous_elements = []
found_element = None

for i in all:
    if not i.string:
        previous_elements.append(i)
    else:
        found_element = i
        break

print("previous:")
for i in previous_elements:
    print(i.attrs)

print("found:")
print(found_element)

输出:

previous:
{}
{'class': ['c1']}
{'class': ['c2']}
{'class': ['c3']}
{'class': ['c4']}
found:
<h1> A heading for section </h1>

【讨论】:

  • 可能有多个 'div' 与所示示例不同。同样在找到带有字符串的元素后,我应该返回元素的父元素吗?
  • 然后你可以为所有的div重复同样的工作
  • 不要在 c5 上中断,而是保存并继续
  • 解析树上的 DFS 怎么样?
  • 如果你继续循环而不是中断,它将是 DFS
【解决方案2】:

原来我必须编写一个递归函数来消除带有单个孩子的标签。代码如下:

# Pass soup.body in following
def process_node(node):
    if type(node) == bs4.element.NavigableString:
        return node.text
    else:
        if len(node.contents) == 1:
            return process_node(node.contents[0])
        elif len(node.contents) > 1:
            return map(process_node, node.children)

到目前为止,它运行良好且快速。

【讨论】:

  • 使用if isinstance(node, bs4.element.NavigableString): 而不是if type(node) == bs4.element.NavigableString: