如何使用Python / Beautiful Soup在两个不同标签之间提取文本?

时间:2019-04-13 02:01:03

标签: python beautifulsoup

我正在尝试使用Python / Beautiful Soup在HTML页面上的两个加粗标签之间提取链接标题。

我要提取的内容的HTML代码段如下:

<B>Heading Title 1:</B>&nbsp;<a href="link1">Title1</a>&nbsp;
<a href="link2">Title2</a>&nbsp;

&nbsp;

<B>Heading Title 2:</B>&nbsp;<a href="link3">Title3</a>&nbsp;
<a href="link4">Title4</a>&nbsp;
<a href="link5">Title5</a>&nbsp;

...

我特别希望将Title1和Title2(由定界符分隔)连接到类似列表的对象中的一个条目,同样对于Title 3,Title 4和Title 5,依此类推。 (我预见的一个问题是每个标题之间的标题数量设置不相同。)

我尝试了各种方法,包括:

import requests, bs4, csv

res = requests.get('WEBSITE.html')

soup = BeautifulSoup(res.text, 'html.parser')

soupy4 = soup.select('a')

with open('output.csv', 'w') as f:
    writer = csv.writer(f, delimiter=',', lineterminator='\n')
    for line in soupy4:
        if 'common_element_link' in line['href']:
            categories.append(line.next_element)
            writer.writerow([categories])

但是,虽然这会将所有标题写入文件,但它是通过直接附加每个其他标题来实现的,如下所示:

['Title1']
['Title1', 'Title2']
['Title1', 'Title2', 'Title3']
['Title1', 'Title2', 'Title3', 'Title4']
...

理想情况下,我希望此代码执行以下操作:

['Title1', 'Title2']
['Title3', 'Title4', 'Title5']
...

关于python列表和一般程序设计,我是一个新手,对如何进行操作一无所知。任何人可能对此都有任何反馈,我将不胜感激。

谢谢!

3 个答案:

答案 0 :(得分:3)

您可以将nth-of-type:not伪类与同级~组合器一起使用。由于a标签都是兄弟姐妹,因此,在所示的html中,我相信我使用类型为nth的b标签将a标签分成多个块。我使用:not从当前版本中删除以后的a个兄弟姐妹。

from bs4 import BeautifulSoup as bs

html = '''
<B>Heading Title 1:</B>&nbsp;<a href="link1">Title1</a>&nbsp;
<a href="link2">Title2</a>&nbsp;

&nbsp;

<B>Heading Title 2:</B>&nbsp;<a href="link3">Title3</a>&nbsp;
<a href="link4">Title4</a>&nbsp;
<a href="link5">Title5</a>&nbsp;
'''
soup = bs(html, 'lxml')
items = soup.select('b:has(~a)')
length = len(items)
if length == 1:
    row = [item.text for item in soup.select('b ~ a')]
    print(row)
elif length > 1:
    for i in range(1, length + 1):
        row = [item.text for item in soup.select('b:nth-of-type(' + str(i) + ') ~ a:not(b:nth-of-type(' + str(i + 1) + ') ~ a)')]
        print(row)

输出:

enter image description here

答案 1 :(得分:3)

您可以使用itertools.groupby组合标题之间的所有链接文本:

import itertools, re
from bs4 import BeautifulSoup as soup
d = [[i.name, i] for i in soup(content, 'html.parser').find_all(re.compile('b|a'))]
new_d = [[a, list(b)] for a, b in itertools.groupby(d, key=lambda x:x[0] == 'b')]
final_result = [[c.text for _, c in b] for a, b in new_d if not a]

输出:

[['Title1', 'Title2'], ['Title3', 'Title4', 'Title5']]

原始的find_all调用充当“扁平化器”,并创建带有目标标记名称和内容的列表列表。 itertools.groupby有一个根据标签名称是否用于粗体内容进行分组的键。因此,可以对new_d进行最后一遍处理,而忽略b组,并从链接中提取文本。

答案 2 :(得分:2)

您的问题是您遍历所有'a'标签而没有任何模式算法,是您要连接的每3个链接吗?您可以在其中放入for循环:

for line in alllinks:
    maintitle=''
    for i in xrange(3):
       maintitle+=line.text
    mainlist.append(maintitle)

查找父块,然后遍历嵌套子对象

sp=sp.find('div',id='whatever')
a=sp.select('a')  (this is recursive, finds all a tags in that div)
for tag in a:
    title=a.text.strip()
    url=a['href']

我建议寻找要分组在一起的“链接”的父html标签,而不要按照所有链接的顺序随意进行

p.s。您也可以使find()具有吸引力,尽管不建议这样做 通过使用recursive = True选项

将字符串添加在一起:str3 = str1 + str2

llist=[]
for z in zrange(10)
   llist.append('bob'+str(z))

每个列表项都有一个索引

print llist[1]

阅读列表,字符串,词典