#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]
ознакомьтесь со списками, строками, словарями