我试图使用BeautifulSoup在Python中解析一些HTML文档中的文本。在文档中,部分段落和换行格式使用<br/>标记完成,部分使用<p></p>标记完成。我想完全删除<br/>标记,并在进一步处理之前将前面的文本包装到<p></p>标记中,这样就可以对所有文档应用相同的方法。
我在Beautifulsoup sibling structure with br tags上看过答案。答案描述了如何完全删除<br>标记,但这会留下未封闭文本的片段,然后这些片段就无法轻松隔离。
我使用lxml提出了一个部分解决方案,定位所有<br/>标记的前一个同级,并将其括起来,但是由于<br/>标记在文档中没有以任何一致的方式使用,因此有时前一个同级是,例如,一个<b><i>节点或我要包装的文本的另一个子集。问题是如何以编程方式查找前一个换行符后面的所有文本,以确定开始<p>标记应该放在哪里。有没有我没想到的解决办法?
我想用一个<br/>或其他标记(例如关闭</p>等)将每个文本段与前一个文本段分隔开,删除<br/>标记,并将该文本段包装到<p></p>标记中。
另一种表达问题的方法是:每次最终用户看到换行符时,我都希望前面的文本段位于它自己的<p>包装器中。
一些HTML:

<h3>Program</h3>
<p>Respighi - <i>Trittico Botticelliano</i>, P. 151<br>Barber - Knoxville: <i>Summer of 1915</i>, Op. 24<br>Korngold - <i>Much Ado About Nothing</i>, Op. 11: Suite</p>
<hr>

示例输出:
<h3>Program</h3>
<p>Respighi - <i>Trittico Botticelliano</i>, P. 151</p>
<p>Barber - Knoxville: <i>Summer of 1915</i>, Op. 24</p>
<p>Korngold - <i>Much Ado About Nothing</i>, Op. 11: Suite</p>
<hr>

更新:由于上面的例子非常简单,这里有一个实际HTML的例子,应该可以用同样的方式处理。
<html>
 <body>
 <p>
 </p>
 <p>
  <span class="orangeheadline">
    Masterworks presented by EBSCO &amp; Vulcan Value Partners
  </span>
  <br/>
  <span class="boldtext">
    Justin Brown Returns! Mozart &amp; Beethoven
   <br/>
   <br/>
    Justin Brown, conductor &amp; piano
  </span>
 </p>
<br/>
<p>
  <span class="boldtext">
   Nov. 17 &amp; 18, 2017 at 8pm
  <br/>
  </span>
  <br/>
  Alys Stephens Center | Jemison Concert Hall | Map &amp; Directions
</p>
 <br/>
 <br/>
<p>
<strong>
  MOZART:
 </strong>
  Piano Concerto No. 27
  <br/>
 <strong>
 CLYNE:
 </strong>
 The Midnight Hour
 <br/>
 <strong>
 BEETHOVEN:
 </strong>
 Symphony No. 4
 <br/>
 <strong>
 </strong>
 <br/>
</p>
<p class="MsoNormal" style="margin-bottom: 0.0001pt;">
  <span style="">
   Justin Brown returns as both conductor and pianist! Maestro Browns virtuosity shines as he leads Mozarts delightful Concerto no. 27 from the keyboard, and takes the podium to conduct Beethovens graceful Fourth Symphony.
  <br/>
  <br/>
  <em>
  Gain insight into the works youre about to hear by joining us at 7pm for Concert Comments in the Reynolds-Kirschbaum Recital Hall. Free.
  </em>
 </span>
 <br/>
 <br/>
 <span class="MsoNormal" style="margin-bottom: 0.0001pt;">
 Click here for information about the Coffee Concert
 </span>
</p>
  <br/>
  <span class="date">
    Past Event
  </span>
  <br/>
  <br/>
  <br/>
</body>
</html>

最佳答案

一些重新构建容器标签的魔术显然可以做类似的事情(最初提出的find不能)

import bs4
html="""
<html>
  <body>
    <!-- <p> -->
      Lorem ipsum dolor sit amet,<br>
      consectetur <b>adipiscing</b> elit,<br>
      sed do eiusmod tempor incididunt<br>
      ut labore et dolore magna aliqua.
    <!-- </p> -->
  </body>
</html>"""
soup=bs4.BeautifulSoup(html,"lxml")
print("Before:")
print(soup.prettify())
root=soup.find('br').parent
p=soup.new_tag('p')
for x in list(root.contents):
  if x.name=='br':
    if(p.contents):
      x.insert_before(p)
    p=soup.new_tag('p')
  else:
    p.contents.append(x)
  x.extract()
