Arhn - архитектура программирования

Отсутствует тег Python BeautifulSoup, который, как мне кажется, явно присутствует

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

Я ищу фактическое количество осадков в сводной таблице с этого сайта. В приведенной ниже ссылке его значение равно 0,01. В конечном итоге я буду перебирать этот сайт, вставляя разные дни в URL-адрес для каждого дня года, что дает мне ежедневное количество осадков в Хьюстоне на каждый день 2019 года.

https://www.wunderground.com/history/daily/KHOU/date/2019-01-01

это изображение веб-сайта и html, чтобы было более понятно, что я хочу найти

код ниже:

result = requests.get('https://www.wunderground.com/history/daily/KHOU/date/2019-01-01')
content = result.content
soup= BeautifulSoup(content, 'html.parser')  #BS4 Documentation states this is 


test1 = soup.find_all('div', {'class':'summary-table'})
len(test1)  #1
print(test1[0].prettify())
#<div _ngcontent-sc235="" class="summary-table">
# No data recorded
# <!-- -->
# <!-- -->
#</div>

#I also tried just finding the tbody tags directly
test2 = soup.find_all('tbody')
len(test2)  #1
print(test2[0].prettify())
#<tbody _ngcontent-sc225="">
# <!-- -->
#</tbody>

Ожидаемый результат:

Тест1: я ожидал получить список тегов div с классом = 'summary-table' (я почти уверен, что есть только один из них). Я ожидал, что это также будет содержать все теги div и теги tbody/thead, которые были внутри него.

Test2: я ожидал, что это будет список всех тегов tbody, чтобы я мог перебирать их, чтобы найти то, что мне нужно.

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


  • сайт построен на javascript, попробуйте использовать селен 06.08.2020

Ответы:


1

Вы не можете ожидать, что будете использовать BeautifulSoup для парсинга абсолютно всего — не все веб-страницы одинаковы. В случае со страницей, которую вы пытаетесь очистить, данные в таблице генерируются асинхронно с помощью JavaScript. Вот грубая последовательность событий, которые происходят, когда вы посещаете свой URL-адрес в браузере:

  1. Ваш браузер отправляет начальный HTTP-запрос GET к URL-адресу веб-страницы.
  2. Сервер отвечает и предоставляет вам этот HTML-документ
  3. Ваш браузер анализирует документ и делает гораздо больше асинхронных запросов к серверу (и, возможно, к другим серверам) к ресурсам, которые ему необходимы для полного отображения страницы в том виде, в котором она предназначена для человеческого глаза (шрифты, изображения и т. д.). На этом этапе браузер также отправляет запросы к API, который обслуживает JSON, чтобы он мог заполнить таблицу данными.

Ваш код в основном делает только первый шаг. Он делает запрос к пустому HTML-документу, который еще не заполнен. Вот почему BeautifulSoup не может видеть данные, которые вы ищете. В общем, вы действительно можете использовать BeautifulSoup для извлечения данных с веб-страниц только в том случае, если на данной веб-странице все данные запечены в HTML-документе. Раньше это было более распространено несколько лет назад, но я бы сказал, что в настоящее время большинство (современных) страниц заполняют DOM асинхронно с использованием JavaScript.

Обычно люди рекомендуют вам использовать Selenium или какой-либо другой безголовый браузер для полной имитации сеанса просмотра, но в вашем случае это излишне. Чтобы получить нужные данные, все, что вам нужно сделать, это отправить запросы к тому же API (тому, о котором я упоминал ранее на третьем шаге), к которому обращается ваш браузер. Для этого вам даже не нужен BeautifulSoup.

Если вы зарегистрируете свой сетевой трафик, вы увидите, что ваш браузер делает несколько запросов к API, обслуживающему JSON. Вот пара ссылок, которые появляются. Нажмите на них, чтобы просмотреть структуру ответа JSON:

https://api.weather.com/v1/location/KHOU:9:US/almanac/daily.json?apiKey=6532d6454b8aa370768e63d6ba5a832e&units=e&start=0101

https: //api.weather.com/v1/location/KHOU:9:US/observations/historical.json?apiKey=6532d6454b8aa370768e63d6ba5a832e&units=e&startDate=20190101&endDate=20190101

Есть еще, но вы поняли идею. Запрос довольно прост, вы передаете ключ API и дату (или иногда дату начала и окончания) в качестве параметров строки запроса. Не знаю, что означает units=e.

Кроме того, этот API, похоже, не заботится о заголовках запросов, что приятно. Это не всегда так — некоторые API-интерфейсы отчаянно заботятся о всевозможных заголовках, таких как user-agent и т. д. Подделать их тоже было бы несложно, но я ценю простые API-интерфейсы.

Вот какой код я придумал:

