Получение содержимого супа в структурированный CSV - PullRequest
2 голосов
/ 28 марта 2020

Я пытаюсь очистить сайт и поместить его в структурированный формат данных. Я хотел бы в конечном итоге. CSV с 6 столбцами: страна, дата, general_text, fiscal_text, денежный_текст, fx_text.

Отображение выглядит следующим образом:

country <- h3
date <- h6
general_text <- p (h3) (the p tag that follows the h3 header)
fiscal_text  <- p (1st h5 ul li) (the p tag that follows the **first** h5. This tag is inside ul and li blocks)
monetary_text <- p (2nd h5 ul li) (the p tag that follows the **second** h5. This tag is inside ul and li blocks)
fx_text <- p (3rd h5 ul li) (the p tag that follows the **third** h5. This tag is inside ul and li blocks)

Шаблон заканчивается на следующем заголовке h3 (страна).

Мне трудно получить каждый элемент в его правильное место / столбец.

Структура сайта повторяет это для каждой страны (фактические теги см. ниже):

h3
 p
h6
h5
 ul
  li
   p
h5
 ul
  li
   p
h5
 ul
  li
   p

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

import requests
import io
import csv 
from bs4 import BeautifulSoup
from urllib.request import urlopen
URL = 'https://www.imf.org/en/Topics/imf-and-covid19/Policy-Responses-to-COVID-19'
page = requests.get(URL)
soup = BeautifulSoup(page.content, 'html.parser')

results = soup.find(class_='rr-intro')

with io.open('test.txt', 'w', encoding='utf8') as f:
    for header in results.find_all(['h3', 'h6', 'h5']):
        f.write(header.get_text() + u'\n') 
        for elem in header.next_siblings:
            if elem.name and elem.name.startswith('h'):
                # stop at next header
                break
            if elem.name and elem.find_all('p'):
                f.write(elem.get_text() + u'\n')

Из комментариев я подумал, что имеет смысл вместо этого создавать списки и каким-то образом архивировать их. Я попробовал это:

h3 = results.find_all('h3')
h6 = results.find_all('h6')
h5 = results.find_all('h5')
h5f = results.find_all('h5', text='Fiscal')
h5m = results.find_all('h5', text='Monetary and macro-financial')
h5x = results.find_all('h5', text='Exchange rate and balance of payments')
country = [country.get_text() for country in h3]  #list of countries
date = [date.get_text() for date in h6]  #date string

Я застрял здесь. Не уверен, как получить содержимое p-тегов в go в нужном месте списка, чтобы оно было заархивировано, или напрямую в CSV.

Я python ладья ie, поэтому я сделал это на основе того, что я нашел в stackoverflow. Любая помощь будет принята с благодарностью.

Редактировать: Чтобы уточнить, структура того, что я хочу, выглядит следующим образом.

<div class="rr-intro">

 <h3>
  Country 1
 </h3>
 <p>
  summary text
 </p>
 <h6>
  date
 </h6>
 <h5>
  Fiscal
 </h5>
 <ul>
  <li>
   <p>
    text for fiscal of country 1
   </p>
  </li>
 </ul>
 <h5>
  Monetary and macro-financial
 </h5>
 <ul>
  <li>
   <p>
    text for monetary of country 1
   </p>
  </li>
 </ul>
 <h5>
  Exchange rate and balance of payments
 </h5>
 <ul>
  <li>
   <p>
    text for FX of country 1
   </p>
  </li>
 </ul>
  <h3>
  Country 2
 </h3>
 <p>
  summary text
 </p>
 <h6>
  date
 </h6>
 <h5>
  Fiscal
 </h5>
 <ul>
  <li>
   <p>
    text for fiscal of country 2
   </p>
  </li>
 </ul>
 <h5>
  Monetary and macro-financial
 </h5>
 <ul>
  <li>
   <p>
    text for monetary of country 2
   </p>
  </li>
 </ul>
 <h5>
  Exchange rate and balance of payments
 </h5>
 <ul>
  <li>
   <p>
    text for FX of country 2
   </p>
  </li>
 </ul>
   <h3>
  Country 3
 </h3> 

et c ...

1 Ответ

1 голос
/ 01 апреля 2020

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

A Python CSV DictWriter можно использовать для записи строки информации, когда следующая страна h3 нашел. Например:

from collections import defaultdict
import requests
import csv 
from bs4 import BeautifulSoup

URL = 'https://www.imf.org/en/Topics/imf-and-covid19/Policy-Responses-to-COVID-19'
page = requests.get(URL)
soup = BeautifulSoup(page.content, 'html.parser')

results = soup.find('div', class_='rr-intro')

section_lookup = {
    'Fiscal' : 'fiscal_text',
    'Moneta' : 'monetary_text',
    'Macro-' : 'monetary_text',
    'Exchan' : 'fx_text',
}

with open('data.csv', 'w', encoding='utf8', newline='') as f_output:
    fieldnames = ['country', 'date', 'general_text', 'fiscal_text', 'monetary_text', 'fx_text']
    csv_output = csv.DictWriter(f_output, fieldnames=fieldnames)
    csv_output.writeheader()

    row = defaultdict(str)
    section = None

    for elem in results.find_all(['h3', 'h6', 'h5', 'p']):
        if elem.name == 'h3':
            if row:
                csv_output.writerow(row)
                row = defaultdict(str)

            row['country'] = elem.get_text(strip=True)
            section = "general_text"

        elif elem.name == 'h5':
            section = section_lookup[elem.get_text(strip=True)[:6]]
        elif elem.name == 'h6':
            row['date'] = elem.get_text(strip=True)[27:]
        elif elem.name == 'p' and section:
            row[section] = f"{row[section]} {elem.get_text(strip=True)}"

    if row:
        csv_output.writerow(row)

Предоставление вам файла data.csv, начиная с:

Excel screenshot showing first two rows

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