Вы не можете рассчитывать использовать BeautifulSoup для очистки абсолютно всего - не все веб-страницы одинаковы. В случае страницы, которую вы пытаетесь очистить, данные в таблице генерируются асинхронно через JavaScript. Вот примерная последовательность событий, которые происходят, когда вы посещаете свой URL-адрес в браузере:
- Ваш браузер делает начальный HTTP-запрос GET на URL-адрес веб-страницы.
- Сервер отвечает и обслуживает вас, что HTML документ
- Ваш браузер анализирует документ и делает гораздо больше асинхронных запросов к серверу (и, возможно, другим серверам) к ресурсам, которые ему необходимы для полной визуализации страницы, как это предназначено для быть увиденным человеческими глазами (шрифты, изображения и т. д. c.). На этом этапе браузер также делает запросы к API, который обслуживает JSON, чтобы он мог заполнить таблицу данными.
Ваш код в основном выполняет только первый шаг. Он делает запрос к документу HTML, который еще не был заполнен. Вот почему BeautifulSoup не может видеть данные, которые вы ищете. В общем, вы действительно можете использовать BeautifulSoup для очистки данных с веб-страниц, только если данная веб-страница содержит все данные, запеченные в документе HTML. Раньше это было чаще go, но я бы сказал, что в настоящее время большинство (современных) страниц заполняют DOM асинхронно, используя JavaScript.
Обычно именно здесь люди рекомендуют использовать Selenium или что-то еще. другой вид безголового браузера для полной имитации сеанса просмотра, но в вашем случае это излишне. Чтобы получить нужные данные, все, что вам нужно сделать, это сделать запросы к тому же API (тот, который я упоминал ранее на шаге 3), к которому ваш браузер обращается. Для этого вам даже не понадобится BeautifulSoup.
Если вы зарегистрируете свой сетевой трафик c, вы увидите, что ваш браузер делает несколько запросов к API, обслуживающему JSON. Вот пара ссылок, которые появляются. Go вперед и щелкните по ним, чтобы просмотреть структуру ответа 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: США / наблюдения / история. json? ApiKey = 6532d6454b8aa370768e63d6ba5a832e & units = e & startDate = 20190101 & endDate = 20190101
* еще 1026 * Есть Но Вы получаете идею. Запрос довольно прост: вы передаете ключ API и дату (или иногда дату начала и окончания) в качестве параметров строки запроса. Не уверен, что означает
units=e
.
Кроме того, этот API, похоже, не заботится о заголовках запросов, что приятно. Это не всегда так - некоторые API-интерфейсы отчаянно заботятся о всевозможных заголовках, таких как user-agent
, et c. Подделать тоже не составит большого труда, но я ценю простые 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
, которую на самом деле не использовал, я просто реализовал ее для удовольствия. Он извлекает данные об осадках с графика.