def get_api_key():

    import requests
    import re

    url = "https://www.wunderground.com/history/daily/KHOU/date/2019-01-01"

    response = requests.get(url)
    response.raise_for_status()

    pattern = "SUN_API_KEY&q;:&q;(?P<api_key>[^&]+)"
    return re.search(pattern, response.text).group("api_key")

def get_average_precip(api_key, date):

    import requests

    url = "https://api.weather.com/v1/location/KHOU:9:US/almanac/daily.json"

    params = {
        "apiKey": api_key,
        "units": "e",
        "start": date.strftime("%m%d")
    }

    response = requests.get(url, params=params)
    response.raise_for_status()

    return response.json()["almanac_summaries"][0]["avg_precip"]

def get_total_precip(api_key, start_date, end_date):

    import requests

    url = "https://api.weather.com/v1/location/KHOU:9:US/observations/historical.json"

    params = {
        "apiKey": api_key,
        "units": "e",
        "startDate": start_date.strftime("%Y%m%d"),
        "endDate": end_date.strftime("%Y%m%d")
    }

    response = requests.get(url, params=params)
    response.raise_for_status()

    return next(obv["precip_total"] for obv in response.json()["observations"] if obv["precip_total"] is not None)

def get_hourly_precip(api_key, start_date, end_date):

    import requests

    url = "https://api.weather.com/v1/location/KHOU:9:US/observations/historical.json"

    params = {
        "apiKey": api_key,
        "units": "e",
        "startDate": start_date.strftime("%Y%m%d"),
        "endDate": end_date.strftime("%Y%m%d")
    }

    response = requests.get(url, params=params)
    response.raise_for_status()

    for observation in response.json()["observations"]:
        yield observation["precip_hrly"]

def main():

    import datetime

    api_key = get_api_key()

    # January 3rd, 2019
    date = datetime.date(2019, 1, 3)
    avg_precip = get_average_precip(api_key, date)

    start_date = date
    end_date = date
    total_precip = get_total_precip(api_key, start_date, end_date)

    print(f"The average precip. is {avg_precip}")
    print(f"The total precip between {start_date.isoformat()} and {end_date.isoformat()} was {total_precip:.2f} inches")

    return 0


if __name__ == "__main__":
    import sys
    sys.exit(main())

Выход:

The average precip. is 0.12
The total precip between 2019-01-03 and 2019-01-03 was 1.46 inches
>>> 

Я также определил функцию get_hourly_precip, которую я на самом деле не использовал, я просто реализовал ее для удовольствия. Он извлекает данные об осадках из графика.

06.08.2020
  • Хорошее усилие, но вам не следует жестко кодировать apikey AFAIK 06.08.2020
  • @bigbounty Достаточно честно. Я добавил функцию get_api_key, которая использует регулярное выражение для извлечения ключа API из HTML-документа. Любая другая функция теперь принимает параметр api_key. 06.08.2020
  • вау, это потрясающий ответ, спасибо! Я очень ценю время, которое вы потратили на это. 12.08.2020
  • @aaronrums Добро пожаловать! Дайте мне знать, если у вас есть другие вопросы. 12.08.2020
  • Новые материалы

    Коллекции публикаций по глубокому обучению
    Последние пару месяцев я создавал коллекции последних академических публикаций по различным подполям глубокого обучения в моем блоге https://amundtveit.com - эта публикация дает обзор 25..

    Представляем: Pepita
    Фреймворк JavaScript с открытым исходным кодом Я знаю, что недостатка в фреймворках JavaScript нет. Но я просто не мог остановиться. Я хотел написать что-то сам, со своими собственными..

    Советы по коду Laravel #2
    1-) Найти // You can specify the columns you need // in when you use the find method on a model User::find(‘id’, [‘email’,’name’]); // You can increment or decrement // a field in..

    Работа с временными рядами спутниковых изображений, часть 3 (аналитика данных)
    Анализ временных рядов спутниковых изображений для данных наблюдений за большой Землей (arXiv) Автор: Рольф Симоэс , Жильберто Камара , Жильберто Кейрос , Фелипе Соуза , Педро Р. Андраде ,..

    3 способа решить квадратное уравнение (3-й мой любимый) -
    1. Методом факторизации — 2. Используя квадратичную формулу — 3. Заполнив квадрат — Давайте поймем это, решив это простое уравнение: Мы пытаемся сделать LHS,..

    Создание VR-миров с A-Frame
    Виртуальная реальность (и дополненная реальность) стали главными модными терминами в образовательных технологиях. С недорогими VR-гарнитурами, такими как Google Cardboard , и использованием..

    Демистификация рекурсии
    КОДЕКС Демистификация рекурсии Упрощенная концепция ошеломляющей О чем весь этот шум? Рекурсия, кажется, единственная тема, от которой у каждого начинающего студента-информатика..