Spaces:
Runtime error
Runtime error
File size: 8,248 Bytes
9072331 3751938 9072331 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 | from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from uszipcode import SearchEngine
import pandas as pd
gasBuddyurl = 'https://www.gasbuddy.com/home'
# To make this work as of 08/16/2024
# Make sure to use sqlalchemy_mate = 2.0.0.0
def fetchZipCodes():
engine = SearchEngine()
allzips = {}
population_threshold = 10000
density_threshold = 1000
for i in range(100000): #Get zipcode info for every possible 5-digit combination
zipcode = str(i).zfill(5)
try:
allzips[zipcode] = engine.by_zipcode(zipcode).to_dict()
except:
pass
#Convert dictionary to DataFrame
allzips = pd.DataFrame(allzips).T.reset_index(drop = True)
allzips = allzips.convert_dtypes()
allzips.dropna(inplace=True)
columnsNeeded = ['zipcode', 'zipcode_type', 'major_city','county', 'state', 'population', 'population_density']
zipsRequired = allzips[columnsNeeded]
zipsRequired = zipsRequired.query('zipcode_type == "STANDARD"')
# Example thresholds (you can adjust these based on your criteria)
# Assuming your DataFrame is named `df` and has columns 'population' and 'population_density'
filtered_df = zipsRequired[(zipsRequired['population'] > population_threshold) & (zipsRequired['population_density'] > density_threshold)]
# Excluding Territories
exclude_states = ['PR', 'VI', 'GU']
# Filter the DataFrame
zipsRequired = filtered_df[~filtered_df['state'].isin(exclude_states)]
return filtered_df
def GasBuddy(url, zipCodes, headless = True):
chrome_options = Options()
if headless:
chrome_options.add_argument("--headless") # Run in headless mode
chrome_options.add_argument("--no-sandbox") # Sandbox mode disabled (Linux-specific)
driverLocal = webdriver.Chrome(service=Service(), options=chrome_options)
driverLocal.get(url)
price_Heads = {}
price_Heads.fromkeys(zipCodes)
for z in zipCodes:
price_Heads[z] = {}
price_Heads[z].setdefault('price', [])
price_Heads[z].setdefault('station', [])
# Enter the Zip Code and Hit Enter
try:
enterZipCode = WebDriverWait(driverLocal, 5).until(
expected_conditions.presence_of_element_located((By.XPATH, \
".//div[@class='input__inputGroup___3ogvw'] \
/input[@id='search'] \
")))
enterZipCode.clear()
enterZipCode.send_keys(z)
enterZipCode.send_keys(Keys.RETURN)
# Check for the presence of the error message
error_message = None
try:
error_message = WebDriverWait(driverLocal, 2).until(
expected_conditions.presence_of_element_located((By.XPATH, ".//div/em[contains(text(), 'No stations found')]")))
except:
pass
if error_message:
price_Heads[z] = f"No stations found for ZIP code: {z}"
continue
blocks = WebDriverWait(driverLocal, 100).until(
expected_conditions.presence_of_all_elements_located((By.XPATH, \
"\
//div[@class='GenericStationListItem-module__stationListItem___3Jmn4'] \
")))
for b in blocks:
t = b.find_element(By.XPATH, ".//h3[contains(@class, 'StationDisplay-module__stationNameHeader___1A2q8')]").text
p = b.find_element(By.XPATH, ".//span[@class='text__xl___2MXGo text__left___1iOw3 StationDisplayPrice-module__price___3rARL']").text
price_Heads[z].get('station').append(t)
price_Heads[z].get('price').append(p)
except:
pass
driverLocal.close()
return price_Heads
def convertDF(data):
# Flatten the dictionary to create a list of records
records = []
for zipcode, details in data.items():
if isinstance(details, dict):
for price, heading in zip(details['price'], details['station']):
# Handle missing price by replacing '- - -' with 0, and remove the '$' sign
if price == '- - -':
continue
else:
price_value = float(price.replace('$', ''))
records.append({'zipcode': zipcode, 'station': heading.strip(), 'price': price_value})
return pd.DataFrame(records)
def loadLottieFile(filePath: str):
with open(file=filePath, mode = "r") as f:
return json.load(f)
import streamlit as st
import json
from streamlit_lottie import st_lottie_spinner
st.set_page_config(layout="wide",
page_title="Portfolio Optimization For Commodities",
page_icon="Portfolio_Icon.jpg")
if 'loadOnce' not in st.session_state:
anm = loadLottieFile("Anims/location.json")
with st_lottie_spinner(anm , height=400):
st.session_state.loadOnce = fetchZipCodes()
if 'scrappedData' not in st.session_state:
st.session_state.scrappedData = None
# Group by major_city and state, and aggregate zip codes into lists
grouped = st.session_state.loadOnce.groupby(['major_city', 'state'])['zipcode'].apply(list).reset_index()
# Create a dictionary with formatted keys and list of zip codes as values
city_state_zip_dict = {
f"{row['major_city']}, {row['state']}": row['zipcode']
for _, row in grouped.iterrows()
}
_,center,_ = st.columns((2,4,2))
with center.form(key = "parameters"):
st.subheader("Please select locations and configuration parameters as per your scraping needs")
#-------------------------------------------- Zip Codes ----------------------------------------------------------------
st.write("")
selected_locations = st.multiselect('Select Locations and Data will be scrapped as per their zip codes', list(city_state_zip_dict.keys()), default=list())
st.write("")
head = st.checkbox(label="Please check if you want headless scrapping")
submitted = st.form_submit_button("Download")
_, _leftDf, cenButtons, _rightDf, _ = st.columns((2,4,2,4,2))
if submitted:
# Fetch values for the specified keys and flatten the list
zipcodes = [zipcode for key in selected_locations if key in city_state_zip_dict for zipcode in city_state_zip_dict[key]]
anm = loadLottieFile("Anims/citations.json")
with st_lottie_spinner(anm , height=400):
prices = GasBuddy(url=gasBuddyurl, zipCodes=zipcodes, headless=head)
finalDf = convertDF(prices)
finalDf2 = pd.merge(finalDf, st.session_state.loadOnce[['zipcode', 'major_city', 'state']], on='zipcode', how='left')
st.session_state.scrappedData = finalDf2
_leftDf.dataframe(finalDf2, use_container_width=True)
cols = st.session_state.scrappedData.columns if st.session_state.scrappedData is not None else []
with cenButtons.form(key='parametersForm2'):
# if st.session_state.scrappedData is not None:
ind = st.multiselect(label='Column Names for index (new row identifiers) of Pivot Table', options= cols)
val = st.multiselect(label='Column Names for values of Pivot Table', options= cols)
col = st.multiselect(label='Column Names for columns of Pivot Table', options= cols)
submitted2 = st.form_submit_button("Pivot Data")
if submitted2:
try:
pivoted = st.session_state.scrappedData.pivot_table(index=ind, values=val, columns=col)
_rightDf.dataframe(pivoted, use_container_width = True)
except:
cenButtons.error("Your Configurations for Pivot Table are not accepable. Do you mind trying a different combination?")
|