Как мне использовать Python / Beautiful Soup для извлечения текста между двумя разными тегами?

#python #beautifulsoup

#python #beautifulsoup

Вопрос:

Я пытаюсь извлечь заголовки ссылок между двумя выделенными жирным шрифтом тегами на HTML-странице, используя Python / Beautiful Soup.

Фрагмент HTML того, что я пытаюсь извлечь, выглядит следующим образом:

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

amp;nbsp;

<B>Heading Title 2:</B>amp;nbsp;<a href="link3">Title3</a>amp;nbsp;
<a href="link4">Title4</a>amp;nbsp;
<a href="link5">Title5</a>amp;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 и программирования в целом и не знаю, как поступить. Я был бы признателен за любые отзывы, которые кто-либо может иметь по этому поводу.

Спасибо!

Ответ №1:

Вы могли бы использовать nth-of-type :not псевдокласс с общим ~ комбинатором. Поскольку все a теги являются братьями и сестрами, я полагаю, что в показанном html я использую b теги с n-м типом, чтобы разделить a теги между блоками. Я использую :not для удаления более поздних a братьев и сестер из текущего.

 from bs4 import BeautifulSoup as bs

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

amp;nbsp;

<B>Heading Title 2:</B>amp;nbsp;<a href="link3">Title3</a>amp;nbsp;
<a href="link4">Title4</a>amp;nbsp;
<a href="link5">Title5</a>amp;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)
 

вывод:

введите описание изображения здесь

Ответ №2:

Вы можете использовать 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 группы и извлекая текст из ссылок.

Комментарии:

1. Не могли бы вы немного объяснить, как это работает, пожалуйста?

2. @QHarr Исходный find_all вызов работает как «выравниватель» и создает список списков с именами и содержимым целевых тегов. itertools.groupby имеет key группы, основанные на том, является ли имя тега для жирного содержимого. Таким образом, можно выполнить последний проход new_d , игнорируя b группы и извлекая текст из ссылок.

3. @QHarr Спасибо, я обновил сообщение с объяснением.

Ответ №3:

ваша проблема заключается в том, что вы перебираете все теги «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]
 

ознакомьтесь со списками, строками, словарями