From 0329569826d4bd75b2132a327ec8288fac77718c Mon Sep 17 00:00:00 2001 From: juyung Date: Thu, 26 Mar 2026 23:15:56 +0900 Subject: [PATCH] Upload --- README.md | 26 ++++ docs/assets/img/preview.png | Bin 0 -> 55202 bytes redacted.py | 229 ++++++++++++++++++++++++++++++++++++ 3 files changed, 255 insertions(+) create mode 100644 README.md create mode 100644 docs/assets/img/preview.png create mode 100644 redacted.py diff --git a/README.md b/README.md new file mode 100644 index 0000000..5e4ee96 --- /dev/null +++ b/README.md @@ -0,0 +1,26 @@ +# eduMail Scraper + +This repository contains Python tools I used to scrape school contact directories for students, alumni, staff, and professors. It also includes a fully anonymized version of the dataset (~112,000 contacts) that's safe to share, with all personally identifiable information (PII) like names, emails, phone numbers, and profile pictures removed. + +![Preview of the anonymized school contacts dataset](docs/assets/img/preview.png) + +## What's Inside + +- **Python scripts** for scraping and processing contact data +- **Anonymized dataset (`out.csv`)** + +## Dataset Columns + +| Column Name | Description | +|-------------------|-------------| +| Name | Full name | +| Email Address | School email | +| Chat Address | Outlook/Teams chat handle (same as email address) | +| Mobile | Mobile phone number (formats may vary, such as xxx-xxx-xxxx, (xxx) xxx-xxxx, or xxxxxxxxxx) | +| Work Phone | Office or work phone number | +| Job Title | The person's role, such as "Professor," "Student," or "Administrator" | +| Department | The department, program, or field the person belongs to, like "Department of Computer Science" | +| Office Location | Office or building location, like LIB 101 | +| Company | Name of the organization, school, or employer | +| Profile Picture | Profile photo or avatar in base64 | + diff --git a/docs/assets/img/preview.png b/docs/assets/img/preview.png new file mode 100644 index 0000000000000000000000000000000000000000..f8a5f6d6881bbad14adaee612c84b1e6a7007c1b GIT binary patch literal 55202 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 literal 0 HcmV?d00001 diff --git a/redacted.py b/redacted.py new file mode 100644 index 0000000..6dd7a80 --- /dev/null +++ b/redacted.py @@ -0,0 +1,229 @@ +#!/usr/bin/env python3 +import sys +import os +import random +import re +import pandas as pd + +# --- Redaction functions --- +def redact_name(name: str) -> str: + if not isinstance(name, str) or name == '': + return name + + s = name + out = list(s) + first_done = False + + for i, ch in enumerate(s): + if ch.isspace(): + continue + if not first_done: + first_done = True + continue + out[i] = '*' + + return ''.join(out) + +def redact_job_title(val: str) -> str: + if not isinstance(val, str) or val == '': + return val + s = val + out = list(s) + first_done = False + for i, ch in enumerate(s): + if ch.isspace(): + continue + if not first_done: + first_done = True + continue + out[i] = '*' + return ''.join(out) + +def redact_office_number(val: str) -> str: + if not isinstance(val, str) or val == '': + return val + s = str(val) + out = list(s) + first_digit_done = False + for i, ch in enumerate(s): + if ch.isdigit(): + if not first_digit_done: + first_digit_done = True + continue + out[i] = '*' + return ''.join(out) + +def redact_local_domain(addr: str) -> str: + if not isinstance(addr, str) or addr == '': + return addr + addr = addr.strip() + if '@' not in addr: + local = addr + domain = '' + else: + local, domain = addr.split('@', 1) + if not local: + red_local = local + elif len(local) == 1: + red_local = '*' + elif len(local) == 2: + red_local = local + else: + out_chars = list(local) + for i in range(1, len(local) - 1): + out_chars[i] = '*' + red_local = ''.join(out_chars) + return f"{red_local}@{domain}" if domain != '' else red_local + +def redact_phone(num: str) -> str: + if not isinstance(num, str) or num == '': + return num + s = str(num) + digits = [c for c in s if c.isdigit()] + if not digits: + return s + last_digit = digits[-1] + out_chars = [] + digit_idx = 0 + for c in s: + if c.isdigit(): + out_chars.append(last_digit if digit_idx == len(digits) - 1 else '*') + digit_idx += 1 + else: + out_chars.append(c) + return ''.join(out_chars) + +def redact_profile_pic(pic: str) -> str: + if not isinstance(pic, str) or pic == '': + return pic + keep = 20 + return pic if len(pic) <= keep else pic[:20] + ('*' * (len(pic) - 20)) + +def redact_dept(val: str) -> str: + if not isinstance(val, str) or val == '': + return val + s = val + n = len(s) + i = 0 + while i < n and s[i].isspace(): + i += 1 + while i < n and not s[i].isspace(): + i += 1 + out = list(s) + for j in range(i, n): + if out[j].isspace(): + continue + out[j] = '*' + return ''.join(out) + +def redact_office_location(val: str) -> str: + if not isinstance(val, str) or val == '': + return val + s = val + choice = random.choice([1, 2]) + if choice == 1: + return ''.join('*' if ch.isdigit() else ch for ch in s) + else: + return ''.join('*' if ch.isalpha() else ch for ch in s) + +# --- Exposure detection & masking --- +EMAIL_RE = re.compile(r'([A-Za-z0-9._%+\-]+)@([A-Za-z0-9.\-]+\.[A-Za-z]{2,})') +PHONE_CAND_RE = re.compile(r'[\+\(]?\d{1,4}[\)\-\s\.\/]?(?:\d[\-\s\.\/\(\)]?){2,}\d') + +def mask_phone_in_text(s: str, min_visible_digits: int = 4): + if not isinstance(s, str) or s == '': + return s, [] + new = list(s) + exposures = [] + for m in PHONE_CAND_RE.finditer(s): + seq = m.group(0) + visible_digits = sum(1 for ch in seq if ch.isdigit()) + if visible_digits >= min_visible_digits: + for i in range(m.start(), m.end()): + new[i] = '*' + exposures.append((m.start(), m.end(), seq)) + return ''.join(new), exposures + +def mask_email_local_in_text(s: str): + if not isinstance(s, str) or s == '': + return s, [] + new = list(s) + exposures = [] + for m in EMAIL_RE.finditer(s): + local_start, local_end = m.start(1), m.end(1) + local = s[local_start:local_end] + if '*' not in local: + for i in range(local_start, local_end): + new[i] = '*' + exposures.append((local_start, local_end, local)) + return ''.join(new), exposures + +def scan_and_mask_exposures(df: pd.DataFrame, min_visible_digits: int = 4): + df = df.copy() + for idx, row in df.iterrows(): + for col in df.columns: + val = row[col] + if not isinstance(val, str) or val == '': + continue + modified = val + modified, _ = mask_phone_in_text(modified, min_visible_digits) + modified, _ = mask_email_local_in_text(modified) + if modified != val: + df.at[idx, col] = modified + return df + +# --- DataFrame processing pipeline --- +def process_df(df: pd.DataFrame) -> pd.DataFrame: + df = df.copy() + + if 'Name' in df: + df['Name'] = df['Name'].map(redact_name) + if 'Email Address' in df: + df['Email Address'] = df['Email Address'].map(redact_local_domain) + if 'Chat Address' in df: + df['Chat Address'] = df['Chat Address'].map(redact_local_domain) + if 'Mobile' in df: + df['Mobile'] = df['Mobile'].map(redact_phone) + if 'Work Phone' in df: + df['Work Phone'] = df['Work Phone'].map(redact_phone) + if 'Profile Picture' in df: + df['Profile Picture'] = df['Profile Picture'].map(redact_profile_pic) + if 'Department' in df: + df['Department'] = df['Department'].map(redact_dept) + if 'Office Location' in df: + df['Office Location'] = df['Office Location'].map(redact_office_location) + + # Column F (index 5) → Job Title + if len(df.columns) > 5: + col_f = df.columns[5] + df[col_f] = df[col_f].map(redact_job_title) + + # Column H (index 7) → Office Number + if len(df.columns) > 7: + col_h = df.columns[7] + df[col_h] = df[col_h].map(redact_office_number) + + return df + +def main(): + if len(sys.argv) < 2: + print("Usage: python script.py inputfilename.csv [outputfilename.csv]") + sys.exit(1) + + inp = sys.argv[1] + outp = sys.argv[2] if len(sys.argv) > 2 else None + + if not outp: + base, ext = os.path.splitext(inp) + outp = f"{base}_redacted{ext or '.csv'}" + + df = pd.read_csv(inp, dtype=str, keep_default_na=False) + + redacted = process_df(df) + redacted = scan_and_mask_exposures(redacted, min_visible_digits=4) + + redacted.to_csv(outp, index=False) + print(f"Wrote redacted file to: {outp}") + +if __name__ == '__main__': + main()