if(p.contents):
  root.contents.append(p)
if(root.name=='p'):
  root.unwrap()
print()
print("After:")
print(soup.prettify())
print("Re-parsed:")
print(bs4.BeautifulSoup(str(soup),"lxml").prettify())

代码定位一个extract标记,然后在其父标记(称为根内部)上工作,并将所有内容包装到它遇到的replace_with标记之间的<br>-s中。不是很干净的代码,但在某种程度上有效。
如果根碰巧已经是一个<p>标记,那么它将被取消包装(您可以通过取消对该<br>-<p>对的注释来测试它)
它保留了子标签(就像这里的粗体部分),但它也是它的弱点,因为所有这些子标签都会进入最近的<p>。这里的注释没有什么问题,但是如果有标题,或者实际上是任何块元素的话,看起来会很奇怪。
立即输出看起来很难看,所以我只显示重新解析的一个:-)
<html>
 <body>
  <p>
   <!-- <p> -->
   Lorem ipsum dolor sit amet,
  </p>
  <p>
   consectetur
   <b>
    adipiscing
   </b>
   elit,
  </p>
  <p>
   sed do eiusmod tempor incididunt
  </p>
  <p>
   ut labore et dolore magna aliqua.
   <!-- </p> -->
  </p>
 </body>
</html>

一般来说,它也适用于故意作恶的例子,有两件事需要修改:
只要遇到更多的</p>标记,它就会在循环中运行。我不得不为每次迭代重新解析HTML,不知何故一个简单的<p>是不够的
显然,新的“empty”标记已经包含换行符,因此引入了这种奇怪的空性检查:<br>(为了增加乐趣,还需要对元素进行字符串转换)
否则:
import bs4
html="""
<html>
 <body>
 <p>
 </p>
 <p>
  <span class="orangeheadline">
Masterworks presented by EBSCO &amp; Vulcan Value Partners
  </span>
  <br/>
  <span class="boldtext">
    Justin Brown Returns! Mozart &amp; Beethoven
   <br/>
   <br/>
    Justin Brown, conductor &amp; piano
  </span>
 </p>
<br/>
<p>
  <span class="boldtext">
   Nov. 17 &amp; 18, 2017 at 8pm
  <br/>
  </span>
  <br/>
  Alys Stephens Center | Jemison Concert Hall | Map &amp; Directions
</p>
 <br/>
 <br/>
<p>
<strong>
  MOZART:
 </strong>
  Piano Concerto No. 27
  <br/>
 <strong>
 CLYNE:
 </strong>
 The Midnight Hour
 <br/>
 <strong>
 BEETHOVEN:
 </strong>
 Symphony No. 4
 <br/>
 <strong>
 </strong>
 <br/>
</p>
<p class="MsoNormal" style="margin-bottom: 0.0001pt;">
  <span style="">
   Justin Brown returns as both conductor and pianist! Maestro Browns virtuosity shines as he leads Mozarts delightful Concerto no. 27 from the keyboard, and takes the podium to conduct Beethovens graceful Fourth Symphony.
  <br/>
  <br/>
  <em>
  Gain insight into the works youre about to hear by joining us at 7pm for Concert Comments in the Reynolds-Kirschbaum Recital Hall. Free.
  </em>
 </span>
 <br/>
 <br/>
 <span class="MsoNormal" style="margin-bottom: 0.0001pt;">
 Click here for information about the Coffee Concert
 </span>
</p>
  <br/>
  <span class="date">
    Past Event
  </span>
  <br/>
  <br/>
  <br/>
</body>
</html>"""
soup=bs4.BeautifulSoup(html,"lxml")
print("Before:")
print(soup.prettify())

br=soup.find('br')
while br:
  root=br.parent
  p=soup.new_tag('p')
  for x in list(root.contents):
    if x.name=='br':
      if("".join([str(y) for y in p.contents]).strip()):
        x.insert_before(p)
      p=soup.new_tag('p')
    else:
      p.contents.append(x)
    x.extract()
  if("".join([str(y) for y in p.contents]).strip()):
    root.contents.append(p)
  if(root.name=='p'):
    root.unwrap()
  soup=bs4.BeautifulSoup(str(soup),"lxml")
  br=soup.find('br')

print()
print("After:")
print(soup.prettify())
print("Re-parsed:")
print(bs4.BeautifulSoup(str(soup),"lxml").prettify())

关于python - 在Python中将<br/>转换为<p> </p>,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/48216573/

10-16 21:56