【问题标题】:How to perform re substitutions on <p> tags within a specific class?如何对特定类中的 <p> 标签执行重新替换?
【发布时间】:2013-06-20 01:37:14
【问题描述】:

我有一个包含以下实例的 html 文件:

<p>[CR][LF]
Here is the text etc

和:

...here is the last part of the text.[CR][LF]
</p>

其中[CR][LF] 分别代表回车和换行。

这些段落在具有特定类的 div 中,例如 my_class

我想定位这个特定 div 类中的段落标签并执行以下替换:

# remove new line after opening <p> tag
re.sub("<p>\n+", "<p>", div)
# remove new line before closing </p> tag
re.sub("<p>\n+", "<p>", div)

因此,我的方法是:

  • 打开html文件
  • 隔离特定的 div
  • 隔离这些 div 中的 &lt;p&gt; 标签
  • 仅对这些 &lt;p&gt; 标记执行替换
  • 将修改后的内容写回原来的html文件

这是我到目前为止所拥有的,但是当它进行替换并写回文件时,逻辑失败了:

from bs4 import BeautifulSoup
import re
# open the html file in read mode
html_file = open('file.html', 'r')
# convert to string
html_file_as_string = html_file.read()
# close the html file
html_file.close()
# create a beautiful soup object 
bs_html_file_as_string = BeautifulSoup(html_file_as_string, "lxml")
# isolate divs with specific class
for div in bs_html_file_as_string.find_all('div', {'class': 'my_class'}):
    # perform the substitutions
    re.sub("<p>\n+", "<p>", div)
    re.sub("\n+</p>", "</p>", div)
# open original file in write mode
html_file = open('file', 'w')
# write bs_html_file_as_string (with substitutions made) to file
html_file.write(bs_html_file_as_string)
# close the html file
html_file.close()

我也一直在看美丽的汤的replace_with,但不确定它是否与这里相关。

编辑:

下面的解决方案向我展示了另一种不使用 re.sub 来完成任务的方法。

但是,我需要执行另一个替换,但仍然不知道是否可以执行 re.sub within a specific classwithin a paragraph。具体来说,在下面的示例中,我想用&lt;/p&gt;\n&lt;p&gt; 替换所有[CR][LF]。我曾设想这种情况会发生在潜艇上:

re.sub('\n+', r'</p>\n<p>', str)

来自 SciTE 编辑器的屏幕截图,显示回车符和换行符:

演示 HTML (demo_html.html):

<html>
<body>
<p>lalalalalalalala</p>
<p>lalalalalalalala</p>
<div class="my_class">
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Lorem ipsum..consectetur adipiscing elit.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Lorem ipsum dolor sit amet, consectetur adipiscing elit.Lorem ipsum dolor sit amet, consectetur adipiscing elit."Lorem ipsum dolor sit amet", consectetur adipisc'ing elit.Lorem ipsum dolor...sit amet, consectetur adipiscing elit..
Lorem ipsum dolor sit amet, consectetur adipiscing elit.Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Lorem ipsum dolor sit amet, consectetur adipiscing elit..
.....Lorem ipsum dolor sit amet, consectetur adipiscing elit.Lorem ipsum dolor sit amet, consectetur adipiscing elit.Lorem ipsum dolor sit amet, consectetur adipiscing elit.Lorem ipsum dolor sit amet, consectetur adipiscing elit.Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
</div>
<p>lalalalalalalala</p>
<p>lalalalalalalala</p>
</body>
</html>

演示 Python (demo_python.py):

from bs4 import BeautifulSoup
import re

with open('demo_html.html', 'r') as html_file:
    html_file_as_string = html_file.read()
bs_html_file_as_string = BeautifulSoup(html_file_as_string, "lxml")
for div in bs_html_file_as_string.find_all('div', {'class': 'my_class'}):
    for p in div.find('p'):
    p.string.replace('\n','</p>\n<p>')
with open('demo_html.html', 'w') as html_file:
    html_file.write(bs_html_file_as_string.renderContents())

print 'finished'

