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

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

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 ]

3 голосов
/ 13 апреля 2019

Вы можете использовать псевдокласс nth-of-type, :not с общим комбинатором ~.Поскольку все теги a являются родными, я считаю, что в показанном html я использую теги b с типом nth для разделения тегов 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

3 голосов
/ 13 апреля 2019

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

Ваша проблема в том, что вы перебираете все теги 'a' без каких-либо шаблонных алгоритмов, каждые 3 ссылки, которые вы хотите объединить? Вы можете положить цикл внутрь тогда:

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]

чтение списков, строк, словарей

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...