BeautifulSoup-通过标签内的文本搜索 - python

观察以下问题:

import re
from bs4 import BeautifulSoup as BS

soup = BS("""
<a href="/customer-menu/1/accounts/1/update">
    Edit
</a>
""")

# This returns the <a> element
soup.find(
    'a',
    href="/customer-menu/1/accounts/1/update",
    text=re.compile(".*Edit.*")
)

soup = BS("""
<a href="/customer-menu/1/accounts/1/update">
    <i class="fa fa-edit"></i> Edit
</a>
""")

# This returns None
soup.find(
    'a',
    href="/customer-menu/1/accounts/1/update",
    text=re.compile(".*Edit.*")
)

由于某种原因,当<i>标签也存在时,BeautifulSoup将不匹配文本。找到标签并显示其文字会产生

>>> a2 = soup.find(
        'a',
        href="/customer-menu/1/accounts/1/update"
    )
>>> print(repr(a2.text))
'\n Edit\n'

对。根据Docs,汤使用正则表达式的match函数,而不是search函数。所以我需要提供DOTALL标志:

pattern = re.compile('.*Edit.*')
pattern.match('\n Edit\n')  # Returns None

pattern = re.compile('.*Edit.*', flags=re.DOTALL)
pattern.match('\n Edit\n')  # Returns MatchObject

好的。看起来挺好的。让我们一起喝汤

soup = BS("""
<a href="/customer-menu/1/accounts/1/update">
    <i class="fa fa-edit"></i> Edit
</a>
""")

soup.find(
    'a',
    href="/customer-menu/1/accounts/1/update",
    text=re.compile(".*Edit.*", flags=re.DOTALL)
)  # Still return None... Why?!

编辑

我基于geckons的解决方案答案:我实现了以下帮助器:

import re

MATCH_ALL = r'.*'


def like(string):
    """
    Return a compiled regular expression that matches the given
    string with any prefix and postfix, e.g. if string = "hello",
    the returned regex matches r".*hello.*"
    """
    string_ = string
    if not isinstance(string_, str):
        string_ = str(string_)
    regex = MATCH_ALL + re.escape(string_) + MATCH_ALL
    return re.compile(regex, flags=re.DOTALL)


def find_by_text(soup, text, tag, **kwargs):
    """
    Find the tag in soup that matches all provided kwargs, and contains the
    text.

    If no match is found, return None.
    If more than one match is found, raise ValueError.
    """
    elements = soup.find_all(tag, **kwargs)
    matches = []
    for element in elements:
        if element.find(text=like(text)):
            matches.append(element)
    if len(matches) > 1:
        raise ValueError("Too many matches:\n" + "\n".join(matches))
    elif len(matches) == 0:
        return None
    else:
        return matches[0]

现在,当我想找到上面的元素时,我只需运行find_by_text(soup, 'Edit', 'a', href='/customer-menu/1/accounts/1/update')

参考方案

问题是您的带有<a>标记的<i>标记不具有您期望的string属性。首先,让我们看一下text=""find()参数的作用。

注意:text参数是一个旧名称,因为BeautifulSoup 4.4.0称为string

从docs:

尽管string用于查找字符串,但是您可以将其与
查找标签的参数:Beautiful Soup将查找其标签的所有标签
.string与您的string值匹配。该代码查找标签
其.string为“Elsie”:

soup.find_all("a", string="Elsie")
# [<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>]

现在,让我们看看Tagstring属性是什么(再次来自docs):

如果代码只有一个孩子,而该孩子是NavigableString,则
子项以.string形式提供:

title_tag.string
# u'The Dormouse's story'

(...)

如果标签包含多个要素,则不清楚
应该引用.string,因此.string定义为None:

print(soup.html.string)
# None

这正是您的情况。您的<a>标记包含一个文本 <i>标记。因此,当尝试搜索字符串时,查找结果将获得None,因此无法匹配。

该如何解决?

也许有更好的解决方案,但我可能会选择这样的方法:

import re
from bs4 import BeautifulSoup as BS

soup = BS("""
<a href="/customer-menu/1/accounts/1/update">
    <i class="fa fa-edit"></i> Edit
</a>
""")

links = soup.find_all('a', href="/customer-menu/1/accounts/1/update")

for link in links:
    if link.find(text=re.compile("Edit")):
        thelink = link
        break

print(thelink)

我认为没有太多链接指向/customer-menu/1/accounts/1/update,因此它应该足够快。

Python:BeautifulSoup-根据名称属性获取属性值 - python

我想根据属性名称打印属性值,例如<META NAME="City" content="Austin"> 我想做这样的事情soup = BeautifulSoup(f) //f is some HTML containing the above meta tag for meta_tag in soup(&#…

Python 3运算符>>打印到文件 - python

我有以下Python代码编写项目的依赖文件。它可以在Python 2.x上正常工作,但是在使用Python 3进行测试时会报告错误。depend = None if not nmake: depend = open(".depend", "a") dependmak = open(".depend.mak&#…

python JSON对象必须是str,bytes或bytearray,而不是'dict - python

在Python 3中,要加载以前保存的json,如下所示:json.dumps(dictionary)输出是这样的{"('Hello',)": 6, "('Hi',)": 5}当我使用json.loads({"('Hello',)": 6,…

Python:检查是否存在维基百科文章 - python

我试图弄清楚如何检查Wikipedia文章是否存在。例如,https://en.wikipedia.org/wiki/Food 存在,但是https://en.wikipedia.org/wiki/Fod 不会,页面只是说:“维基百科没有此名称的文章。”谢谢! 参考方案 >>> import urllib >>> prin…

快速返回没有Python中特定元素的列表的方法 - python

如果我有任意顺序的卡片套装列表,如下所示:suits = ["h", "c", "d", "s"] 我想返回一个没有'c'的列表noclubs = ["h", "d", "s"] 有没有简单的方法可以…