【问题讨论】:

    标签: python regex python-2.7 beautifulsoup


    【解决方案1】:

    p.string.strip() 将删除前导、尾随空格。

    p.string.replaceWith(NEW_STRING) 会将 p 标签的文本替换为 NEW_STRING。

    from bs4 import BeautifulSoup
    
    with open('file.html', 'r') as f:
        html_file_as_string = f.read()
    soup = BeautifulSoup(html_file_as_string, "lxml")
    for div in soup.find_all('div', {'class': 'my_class'}):
        for p in div.find('p'):
            p.string.replace_with(p.string.strip())
    with open('file', 'w') as f:
        f.write(soup.renderContents())
    

    顺便说一句,re.sub(..) 返回替换字符串。它不会替换替换的原始字符串。

    >>> import re
    >>> text = '   hello'
    >>> re.sub('\s+', '', text)
    'hello'
    >>> text
    '   hello'
    

    编辑

    已编辑代码以匹配已编辑的问题:

    from bs4 import BeautifulSoup
    
    with open('file.html', 'r') as f:
        html_file_as_string = f.read()
    soup = BeautifulSoup(html_file_as_string, "lxml")
    for div in soup.find_all('div', {'class': 'my_class'}):
        for p in div.findAll('p'):
            new = BeautifulSoup(u'\n'.join(u'<p>{}</p>'.format(line.strip()) for line in p.text.splitlines() if line), 'html.parser')
            p.replace_with(new)
    with open('file', 'w') as f:
        f.write(soup.renderContents())
    

    【讨论】:

    • 如果p 标签有子标签或为空simple testcase,这将不起作用
    • 这个解决方案正在工作并向我展示了如何使用p.string.strip(),但是我已经更新了原始帖子,其中包含需要另一个re.sub 类似修改的更多细节,我无法弄清楚如何应用类似的逻辑到p.string.strip() 解决这个问题。所以我不知道我是否应该继续寻求知道如何应用re.sub 解决方案或类似于p.string.strip() 的替代方案。
    • @user1063287,你能通过在某处上传文件来显示你的file.html吗?
    • 回溯:...new = BeautifulSoup('\n'.join('&lt;p&gt;{}&lt;/p&gt;'.format(line.strip()) for line in p.text.splitlines() if line), 'lxml') UnicodeEncodeError: 'ascii' codec can't encode character u'\u201c' in position 0: ordinal not in range(128)。我猜这是指段落中的"
    • @user1063287,对不起,我没有注意到。我更新了代码以使用html.parser
    【解决方案2】:

    您需要检查p 的第一个和最后一个内容元素是否是文本节点(bs4.NavigableString 的实例,它是str 的子类)。这应该有效:

    from bs4 import BeautifulSoup, NavigableString
    import re
    
    html_file_as_string = """
    <p>test1</p>
    
    <p>
    test2</p>
    <p>test3
    </p>
    
    <p></p>
    
    <p>
    test4
    <b>...</b>
    test5
    </p>
    
    <p><b>..</b>
    </p>
    
    <p>
    <br></p>
    
    """
    
    soup = BeautifulSoup(html_file_as_string, "lxml")
    for p in soup.find_all('p'):
        if p.contents:
            if isinstance(p.contents[0], NavigableString):
                p.contents[0].replace_with(p.contents[0].lstrip())
            if isinstance(p.contents[-1], NavigableString):
                p.contents[-1].replace_with(p.contents[-1].rstrip())
    
    print(soup)
    

    输出:

    <html><body><p>test1</p>
    <p>test2</p>
    <p>test3</p>
    <p></p>
    <p>test4
    <b>...</b>
    test5</p>
    <p><b>..</b></p>
    <p><br/></p>
    </body></html>
    

    使用正则表达式解析/处理 html 几乎总是一个坏主意。

    【讨论】:

      【解决方案3】:

      for 循环中的替换结果不会被存储;您可以尝试以下方法:

      import re
      
      strings = ['foo', 'bar', 'qux']
      
      for k, s in enumerate(strings):
          strings[k] = re.sub('foo', 'cheese', s)
      

      【讨论】:

        猜你喜欢
        • 2021-05-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-09-08
        • 2013-06-02
        相关资源
        最近更新 更多