【问题标题】:How can I get only visible text from some HTML node in Python如何仅从 Python 中的某个 HTML 节点获取可见文本
【发布时间】:2014-12-26 15:23:36
【问题描述】:

如何从 Python 中的某个 HTML 节点获取仅可见文本

假设我有一个这样的节点:

<span>
   <style>.vAnH{display:none}.vsP6{display:inline}</style>
   <span class="vAnH">34</span>
   <span />
   <span style="display: inline">111</span>
   <span style="display:none">120</span>
   <span class="vAnH">120</span>
   <div style="display:none">120</div>
   <span class="78">.</span>
   <span class="vAnH">100</span>
   <div style="display:none">100</div>
   161
   <span style="display: inline">.</span>
   <span class="174">126</span>
   <span class="vAnH">159</span>
   <div style="display:none">159</div>
   <span />
   <span class="vsP6">.</span>
   <span style="display:none">5</span>
   <span class="vAnH">5</span>
   <div style="display:none">5</div>
   <span style="display:none">73</span>
   <span class="vAnH">73</span>
   <div style="display:none">73</div>
   <span class="221">98</span>
   <span style="display:none">194</span>
   <div style="display:none">194</div>
</span>

是否有任何第三方库可以做到这一点,还是我应该手动解析它?

【问题讨论】:

  • @BrtH 我已经在使用它了,但我没有看到任何解决方案只在此处获取可见文本
  • 好的。我认为将 find_all 与过滤器 function 结合使用是可能的。
  • 没有独立的 HTML 解析器或 XPath 引擎能够为您应用 CSS 规则。您可能必须使用无头浏览器(即 selenium)来执行此类操作。
  • (可选地编写逻辑来过滤显示:自己没有,是的)

标签: python html html-parsing


【解决方案1】:

有多种方法可以让最终用户在浏览器中显示/隐藏节点。 BeautifulSoup 是一个 HTML 解析器,它不知道一个元素是否会被显示。不过,这里有一个尝试:

例如,如果某个元素被 CSS 规则隐藏,它将不起作用,但可能适用于您的用例。

最简单的选择是切换到selenium.text 这里只返回元素的可见文本:

from selenium import webdriver

driver = webdriver.Firefox() 
driver.get('http://domain.com')

element = driver.find_element_by_id('id_of_an_element')
print(element.text)

【讨论】:

  • 不打开浏览器有什么办法吗?
  • @FrozenHeart 好吧,一个无头的PhantomJS 浏览器可以通过selenium 自动化。可能是您的选择。
  • @alecxe,当我尝试您的上述代码时,它显示以下错误:“ Traceback (most recent call last): File "/home/barpa32/anaconda3/lib/python3.8/site-packages /selenium/webdriver/common/service.py”,第 72 行,在 start self.process = subprocess.Popen(cmd, env=self.env, File "/home/barpa32/anaconda3/lib/python3.8/subprocess. py",第 854 行,在 init self._execute_child(args,可执行文件,preexec_fn,close_fds,文件 "/home/barpa32/anaconda3/lib/python3.8/subprocess.py",第 1702 行, in _execute_child raise child_exception_type(errno_num, err_msg, err_filename)"
【解决方案2】:

如果您不想采用 Selenium 方式,可以使用 BeautifulSoup 获得一些东西:

from bs4 import BeautifulSoup

def is_visible_span_or_div(tag, is_parent=False):
    """ This function checks if the element is a span or a div,
    and if it is visible. If so, it recursively checks all the parents
    and returns False is one of them is hidden """

    # loads the style attribute of the element
    style = tag.attrs.get('style', False)

    # checks if element is div or span, if it's not a parent
    if not is_parent and tag.name not in ('div', 'span'):
        return False

    # checks if the element is hidden
    if style and ('hidden' in style or 'display: none' in style):
        return False

    # makes a recursive call to check the parent as well
    parent = tag.parent
    if parent and not is_visible_span_or_div(parent, is_parent=True):
        return False

    # neither the element nor its parent(s) are hidden, so return True
    return True

html = """
    <span style="display: none;">I am not visible</span>
    <span style="display: inline">I am visible</span>
    <div style="display: none;">
        <span>I am a visible span inside a hidden div</span>
    </div>
"""

soup = BeautifulSoup(html)

visible_elements = soup.find_all(is_visible_span_or_div)

print(visible_elements)

请记住,它并不能准确反映浏览器显示或隐藏元素的方式,因为其他因素可能决定元素的可见性(例如宽度、高度、不透明度、绝对定位在窗口...)。

尽管如此,这个脚本还是相当可靠的,因为它还递归检查所有元素的父元素,并在找到隐藏的父元素时立即返回 False。

我看到这个函数的唯一问题是它的开销很大,因为它必须检查每个元素的所有父元素,即使这些元素恰好在 DOM 树中。它可以很容易地为此进行优化,但可能会以可读性为代价。

【讨论】:

  • @Jivan,我试过了,你的代码有效,谢谢。我修改了代码的最后 3 行,它只打印可见元素中的文本(我的意思是没有 HTML 标记):“#soup = BeautifulSoup(html, 'features="lxml') soup = BeautifulSoup(html, 'html .parser') visible_elements = soup.find_all(is_visible_span_or_div) for teks in visible_elements: teks=teks.text print(teks) "
  • 但是,它不能正常工作(这意味着工作但只显示视觉文本的某些部分[正文下方的文本)此网页:“saugeentimes.com/…
【解决方案3】:

您需要编写一个自定义过滤器函数。一个工作示例:

from bs4 import BeautifulSoup
import re

data = '''<span>
   <style>.vAnH{display:none}.vsP6{display:inline}</style>
   <span class="vAnH">34</span>
   <span />
   <span style="display: inline">111</span>
   <span style="display:none">120</span>
   <span class="vAnH">120</span>
   <div style="display:none">120</div>
   <span class="78">.</span>
   <span class="vAnH">100</span>
   <div style="display:none">100</div>
   161
   <span style="display: inline">.</span>
   <span class="174">126</span>
   <span class="vAnH">159</span>
   <div style="display:none">159</div>
   <span />
   <span class="vsP6">.</span>
   <span style="display:none">5</span>
   <span class="vAnH">5</span>
   <div style="display:none">5</div>
   <span style="display:none">73</span>
   <span class="vAnH">73</span>
   <div style="display:none">73</div>
   <span class="221">98</span>
   <span style="display:none">194</span>
   <div style="display:none">194</div>
</span>'''

soup = BeautifulSoup(data)
no_disp = re.search(r'\.(.+?){display:none}', soup.style.string).group(1)

def find_visible(tag):
    return (not tag.name == 'style') and (not no_disp in tag.get('class', '')) and (not 'display:none' in tag.get('style', ''))

for tag in soup.find_all(find_visible, text=True):
    print tag.string

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-01-07
    • 2019-03-10
    • 1970-01-01
    • 2018-11-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多