Skip to content

Instantly share code, notes, and snippets.

@AO8
Last active August 11, 2020 04:57
Show Gist options
  • Save AO8/085f396e3918796edce7be407dd4a3ad to your computer and use it in GitHub Desktop.
Save AO8/085f396e3918796edce7be407dd4a3ad to your computer and use it in GitHub Desktop.
Daily Coronavirus data update (global and WA state) with Python 3, requests, and beautifulsoup.
# Check out https://ncov2019.live/data by Avi Schiffmann
# as featured in the Seattle Times at:
# https://www.seattletimes.com/seattle-news/education/qa-avi-schiffmann-the-washington-state-teen-behind-a-coronavirus-website-with-millions-of-views/
from email.mime.text import MIMEText
import smtplib
import ssl
import sys
import stdiomask
from bs4 import BeautifulSoup
import requests
URL = "https://ncov2019.live/data"
def main():
"""Make call to site, extract and transform date, then email through Gmail."""
resp = get_response(URL)
soup = create_soup(resp)
data = get_data_from_soup(soup)
cases = int(get_cases_from_data(data).replace(",", ""))
deaths = int(get_deaths_from_data(data).replace(",", ""))
recovered = int(get_recovered_from_data(data).replace(",", ""))
wa_data = get_wa_data_from_soup(soup)
wa_cases = int(get_cases_from_wa_data(wa_data).replace(",", ""))
wa_deaths = int(get_deaths_from_wa_data(wa_data).replace(",", ""))
body = f"""Your daily Coronavirus update.
GLOBAL
Confirmed cases:
{cases:,}
Total deaths:
{deaths:,}
Mortality rate:
{deaths/cases:.1%}
Total recovered:
{recovered:,}
WASHINGTON STATE
Confirmed cases:
{wa_cases:,}
Total deaths:
{wa_deaths}
Source: ncov2019.live/data (as featured in the Seattle Times)"""
sender = "your_gmail_address"
password = stdiomask.getpass(mask="*")
receiver = "#your_to_address"
send_gmail(sender, password, receiver, "Coronavirus update", body)
def send_gmail(sender, password, receiver, subject, body):
"""Function for sending a nicely-formatted email through Gmail."""
msg = MIMEText(body)
msg["Subject"] = subject
msg["From"] = sender
msg["To"] = receiver
# create a secure SSL context
context = ssl.create_default_context()
# 'with' context manager closes connection at end of indented code block
with smtplib.SMTP_SSL("smtp.gmail.com", 465, context=context) as s:
s.login(sender, password)
s.sendmail(sender, receiver, msg.as_string())
print("Email sent!")
def get_response(url):
"""Make a request to supplied url."""
try:
resp = requests.get(url)
print(resp.status_code)
except requests.exceptions.RequestException as e:
print("There was a problem getting a response from the url provided:\n\n{e}")
sys.exit()
return resp
def create_soup(resp):
"""Convert response from get_response() to a soup object."""
html = resp.text
soup = BeautifulSoup(html, "html.parser")
return soup
def get_data_from_soup(soup):
"""Get data from soup object and return a list of p's.
The p tags in col-12 collects key Coronavirus stats and is
specific to https://ncov2019.live/data.
"""
data_div = soup.find_all(class_="container--wrap")[0]
data = data_div.find_all("p")
return data
def get_cases_from_data(data):
"""Get total confirmed cases from data, a list.
Item at index 2 corresponds to total confirmed cases.
"""
confirmed_cases = data[3].get_text().strip()
return confirmed_cases
def get_deaths_from_data(data):
"""Get total deaths from data, a list.
Item at index 4 corresponds to total deaths.
"""
total_deaths = data[5].get_text().strip()
return total_deaths
def get_recovered_from_data(data):
"""Get total recovered cases from data, a list.
Item at index 8 corresponds to total deaths.
"""
total_deaths = data[9].get_text().strip()
return total_deaths
def get_wa_data_from_soup(soup):
state_table = soup.find_all("table")[2]
data = state_table.find_all("td")
return data
def get_cases_from_wa_data(data):
for idx, element in enumerate(data):
if element.text.strip() == "Washington":
wa_idx = idx
wa_cases = data[wa_idx+1].text.strip()
return wa_cases
def get_deaths_from_wa_data(data):
for idx, element in enumerate(data):
if element.text.strip() == "Washington":
wa_idx = idx
wa_deaths = data[wa_idx+2].text.strip()
return wa_deaths
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment