Spaces:
Sleeping
Sleeping
Download run.py from dimdimm/crawling-datas: direct link, hf CLI and curl.
- Browser
- Download file 2.71 kB
-
https://huggingface.co/spaces/dimdimm/crawling-datas/resolve/main/run.py
- Command line
-
hf download hf://spaces/dimdimm/crawling-datas/run.py
-
curl -L -o run.py https://huggingface.co/spaces/dimdimm/crawling-datas/resolve/main/run.py
2.71 kB
| import argparse | |
| import logging | |
| import signal | |
| import sys | |
| import time | |
| import schedule | |
| from cleaner import clean_job | |
| from jobstreet import scrape_jobstreet | |
| from linkedin import scrape_linkedin | |
| from normalize import normalize | |
| from supabase_client import supabase | |
| logging.basicConfig( | |
| level=logging.INFO, | |
| format="%(asctime)s [%(levelname)s] %(message)s", | |
| ) | |
| log = logging.getLogger("scraper") | |
| KEYWORDS = [ | |
| "software engineer", | |
| "backend developer", | |
| "frontend developer", | |
| "data scientist", | |
| "full stack developer", | |
| "devops engineer", | |
| "ui ux designer", | |
| "mobile developer", | |
| # Non-IT (untuk variasi dataset di luar teknologi) | |
| "marketing", | |
| "accounting", | |
| "customer service", | |
| "sales", | |
| "finance", | |
| ] | |
| def push(jobs, source): | |
| batch = [] | |
| seen = set() | |
| for job in jobs: | |
| job = clean_job(job) | |
| if not job: | |
| continue | |
| url = job["url"] | |
| if url in seen: | |
| continue | |
| seen.add(url) | |
| batch.append(normalize(job, source)) | |
| if not batch: | |
| return 0 | |
| try: | |
| supabase.table("jobs") \ | |
| .upsert(batch, on_conflict="url") \ | |
| .execute() | |
| except Exception as e: | |
| log.error("[Supabase] Upsert gagal (%s): %s", source, e) | |
| return 0 | |
| return len(batch) | |
| def scrape_source(scraper, source, keyword): | |
| try: | |
| jobs = scraper(keyword) | |
| except Exception as e: | |
| log.error("[%s] Scraping gagal (keyword=%s): %s", source, keyword, e) | |
| return | |
| inserted = push(jobs, source) | |
| log.info("[%s] %s: %d ditemukan, %d dimasukkan", source, keyword, len(jobs), inserted) | |
| def main(): | |
| for keyword in KEYWORDS: | |
| scrape_source(scrape_linkedin, "LinkedIn", keyword) | |
| scrape_source(scrape_jobstreet, "JobStreet", keyword) | |
| running = True | |
| def shutdown(signum, frame): | |
| global running | |
| log.info("Ctrl+C ditekan. Menghentikan scraper...") | |
| running = False | |
| if __name__ == "__main__": | |
| parser = argparse.ArgumentParser(description="Job scraper LinkedIn & JobStreet") | |
| parser.add_argument("--once", action="store_true", | |
| help="Jalankan sekali lalu keluar (untuk cron/CI)") | |
| args = parser.parse_args() | |
| signal.signal(signal.SIGINT, shutdown) | |
| signal.signal(signal.SIGTERM, shutdown) | |
| main() # jalankan langsung saat pertama kali start | |
| if args.once: | |
| sys.exit(0) | |
| schedule.every(24).hours.do(main) | |
| log.info("Job scraper berjalan setiap 24 jam...") | |
| log.info("Tekan Ctrl+C untuk menghentikan.") | |
| while running: | |
| schedule.run_pending() | |
| time.sleep(60) | |
| log.info("Scraper dihentikan. Sampai jumpa!") | |
| sys.exit(0) |