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)