Spaces:
Runtime error
Runtime error
File size: 4,262 Bytes
4d7f847 1c6da08 4d7f847 1c6da08 | 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 | 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) |