Создание веб-сайта с использованием BeautifulSoup в Dataframe

#html #python-3.x #dataframe #web-scraping #beautifulsoup

#HTML #python-3.x #фрейм данных #очистка веб-сайта #beautifulsoup

Вопрос:

Это html-код:

     <div class="wp-block-atomic-blocks-ab-accordion ab-block-accordion ab-font-size-18"><details><summary class="ab-accordion-title"><strong>American Samoa</strong></summary><div class="ab-accordion-text">
    <ul><li><strong><a href="https://www.americansamoa.gov/covid-19-advisories" target="_blank" rel="noreferrer noopener" aria-label="American Samoa Department of Health Travel Advisory (opens in a new tab)">American Samoa Department of Health Travel Advisory</a></strong></li><li>March 2, 2020—Governoramp;nbsp;Moligaamp;nbsp;<a rel="noreferrer noopener" href="https://www.rnz.co.nz/international/pacific-news/410783/american-samoa-establishes-govt-taskforce-to-plan-for-coronavirus" target="_blank">appointed</a>amp;nbsp;a government taskforceamp;nbsp;to provide a plan for preparation and response to the covid-19 coronavirus.amp;nbsp;</li></ul>
    
    <ul><li>March 25, 2020 – The Governor <a href="https://6fe16cc8-c42f-411f-9950-4abb1763c703.filesusr.com/ugd/4bfff9_2d3c78a841824b8aafe05032f853585b.pdf">issued</a> an Executive Order 001 recognizing the Declared Public Health Emergency and State of Emergency, and imminent threat to public health. The order requires the immediate and comprehensive enforcement by the Commissioner of Public Safety, Director of Health, Attorney General, and other agency leaders.
    <ul>
    <li>Business are also required to provide necessary supplies to the public and are prohibited from price gouging.</li>
    </ul>
    </li></ul>
</div></details></div>
  

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

Штат: Американское Самоа
Дата: 2020-03-25
Текст: Распоряжение губернатора 001 о признании объявленной чрезвычайной ситуации в области общественного здравоохранения и чрезвычайного положения и непосредственной угрозы общественному здоровью

Мой код пока:

 soup = bs4.BeautifulSoup(data)
for tag in soup.find_all("summary"):
    print("{0}: {1}".format(tag.name, tag.text))
    for tag1 in soup.find_all("li"):
        #print(type(tag1))
        ln = tag1.text
        dt = (ln.split(' – ')[0])
        dt = (dt.split('—')[0])
        #txt = ln.split(' – ')[1]
        print(dt)
  

Нужна помощь:

  1. Как получить текст до «.» только мне не нужен весь тест целиком
  2. Как добавить в dataframe новую строку по мере прохождения цикла (я прикрепил только часть исходного кода веб-страницы)

Ценю вашу помощь!

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

1. Можете ли вы проверить свой html на точность? Например, где делать <details> и <div class="ab-accordion-text"> закрыть?

2. <div class=»ab-accordion-text»> нигде не закрывается, и я обновил HTML-код для <подробности>

3. Хорошо. И если существует более одного элемента, отображается ли он под <details> как другой <summary> и т.д. Или как другой набор <details> ?

4. да, это огромная веб-страница, с которой я читаю, начинается другой раздел -<div class=»wp-block-atomic-blocks-ab-accordion ab-block-accordion ab-font-size-18″><подробности><summary class=»ab-accordion-title»><strong> Калифорния</strong></summary><div class=»ab-accordion-text»> <p><a rel=»noreferrer noopener «aria-label=» (открывается в новой вкладке)» href=» covid19.ca.gov » target=»_blank»><strong> Страница ресурса о коронавирусе в Калифорнии.</strong></a></p>

5. Можете ли вы опубликовать URL страницы, которую хотите очистить?

Ответ №1:

Для начала я добавил приведенный ниже код. К сожалению, веб-страница неоднородна в использовании HTML-списков, некоторые ul элементы содержат вложенные ul , другие — нет. Этот код не идеален, но является отправной точкой, например, American Samoa имеет абсолютный беспорядок вложенных ul элементов, поэтому появляется только один раз в df .

 from bs4 import BeautifulSoup
import requests
import re
import pandas as pd

HEADERS = {
    'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:77.0) Gecko/20100101 Firefox/77.0',
}
# You need to specify User Agent headers or else you get a 403
data = requests.get("https://www.nga.org/coronavirus-state-actions-all/", headers=HEADERS).text
soup = BeautifulSoup(data, 'lxml')
rows_list = []
for detail in soup.find_all("details"):
    state = detail.find('summary')
    ul = detail.find('ul')
    for li in ul.find_all('li', recursive=False):
        # Three types of hyphen are used on this webpage
        split = re.split('(?:-|–|—)', li.text, 1)
        if len(split) == 2:
            rows_list.append([state.text, split[0], split[1]])
        else:
            print("Error", li.text)
df = pd.DataFrame(rows_list)
with pd.option_context('display.max_rows', None, 'display.max_columns', None, 'display.max_colwidth', -1):
    print(df)
  

Он создает и печатает фрейм данных с 547 строками и печатает некоторые сообщения об ошибках для текста, который он не может разделить. Вам нужно будет точно определить, какие данные вам нужны и как настроить код в соответствии с вашими целями.

Вы можете использовать ‘html.parser’, если у вас не установлен ‘lxml’.

ОБНОВЛЕНО Другой подход заключается в использовании регулярных выражений для сопоставления любой строки, начинающейся с даты:

 from bs4 import BeautifulSoup
import requests
import re
import pandas as pd

HEADERS = {
    'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:77.0) Gecko/20100101 Firefox/77.0',
}
# You need to specify User Agent headers or else you get a 403
data = requests.get("https://www.nga.org/coronavirus-state-actions-all/", headers=HEADERS).text
soup = BeautifulSoup(data, 'html.parser')
rows_list = []
for detail in soup.find_all("details"):
    state = detail.find('summary')
    for li in detail.find_all('li'):
        p = re.compile(r'(s*(Jan(?:uary)?|Feb(?:ruary)?|Mar(?:ch)?|Apr(?:il)?|May|Jun(?:e)?|Jul(?:y)?|Aug(?:ust)?|Sep(?:tember)?|Oct(?:ober)?|Nov(?:ember)?|Dec(?:ember)?)s*(d{1,2}),*s*(d{4}))', re.IGNORECASE)
        m = re.match(p, li.text)
        if m:
            rows_list.append([state.text, m.group(0), m.string.replace(m.group(0), '')])
        else:
            print("Error", li.text)
df = pd.DataFrame(rows_list)
df.to_csv('out.csv')
  

это дает гораздо больше записей, 4785. Опять же, это отправная точка, некоторые данные пропускаются, но гораздо реже. Он записывает данные в csv-файл out.csv.