Skip to content

Commit ca4c399

Browse files
authored
rewrite daixin parser
1 parent 0657cb9 commit ca4c399

1 file changed

Lines changed: 146 additions & 0 deletions

File tree

bin/_parsers/daixin.py

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
4+
import os, datetime, sys, re
5+
from bs4 import BeautifulSoup
6+
from pathlib import Path
7+
from dotenv import load_dotenv
8+
from shared_utils import appender, errlog
9+
from urllib.parse import urlparse
10+
import pycountry
11+
12+
env_path = Path("../.env")
13+
load_dotenv(dotenv_path=env_path)
14+
home = os.getenv("RANSOMWARELIVE_HOME")
15+
tmp_dir = Path(home + os.getenv("TMP_DIR"))
16+
17+
# ---------- helpers ----------
18+
SPACE_RE = re.compile(r"\s+")
19+
PAREN_RE = re.compile(r"\(([^)]+)\)")
20+
SPLIT_COUNTRY_RE = re.compile(r"[,;/]|\band\b", flags=re.I)
21+
22+
def normspace(s: str) -> str:
23+
return SPACE_RE.sub(" ", (s or "").strip())
24+
25+
def clean_title(h4) -> str:
26+
if not h4:
27+
return ""
28+
for img in h4.find_all("img"):
29+
img.decompose()
30+
return normspace(h4.get_text(" ", strip=True))
31+
32+
def alpha3_to_alpha2(code: str) -> str:
33+
"""Convert ISO-3166 alpha-3 to alpha-2 if possible."""
34+
try:
35+
return pycountry.countries.get(alpha_3=code).alpha_2
36+
except Exception:
37+
return code # fallback if not found
38+
39+
def extract_1st_country(title: str) -> str:
40+
if not title:
41+
return ""
42+
m = PAREN_RE.search(title)
43+
if not m:
44+
return ""
45+
inside = normspace(m.group(1))
46+
first_token = SPLIT_COUNTRY_RE.split(inside)[0].strip()
47+
m_iso = re.search(r"\b([A-Z]{2,3})\b", first_token)
48+
if m_iso:
49+
code = m_iso.group(1)
50+
if len(code) == 3: # Convert 3-letter to 2-letter
51+
return alpha3_to_alpha2(code)
52+
return code
53+
return first_token
54+
55+
def strip_country_from_title(title: str) -> str:
56+
if not title:
57+
return ""
58+
return normspace(re.sub(r"\s*\([^)]*\)\s*$", "", title))
59+
60+
def get_website(card) -> str:
61+
for h6 in card.find_all("h6"):
62+
txt = h6.get_text(" ", strip=True).lower()
63+
if "web site" in txt:
64+
a = h6.find("a", href=True)
65+
if a and a.get("href"):
66+
href = a["href"].strip()
67+
# Normalize to include scheme if missing
68+
if not re.match(r"^https?://", href, flags=re.I):
69+
href = "https://" + href.lstrip()
70+
try:
71+
return re.sub(r"^www\.", "", urlparse(href).netloc.lower())
72+
except Exception:
73+
return ""
74+
return ""
75+
76+
77+
def get_description_before_hr(card) -> str:
78+
desc_parts = []
79+
for child in card.children:
80+
name = getattr(child, "name", None)
81+
if name == "hr":
82+
break
83+
if name == "p":
84+
cls = child.get("class") or []
85+
if "card-text" in cls:
86+
t = normspace(child.get_text(" ", strip=True))
87+
if t:
88+
desc_parts.append(t)
89+
return normspace(" ".join(desc_parts))
90+
91+
# ---------- main ----------
92+
def main():
93+
script_path = os.path.abspath(__file__)
94+
if os.path.islink(script_path):
95+
original_path = os.readlink(script_path)
96+
if not os.path.isabs(original_path):
97+
original_path = os.path.join(os.path.dirname(script_path), original_path)
98+
group_name = os.path.basename(original_path).replace('.py','')
99+
else:
100+
group_name = os.path.basename(script_path).replace('.py','')
101+
102+
for filename in os.listdir(tmp_dir):
103+
try:
104+
if not filename.startswith(group_name + '-'):
105+
continue
106+
107+
html_doc = tmp_dir / filename
108+
with open(html_doc, 'r', encoding='utf-8') as file:
109+
soup = BeautifulSoup(file, 'html.parser')
110+
111+
cards = soup.find_all("div", class_="card-body")
112+
for card in cards:
113+
try:
114+
h4 = card.find("h4")
115+
raw_title = clean_title(h4)
116+
if not raw_title:
117+
continue
118+
119+
country = extract_1st_country(raw_title)
120+
victim = strip_country_from_title(raw_title)
121+
122+
website = get_website(card)
123+
description = get_description_before_hr(card)
124+
125+
126+
if not description:
127+
continue
128+
129+
appender(
130+
victim=victim,
131+
group_name=group_name,
132+
description=description,
133+
website=website,
134+
published="",
135+
post_url="",
136+
country=country
137+
)
138+
139+
except Exception as e_card:
140+
errlog(group_name + ' - card parse error: ' + str(e_card) + ' in file: ' + filename)
141+
142+
except Exception as e:
143+
errlog(group_name + ' - parsing fail with error: ' + str(e) + ' in file: ' + filename)
144+
145+
if __name__ == "__main__":
146+
main()

0 commit comments

Comments
 (0)