我有4个嵌套的div标签,当我使用find_all打印文本时,它将打印文本4次

时间:2019-06-09 17:35:41

标签: python html beautifulsoup

我正在从包含很多div标签的html文件中提取文本。但是,在某些地方,有4个嵌套的div标签,当我打印文本时,它会打印4次。

<div>
    <div id="PGBRK" style="TEXT-INDENT: 0pt; WIDTH: 100%; MARGIN-LEFT: 0pt; MARGIN-RIGHT: 0pt">
        <div id="PN" style="PAGE-BREAK-AFTER: always; WIDTH: 100%">
            <div style="TEXT-ALIGN: center; WIDTH: 100%"><font style="DISPLAY: inline; FONT-FAMILY: Times New Roman; FONT-SIZE: 10pt">27</font></div> 
        </div>
    </div>
</div>

例如,在这里,如果我这样做:

for item in page_soup.find_all('div'):
    if "27" in item.text:
            print(item)

它将数字27打印四次,因此弄乱了整个文本。 如何获得我的代码仅打印嵌套文本一次?

编辑1: 这对于代码的这一部分效果很好。但是就像我说的那样,这仅在某些地方是正确的。例如,当我这样做时:

for item in page_soup.find_all('div', recursive = False):
    print(item)

它不打印任何内容。作为参考,this是我要抓取的文档。

编辑2: 我正在从给定的html中提取“ ITEM 1A。风险因素”部分。

should_print = False

for item in page_soup.find_all('div'):
    if "ITEM 1A." in item.text:
        should_print = True
    elif "ITEM 1B." in item.text:
        break
    if should_print:
        print(item)

因此,我正在打印从ITEM 1A开始的所有内容。直到找到ITEM 1B。 在某些地方,有嵌套的div标签,此代码多次将其打印出来。

如果我这样做,则递归= False,它不会打印任何内容。

5 个答案:

答案 0 :(得分:0)

您可以提供选项text = "27"来按文本搜索div并仅标识该确切的div。下面的代码应该可以正常工作。如果要获取所有div,则只需删除text = "27"或将其替换为要查找的文本。您也可以使用recursive = False仅获取顶级div。

编辑1:

from bs4 import BeautifulSoup

t = '''
<div>
27
</div>
<div>
<div id="PGBRK" style="TEXT-INDENT: 0pt; WIDTH: 100%; MARGIN-LEFT: 0pt; MARGIN-RIGHT: 0pt">

<div id="PN" style="PAGE-BREAK-AFTER: always; WIDTH: 100%">

<div style="TEXT-ALIGN: center; WIDTH: 100%"><font style="DISPLAY: inline; FONT-FAMILY: Times New Roman; FONT-SIZE: 10pt">27</font></div>

</div>
</div>
</div>
</div>
'''

page_soup = BeautifulSoup(t, 'html.parser')

for item in page_soup.find_all('div', text="27"):
    print(item.text)

编辑2:

我添加了一个特定代码,专门用于解决您的问题。试试下面的代码。您期望的div范围是从567 - 715中删除的页码。

import requests
from bs4 import BeautifulSoup

resp = requests.get(
    r'https://www.sec.gov/Archives/edgar/data/4904/000000490412000013/ye11aep10k.htm')
t = resp.text

page_soup = BeautifulSoup(t, 'html.parser')

s = 'body > div:not(#PGBRK)'

for i in page_soup.select(s)[567:715]:
   print(i.get_text(strip=True))

答案 1 :(得分:0)

这是一个选择

import bs4, re

html = '''<div>
<div id="PGBRK" style="TEXT-INDENT: 0pt; WIDTH: 100%; MARGIN-LEFT: 0pt; MARGIN-RIGHT: 0pt">

<div id="PN" style="PAGE-BREAK-AFTER: always; WIDTH: 100%">

<div style="TEXT-ALIGN: center; WIDTH: 100%"><font style="DISPLAY: inline; FONT-FAMILY: Times New Roman; FONT-SIZE: 10pt">27</font></div>

</div>
</div>
</div>
</div>'''

soup = bs4.BeautifulSoup(html,'html.parser')
elements = soup.find_all(text=re.compile('27'))
print(elements)

输出

[u'27']

答案 2 :(得分:0)

  

打印从ITEM 1A开始的所有内容。直到找到ITEM 1B

