Разобрать абзацы (<p>) между абзацами с определенными классами ( ). BeautifulSoup - PullRequest
1 голос
/ 03 февраля 2020

Я анализирую веб-страницу с кучей текста. Формат страницы следующий:

January

1

text text text

even more text

2

text text text

even more text

Я хочу разобрать эту страницу в список словарей, который будет выглядеть следующим образом:

[{'month': 'January', 'day': '1', 'text':'sample text for January 1st'},
 {'month': 'January', 'day': '2', 'text':'text for January 2nd'},
 {'month': 'January', 'day': '3', 'text':'January 3rd'},
  ...]

Это то, что html просмотр этой страницы:

index. html

<div id="january">
  <h2><span>January</span></h2>
  <p class="subtitle centerline">1</p>
  <p>sample text for January 1st</p>
  <p>even more sample text</p>

  <p class="subtitle centerline">2</p>
  <p>sample text for January 2nd</p>
  <p>different sample text</p>
  <p class="right"><em>John Smith</em></p>

  <p class="subtitle centerline">3</p>
  ...
</div>

Я успешно написал первую часть сценария синтаксического анализа, где я могу получить месяц и день.

scrape.py

data = []
day_dict = {}
months = ['january', 'february', ...]

for month in months:
    month_block = soup.find(id=month)
    month_name = month_block.find('h2').string

    days = []
    for i in month_block.find_all(class_="subtitle"):
      day = i.string.strip()

      day_dict['month'] = month_name 
      day_dict['day'] = day
      data.append(day_dict.copy())

В результате получается следующий список словарей:

[{'month': 'January', 'day': '1'},
 {'month': 'January', 'day': '2'},
 {'month': 'January', 'day': '3'},
  ...]

Поскольку тег

с образцом текст не child от даты, которую я могу указать, какие абзацы получить.

Вопрос

Есть ли способ получить только текст, который находится между двумя теги с тем же классом? Например (в псевдокоде):

for i in month_block.find_all(class_="subtitle"):
      day = i.string.strip()
      text =  month_block.find_all("p").after(day).before(day + 1) # new line in pseudo code 

      day_dict['month'] = month_name 
      day_dict['day'] = day
      day_dict['text'] = text # new line 
      data.append(day_dict.copy())

Извините за грубый псевдокод. Если вам нужны подробности или объяснения, пожалуйста, дайте мне знать.

Спасибо, что нашли время, чтобы прочитать это.

Ответы [ 2 ]

2 голосов
/ 04 февраля 2020

Я рекомендую другое решение, которое очень подходит для извлечения данных из XML.

from simplified_scrapy.spider import SimplifiedDoc 
html='''
<div id="january">
  <h2><span>January</span></h2>
  <p class="subtitle centerline">1</p>
  <p>sample text for January 1st</p>
  <p>even more sample text</p>

  <p class="subtitle centerline">2</p>
  <p>sample text for January 2nd</p>
  <p>different sample text</p>
  <p class="right"><em>John Smith</em></p>

  <p class="subtitle centerline">3</p>
  ...
</div>
'''
data = []
months = ['january', 'february']
doc = SimplifiedDoc(html) # create doc
for month in months:
    month_block = doc.select('#'+month)
    if not month_block: continue
    month_name = month_block.h2.text
    for i in month_block.selects(".subtitle"):
        day_dict = {"month":month_name,"day":i.text,"text":i.next.text}
        data.append(day_dict)

print (data)

Результат:

[{'month': 'January', 'day': '1', 'text': 'sample text for January 1st'}, {'month': 'January', 'day': '2', 'text': 'sample text for January 2nd'}, {'month': 'January', 'day': '3', 'text': None}]

Вот еще примеры: https://github.com/yiyedata/simplified-scrapy-demo/blob/master/doc_examples/

2 голосов
/ 03 февраля 2020

Попробуйте:

import bs4

calendar = {}

text = """<div id="january">
  <h2><span>January</span></h2>
  <p class="subtitle centerline">1</p>
  <p>sample text for January 1st</p>
  <p>even more sample text</p>

  <p class="subtitle centerline">2</p>
  <p>sample text for January 2nd</p>
  <p>different sample text</p>
  <p class="right"><em>John Smith</em></p>

  <p class="subtitle centerline">3</p>
</div>"""

soup = bs4.BeautifulSoup(text, "html.parser")

for month_div in soup.children:
    month = month_div.find('h2').string
    calendar[month] = {}

    for entry in month_div.find_all(class_="subtitle"):
        day = entry.string.strip()
        events = []

        s = entry
        while True:
            s = s.find_next_sibling()
            if s and "subtitle" not in s.attrs.get("class", []):
                events.append(s.string.strip())
            else:
                break

        calendar[month][day] = events

print(calendar)

Вывод:

{'January': {'1': ['sample text for January 1st', 'even more sample text'], '2': ['sample text for January 2nd', 'different sample text', 'John Smith'], '3': []}}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...