Extractor / src /streamlit_app.py
jayanth-2005's picture
Update src/streamlit_app.py
1c6da08 verified
Raw History Blame Contribute Delete
4.26 kB
import streamlit as st
import requests
from bs4 import BeautifulSoup
import pandas as pd
import re
from urllib.parse import urljoin
HEADERS = {"User-Agent": "Mozilla/5.0"}
# -------------------------------
# FETCH PAGE
# -------------------------------
def fetch_page(url):
try:
response = requests.get(url, headers=HEADERS, timeout=10)
return BeautifulSoup(response.text, "html.parser")
except:
return None
# -------------------------------
# EXTRACT FROM BLOCKS (MAIN LOGIC)
# -------------------------------
def extract_from_blocks(soup):
blocks = soup.find_all(["p", "div", "li"])
data = []
for block in blocks:
text = block.get_text(" ", strip=True)
# Email detection
email_match = re.search(
r"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}", text
)
if email_match:
email = email_match.group()
# Name guess
words = text.split()
name = " ".join(words[:3]) if len(words) >= 3 else "Unknown"
# Phone
phone_match = re.search(r"\+?\d[\d\s\-]{8,15}", text)
phone = phone_match.group() if phone_match else "Not Available"
data.append([name, email, phone])
return data
# -------------------------------
# TRY TO FIND PROFILE LINKS
# -------------------------------
def get_profile_links(soup, base_url):
links = set()
for a in soup.find_all("a", href=True):
href = a["href"]
full = urljoin(base_url, href)
keywords = ["faculty", "profile", "staff", "person", "people"]
if any(k in full.lower() for k in keywords):
links.add(full)
return list(links)
# -------------------------------
# EXTRACT FROM PROFILE PAGE
# -------------------------------
def extract_from_profile(url):
soup = fetch_page(url)
if not soup:
return None
text = soup.get_text(" ", strip=True)
# Name
name = "Not Available"
for tag in ["h1", "h2", "h3"]:
t = soup.find(tag)
if t:
val = t.get_text(strip=True)
if 2 < len(val) < 80:
name = val
break
# Email
email_match = re.search(
r"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}", text
)
email = email_match.group() if email_match else "Not Available"
# Phone
phone_match = re.search(r"\+?\d[\d\s\-]{8,15}", text)
phone = phone_match.group() if phone_match else "Not Available"
return [name, email, phone]
# -------------------------------
# MAIN SCRAPER FUNCTION
# -------------------------------
def scrape(url):
soup = fetch_page(url)
if not soup:
return None, "❌ Failed to load page"
# Method 1: Direct extraction
data = extract_from_blocks(soup)
# Method 2: Profile-based extraction (fallback)
if not data:
links = get_profile_links(soup, url)
for link in links[:20]: # limit for speed
result = extract_from_profile(link)
if result:
data.append(result)
if not data:
return None, "❌ No data found (site may block scraping or use JavaScript)"
df = pd.DataFrame(data, columns=["Name", "Email", "Phone"])
df = df.drop_duplicates()
return df, f"βœ… Found {len(df)} records"
# -------------------------------
# STREAMLIT UI
# -------------------------------
st.set_page_config(page_title="Faculty Scraper", layout="wide")
st.title("πŸŽ“ Universal Faculty Scraper")
st.markdown("Paste a college faculty page URL and extract emails, names, and phone numbers.")
url = st.text_input("πŸ”— Enter Faculty Page URL")
if st.button("πŸš€ Scrape"):
if not url:
st.warning("Please enter a URL")
else:
with st.spinner("Scraping in progress..."):
df, msg = scrape(url)
if df is not None:
st.success(msg)
st.dataframe(df, use_container_width=True)
csv = df.to_csv(index=False).encode("utf-8")
st.download_button(
label="πŸ“₯ Download CSV",
data=csv,
file_name="faculty_data.csv",
mime="text/csv",
)
else:
st.error(msg)