低谷.string属性(https://www.crummy.com/software/BeautifulSoup/bs4/doc/#string

import requests
from bs4 import BeautifulSoup

url = 'https://www.sec.gov/Archives/edgar/data/4904/000000490412000013/ye11aep10k.htm'
html_doc = requests.get(url).content
page_soup = BeautifulSoup(html_doc, 'html.parser')

do_print = False
for el in page_soup.find_all('div'):
    if el.string:
        if "ITEM 1A" in el.string:
            do_print = True
        elif "ITEM 1B" in el.string:
            break
    if do_print:
        print(el)

输出(我将显示不带中间部分的代表性开始和结束块,以进行简短的转储):

<div align="justify" style="TEXT-INDENT: 0pt; DISPLAY: block; MARGIN-LEFT: 0pt; MARGIN-RIGHT: 0pt"><font style="DISPLAY: inline; FONT-FAMILY: Times New Roman; FONT-SIZE: 12pt; FONT-WEIGHT: bold"><font style="DISPLAY: inline; TEXT-DECORATION: underline">ITEM 1A.   RISK FACTORS</font></font></div>
<div style="TEXT-INDENT: 0pt; DISPLAY: block"><br/>
</div>
<div align="justify" style="TEXT-INDENT: 0pt; DISPLAY: block; MARGIN-LEFT: 0pt; MARGIN-RIGHT: 0pt"><font style="DISPLAY: inline; FONT-FAMILY: Times New Roman; FONT-SIZE: 12pt; FONT-WEIGHT: bold">GENERAL RISKS OF OUR REGULATED OPERATIONS</font></div>
<div style="TEXT-INDENT: 0pt; DISPLAY: block">
<div align="justify" style="TEXT-INDENT: 0pt; DISPLAY: block; MARGIN-LEFT: 0pt; MARGIN-RIGHT: 0pt"><font style="FONT-STYLE: italic; DISPLAY: inline; FONT-FAMILY: Times New Roman; FONT-SIZE: 12pt; FONT-WEIGHT: bold"> </font></div>
<div align="justify" style="TEXT-INDENT: 0pt; DISPLAY: block; MARGIN-LEFT: 0pt; MARGIN-RIGHT: 0pt"><font style="FONT-STYLE: italic; DISPLAY: inline; FONT-FAMILY: Times New Roman; FONT-SIZE: 12pt; FONT-WEIGHT: bold">The regulatory environment in Ohio has recently become unpredictable and increasingly uncertain. – Affecting AEP and OPCo</font></div>
<div style="TEXT-INDENT: 0pt; DISPLAY: block"><br/>
.....
<div style="TEXT-ALIGN: center; WIDTH: 100%"><font style="DISPLAY: inline; FONT-FAMILY: Times New Roman; FONT-SIZE: 10pt">37</font></div>
<div style="TEXT-ALIGN: center; WIDTH: 100%">
<hr noshade="" size="2" style="COLOR: black"/>
</div>
<div id="HDR">
<div align="right" id="GLHDR" style="WIDTH: 100%"><font style="DISPLAY: inline; FONT-FAMILY: Times New Roman; FONT-SIZE: 8pt">  </font></div>
</div>
<div align="right" id="GLHDR" style="WIDTH: 100%"><font style="DISPLAY: inline; FONT-FAMILY: Times New Roman; FONT-SIZE: 8pt">  </font></div>
<div style="TEXT-INDENT: 0pt; DISPLAY: block"> </div>

答案 3 :(得分:0)

好吧,我认为这是一个很酷的问题,如果您想对它进行概括以找出每个级别上存在哪些文本,而又不求助于像27这样的特定数字,那么我看不到一个简单的答案。Beautiful Soup似乎没有只在顶部显示文本的功能,而recursive = False只是阻止搜索深入到第一级以下,但仍将第一级以下的所有内容都包括在内,因此,如果在顶级标签,它将捕获它及其下面的所有内容

因此,我认为您实际上必须递归divs树并在每个级别上比较文本。我明白了。在递归中冒泡时,它以相反的顺序打印,但可以存储在列表中并以向前的顺序输出。

from bs4 import BeautifulSoup
soup = BeautifulSoup('<div>1A<div>2A</div>1B<div>2B<div>3A</div><div>3A</div>2C</div>1C</div>', 'html.parser')

def mangle(node):
    divs = node.find_all('div')
    if len(divs):
        result = [divs[0]] + [n for n in divs[0].next_siblings if n.__class__.__name__ == 'Tag']
        txt = []
        for r in result:
            txt.append(r.__repr__())
            for c in mangle(r):
                txt[-1] = txt[-1].replace(c.__repr__(), '')

        print(''.join(BeautifulSoup(t, 'html.parser').text for t in txt))
        return result
    else:
        return []    

if __name__ == '__main__':
    mangle(soup)

基本上,它沿着div的分支移动,并在树的每个分支上建立列表,包括标签,然后调用者删除其下找到的所有内容,仅保留在该级别定义的文本。我将标签保留在适当的位置,以便不会误删除出现在多个级别的文本模式。

html 1A2A1B2B3A3A2C1C的输出为

3A3A
2A2B2C
1A1B1C

分别是第三,第二和第一嵌套级别。希望这会有所帮助。

答案 4 :(得分:0)

自从我终于开始工作以来,我将回答我自己的问题。

解决方案很简单,我只是觉得太难了。 我只是添加了条件,即该项目的父项不应该是“ div”。现在,该程序不会多次打印文本。

should_print = False

for item in page_soup.find_all('div'):
    if item.name == "div" and item.parent.name != "div"
        if "ITEM 1A." in item.text:
            should_print = True
        elif "ITEM 1B." in item.text:
            break
        if should_print:
            print(item)

感谢大家的贡献。感激...