Download features.py from saidutta69/AdVig: direct link, hf CLI and curl.
- Browser
- Download file 7.2 kB
-
https://huggingface.co/saidutta69/AdVig/resolve/main/features.py
- Command line
-
hf download hf://saidutta69/AdVig/features.py
-
curl -L -o features.py https://huggingface.co/saidutta69/AdVig/resolve/main/features.py
7.2 kB
| """AdVig feature extractor. | |
| ~30 features, all computable with cheap integer ops + tiny lexicon lookups, | |
| designed to port 1:1 to C on an ESP8266-class MCU. No allocations beyond | |
| small fixed buffers needed at inference time. | |
| """ | |
| import math | |
| import re | |
| # multi-part public suffixes (subset covering top traffic) | |
| MULTIPART_SUFFIXES = { | |
| "co.uk", "org.uk", "ac.uk", "gov.uk", "co.jp", "ne.jp", "or.jp", "ac.jp", | |
| "com.au", "net.au", "org.au", "edu.au", "gov.au", "co.nz", "net.nz", | |
| "org.nz", "co.in", "net.in", "org.in", "gov.in", "co.za", "org.za", | |
| "web.za", "com.br", "net.br", "org.br", "gov.br", "com.mx", "org.mx", | |
| "com.cn", "net.cn", "org.cn", "gov.cn", "com.hk", "org.hk", "com.sg", | |
| "com.my", "org.my", "com.tr", "org.tr", "co.kr", "or.kr", "com.ar", | |
| "com.co", "org.co", "com.pl", "org.pl", "com.ru", "org.ru", "net.ru", | |
| "com.ua", "org.ua", "net.ua", "com.tw", "org.tw", "com.vn", "com.ph", | |
| "co.id", "or.id", "com.pk", "org.pk", "com.bd", "co.th", "or.th", | |
| "com.sa", "com.eg", "com.ng", "com.gh", "co.ke", "com.il", "org.il", | |
| "co.at", "or.at", "ac.at", "co.hu", "org.hu", "co.ro", "org.ro", | |
| "com.es", "org.es", "com.pt", "org.pt", "co.it", "org.it", "com.gr", | |
| "co.be", "org.be", "co.nl", "org.nl", "co.dk", "co.no", "co.se", | |
| "com.fi", "com.ie", "co.cz", "co.cl", "com.pe", "com.uy", "com.ec", | |
| "com.do", "com.gt", "com.sv", "com.ni", "com.pa", "com.ve", | |
| } | |
| TRUSTED_TLDS = {"com", "org", "net", "edu", "gov", "mil", "int"} | |
| ADHEAVY_TLDS = { | |
| "xyz", "top", "click", "link", "online", "site", "club", "icu", "buzz", | |
| "stream", "cfd", "sbs", "shop", "store", "fun", "space", "website", | |
| "live", "rest", "monster", "quest", "cyou", "cam", "bar", "gdn", "mom", | |
| "lol", "bond", "autos", "boat", "review", "country", "kim", "work", | |
| "bid", "trade", "webcam", "dating", "adult", "porn", "sex", "casino", | |
| } | |
| VENDOR_TOKENS = { | |
| "doubleclick", "googlesyndication", "googleadservices", "adnxs", | |
| "criteo", "criteo", "taboola", "outbrain", "scorecardresearch", | |
| "quantserve", "quantcast", "moatads", "pubmatic", "rubiconproject", | |
| "openx", "smartadserver", "zedo", "yieldmo", "adform", "adroll", | |
| "adcolony", "chartboost", "applovin", "inmobi", "mopub", "admob", | |
| "adservice", "adsystem", "adsense", "adsrvr", "amplitude", | |
| } | |
| BIGTECH_TOKENS = { | |
| "google", "googleapis", "facebook", "fbcdn", "instagram", "amazon", | |
| "awsstatic", "cloudfront", "microsoft", "apple", "icloud", "netflix", | |
| "twitter", "tiktok", "bing", "linkedin", "reddit", "wikipedia", | |
| "cloudflare", "akamai", "fastly", "youtube", "yahoo", "ebay", | |
| } | |
| FEATURE_NAMES = [ | |
| "length", "label_count", "max_label_len", "digit_count", "max_digit_run", | |
| "hyphen_count", "entropy", "vowel_ratio", "starts_with_www", | |
| "has_punycode", "subdomain_depth", "tld_trusted", "tld_adheavy", | |
| "tld_is_cctld", "tld_length", | |
| "tok_ad", "tok_advert", "tok_banner", "tok_promo", "tok_sponsor", | |
| "tok_track", "tok_analytics", "tok_metrics", "tok_telemetry", | |
| "tok_beacon", "tok_pixel", "tok_tag", "tok_click", "tok_impression", | |
| "tok_affiliate", "tok_syndication", "tok_vendor", | |
| "tok_bigtech", "bigtech_and_adtoken", | |
| ] | |
| _VOWELS = frozenset("aeiou") | |
| def _registrable(domain: str) -> str: | |
| parts = domain.split(".") | |
| if len(parts) >= 3 and ".".join(parts[-2:]) in MULTIPART_SUFFIXES: | |
| return ".".join(parts[-3:]) | |
| if len(parts) >= 2: | |
| return ".".join(parts[-2:]) | |
| return domain | |
| def extract_features(domain: str): | |
| d = domain.lower() | |
| n = len(d) | |
| labels = d.split(".") | |
| label_count = len(labels) | |
| max_label_len = max(len(x) for x in labels) | |
| digit_count = sum(ch.isdigit() for ch in d) | |
| hyphen_count = d.count("-") | |
| max_digit_run = run = 0 | |
| for ch in d: | |
| if ch.isdigit(): | |
| run += 1 | |
| if run > max_digit_run: | |
| max_digit_run = run | |
| else: | |
| run = 0 | |
| freq = {} | |
| for ch in d: | |
| freq[ch] = freq.get(ch, 0) + 1 | |
| entropy = -sum((c / n) * math.log2(c / n) for c in freq.values()) if n else 0.0 | |
| vowels = sum(1 for ch in d if ch in _VOWELS) | |
| vowel_ratio = vowels / n if n else 0.0 | |
| starts_with_www = float(d.startswith("www.") or ".www." in ("." + d)) | |
| has_punycode = float("xn--" in d) | |
| reg = _registrable(d) | |
| reg_parts = reg.split(".") | |
| subdomain_depth = label_count - len(reg_parts) | |
| tld = labels[-1] | |
| tld_trusted = float(tld in TRUSTED_TLDS) | |
| tld_adheavy = float(tld in ADHEAVY_TLDS) | |
| tld_is_cctld = float(len(tld) == 2 and not tld_trusted) | |
| tld_length = len(tld) | |
| segs = re.split(r"[._-]", d) | |
| segset = set(segs) | |
| def any_seg(pred): | |
| return float(any(pred(t) for t in segs)) | |
| tok_ad = any_seg(lambda t: t in ("ad", "ads")) | |
| tok_advert = any_seg(lambda t: t.startswith("advert") or t.startswith("adver")) | |
| tok_banner = any_seg(lambda t: "banner" in t) | |
| tok_promo = any_seg(lambda t: t.startswith("promo")) | |
| tok_sponsor = any_seg(lambda t: "sponsor" in t) | |
| tok_track = any_seg(lambda t: t.startswith("track")) | |
| tok_analytics = any_seg(lambda t: "analytic" in t) | |
| tok_metrics = any_seg(lambda t: t.startswith("metric") or t.startswith("stat") or t == "st") | |
| tok_telemetry = any_seg(lambda t: t.startswith("telemetr")) | |
| tok_beacon = any_seg(lambda t: "beacon" in t) | |
| tok_pixel = any_seg(lambda t: "pixel" in t) | |
| tok_tag = any_seg(lambda t: t.startswith("tag")) | |
| tok_click = any_seg(lambda t: t.startswith("click") or t in ("clk", "clks")) | |
| tok_impression = any_seg(lambda t: t.startswith("impr")) | |
| tok_affiliate = any_seg(lambda t: t.startswith("affil") or t == "aff") | |
| tok_syndication = any_seg(lambda t: t.startswith("syndic")) | |
| tok_vendor = float(bool(segset & VENDOR_TOKENS)) | |
| tok_bigtech = float(bool(segset & BIGTECH_TOKENS)) | |
| ad_any = max(tok_ad, tok_advert, tok_track, tok_analytics, tok_vendor, | |
| tok_banner, tok_beacon, tok_pixel) | |
| bigtech_and_adtoken = ad_any * tok_bigtech | |
| return [ | |
| float(n), float(label_count), float(max_label_len), | |
| float(digit_count), float(max_digit_run), float(hyphen_count), | |
| entropy, vowel_ratio, starts_with_www, has_punycode, | |
| float(subdomain_depth), tld_trusted, tld_adheavy, tld_is_cctld, | |
| float(tld_length), | |
| tok_ad, tok_advert, tok_banner, tok_promo, tok_sponsor, tok_track, | |
| tok_analytics, tok_metrics, tok_telemetry, tok_beacon, tok_pixel, | |
| tok_tag, tok_click, tok_impression, tok_affiliate, | |
| tok_syndication, tok_vendor, tok_bigtech, bigtech_and_adtoken, | |
| ] | |
| def extract_batch(domains): | |
| import numpy as np | |
| return np.array([extract_features(d) for d in domains], dtype=np.float32) | |
| if __name__ == "__main__": | |
| tests = ["ads.doubleclick.net", "www.google.com", "stats.g.analytics.example.com", | |
| "mail.yahoo.co.uk", "pixel-tracking.adnxs.com"] | |
| for t in tests: | |
| feats = dict(zip(FEATURE_NAMES, extract_features(t))) | |
| keep = ["length", "subdomain_depth", "tok_ad", "tok_track", "tok_analytics", | |
| "tok_vendor", "tok_bigtech", "bigtech_and_adtoken", "tld_trusted"] | |
| print(f"{t:45s}", {k: feats[k] for k in keep}) | |