Spaces:
Running
Running
Add date_vn to article scraper, add date extraction to search functions
Browse files- app_v2_entry.py +51 -12
app_v2_entry.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
| 1 |
"""VNEWS v2 Entry Point - with fast bongda proxy + rewrite endpoints + multilingual TTS"""
|
| 2 |
import sys, os
|
| 3 |
-
from
|
|
|
|
| 4 |
|
| 5 |
try:
|
| 6 |
import ai_ext
|
|
@@ -213,6 +214,11 @@ def _has_kw(topic,title):
|
|
| 213 |
if not words:return True
|
| 214 |
return any(w in tt for w in words)
|
| 215 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 216 |
def _s_vnexpress(topic,limit=8):
|
| 217 |
items=[]
|
| 218 |
try:
|
|
@@ -221,7 +227,7 @@ def _s_vnexpress(topic,limit=8):
|
|
| 221 |
a=art.select_one('h2 a, h3 a')
|
| 222 |
if a and a.get('href'):
|
| 223 |
t=_clean(a.get('title','') or a.get_text(strip=True))
|
| 224 |
-
if _has_kw(topic,t):items.append({'title':t,'url':a['href'],'via':'VnExpress'})
|
| 225 |
except:pass
|
| 226 |
return items
|
| 227 |
|
|
@@ -233,7 +239,7 @@ def _s_dantri(topic,limit=8):
|
|
| 233 |
t=_clean(a.get_text(strip=True));href=a.get('href','')
|
| 234 |
if t and len(t)>15 and _has_kw(topic,t):
|
| 235 |
if not href.startswith('http'):href='https://dantri.com.vn'+href
|
| 236 |
-
items.append({'title':t,'url':href,'via':'Dân Trí'})
|
| 237 |
if len(items)>=limit:break
|
| 238 |
except:pass
|
| 239 |
return items
|
|
@@ -246,7 +252,7 @@ def _s_vietnamnet(topic,limit=6):
|
|
| 246 |
t=_clean(a.get('title','') or a.get_text(strip=True));href=a.get('href','')
|
| 247 |
if t and len(t)>15 and _has_kw(topic,t):
|
| 248 |
if not href.startswith('http'):href='https://vietnamnet.vn'+href
|
| 249 |
-
items.append({'title':t,'url':href,'via':'VietNamNet'})
|
| 250 |
if len(items)>=limit:break
|
| 251 |
except:pass
|
| 252 |
return items
|
|
@@ -259,7 +265,7 @@ def _s_bongda(topic,limit=5):
|
|
| 259 |
t=_clean(a.get_text(strip=True));href=a.get('href','')
|
| 260 |
if t and len(t)>15 and _has_kw(topic,t):
|
| 261 |
if not href.startswith('http'):href='https://bongda.com.vn'+href
|
| 262 |
-
items.append({'title':t,'url':href,'via':'Bóng Đá'})
|
| 263 |
if len(items)>=limit:break
|
| 264 |
except:pass
|
| 265 |
return items
|
|
@@ -272,7 +278,7 @@ def _s_genk(topic,limit=5):
|
|
| 272 |
t=_clean(a.get('title','') or a.get_text(strip=True));href=a.get('href','')
|
| 273 |
if t and len(t)>15 and _has_kw(topic,t):
|
| 274 |
if href.startswith('/'):href='https://genk.vn'+href
|
| 275 |
-
items.append({'title':t,'url':href,'via':'GenK'})
|
| 276 |
if len(items)>=limit:break
|
| 277 |
except:pass
|
| 278 |
return items
|
|
@@ -285,7 +291,7 @@ def _s_thanhnien(topic,limit=6):
|
|
| 285 |
t=_clean(a.get('title','') or a.get_text(strip=True));href=a.get('href','')
|
| 286 |
if t and len(t)>15 and _has_kw(topic,t):
|
| 287 |
if not href.startswith('http'):href='https://thanhnien.vn'+href
|
| 288 |
-
items.append({'title':t,'url':href,'via':'Thanh Niên'})
|
| 289 |
if len(items)>=limit:break
|
| 290 |
except:pass
|
| 291 |
return items
|
|
@@ -298,7 +304,7 @@ def _s_tuoitre(topic,limit=6):
|
|
| 298 |
t=_clean(a.get('title','') or a.get_text(strip=True));href=a.get('href','')
|
| 299 |
if t and len(t)>15 and _has_kw(topic,t):
|
| 300 |
if not href.startswith('http'):href='https://tuoitre.vn'+href
|
| 301 |
-
items.append({'title':t,'url':href,'via':'Tuổi Trẻ'})
|
| 302 |
if len(items)>=limit:break
|
| 303 |
except:pass
|
| 304 |
return items
|
|
@@ -311,7 +317,7 @@ def _s_thethaovanhoa(topic,limit=5):
|
|
| 311 |
t=_clean(a.get('title','') or a.get_text(strip=True));href=a.get('href','')
|
| 312 |
if t and len(t)>15 and _has_kw(topic,t):
|
| 313 |
if not href.startswith('http'):href='https://thethaovanhoa.vn'+href
|
| 314 |
-
items.append({'title':t,'url':href,'via':'TT&VH'})
|
| 315 |
if len(items)>=limit:break
|
| 316 |
except:pass
|
| 317 |
return items
|
|
@@ -376,6 +382,39 @@ def _scrape_article_fast(url):
|
|
| 376 |
if ogi:
|
| 377 |
og_img = ogi.get('content', '')
|
| 378 |
if og_img.startswith('//'): og_img = 'https:' + og_img
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 379 |
h1 = soup.find('h1')
|
| 380 |
if not title and h1: title = h1.get_text(strip=True)[:200]
|
| 381 |
body = []
|
|
@@ -416,18 +455,18 @@ def _scrape_article_fast(url):
|
|
| 416 |
if ct: body.append({'type': 'p', 'text': ct})
|
| 417 |
if len(body) >= 2:
|
| 418 |
return {'title': _clean(title), 'summary': _clean(summary), 'og_image': og_img,
|
| 419 |
-
'body': body[:50], 'source': domain, 'url': url}
|
| 420 |
if title and (summary or og_img):
|
| 421 |
fallback = []
|
| 422 |
if og_img: fallback.append({'type': 'img', 'src': og_img})
|
| 423 |
if summary: fallback.append({'type': 'p', 'text': summary})
|
| 424 |
if fallback:
|
| 425 |
return {'title': _clean(title), 'summary': _clean(summary), 'og_image': og_img,
|
| 426 |
-
'body': fallback, 'source': domain, 'url': url, 'fallback': True}
|
| 427 |
if title:
|
| 428 |
return {'title': _clean(title), 'summary': '', 'og_image': '',
|
| 429 |
'body': [{'type': 'p', 'text': 'Nội dung đang được tải...'}],
|
| 430 |
-
'source': domain, 'url': url, 'fallback': True}
|
| 431 |
break
|
| 432 |
except Exception:
|
| 433 |
continue
|
|
|
|
| 1 |
"""VNEWS v2 Entry Point - with fast bongda proxy + rewrite endpoints + multilingual TTS"""
|
| 2 |
import sys, os
|
| 3 |
+
from datetime import timedelta
|
| 4 |
+
from main import app, HEADERS, BONGDA_HEADERS, fetch_bongda_api, HL_LEAGUES, VN_TZ
|
| 5 |
|
| 6 |
try:
|
| 7 |
import ai_ext
|
|
|
|
| 214 |
if not words:return True
|
| 215 |
return any(w in tt for w in words)
|
| 216 |
|
| 217 |
+
def _s_date(el):
|
| 218 |
+
dt = el.find('time') or el.select_one('.date, .time, span.date, span.time, .meta-date')
|
| 219 |
+
if dt: return _clean(dt.get_text(strip=True))[:20]
|
| 220 |
+
return ''
|
| 221 |
+
|
| 222 |
def _s_vnexpress(topic,limit=8):
|
| 223 |
items=[]
|
| 224 |
try:
|
|
|
|
| 227 |
a=art.select_one('h2 a, h3 a')
|
| 228 |
if a and a.get('href'):
|
| 229 |
t=_clean(a.get('title','') or a.get_text(strip=True))
|
| 230 |
+
if _has_kw(topic,t):items.append({'title':t,'url':a['href'],'via':'VnExpress','date':_s_date(art)})
|
| 231 |
except:pass
|
| 232 |
return items
|
| 233 |
|
|
|
|
| 239 |
t=_clean(a.get_text(strip=True));href=a.get('href','')
|
| 240 |
if t and len(t)>15 and _has_kw(topic,t):
|
| 241 |
if not href.startswith('http'):href='https://dantri.com.vn'+href
|
| 242 |
+
items.append({'title':t,'url':href,'via':'Dân Trí','date':_s_date(a.parent or a)})
|
| 243 |
if len(items)>=limit:break
|
| 244 |
except:pass
|
| 245 |
return items
|
|
|
|
| 252 |
t=_clean(a.get('title','') or a.get_text(strip=True));href=a.get('href','')
|
| 253 |
if t and len(t)>15 and _has_kw(topic,t):
|
| 254 |
if not href.startswith('http'):href='https://vietnamnet.vn'+href
|
| 255 |
+
items.append({'title':t,'url':href,'via':'VietNamNet','date':_s_date(a.parent or a)})
|
| 256 |
if len(items)>=limit:break
|
| 257 |
except:pass
|
| 258 |
return items
|
|
|
|
| 265 |
t=_clean(a.get_text(strip=True));href=a.get('href','')
|
| 266 |
if t and len(t)>15 and _has_kw(topic,t):
|
| 267 |
if not href.startswith('http'):href='https://bongda.com.vn'+href
|
| 268 |
+
items.append({'title':t,'url':href,'via':'Bóng Đá','date':_s_date(a.parent or a)})
|
| 269 |
if len(items)>=limit:break
|
| 270 |
except:pass
|
| 271 |
return items
|
|
|
|
| 278 |
t=_clean(a.get('title','') or a.get_text(strip=True));href=a.get('href','')
|
| 279 |
if t and len(t)>15 and _has_kw(topic,t):
|
| 280 |
if href.startswith('/'):href='https://genk.vn'+href
|
| 281 |
+
items.append({'title':t,'url':href,'via':'GenK','date':_s_date(a.parent or a)})
|
| 282 |
if len(items)>=limit:break
|
| 283 |
except:pass
|
| 284 |
return items
|
|
|
|
| 291 |
t=_clean(a.get('title','') or a.get_text(strip=True));href=a.get('href','')
|
| 292 |
if t and len(t)>15 and _has_kw(topic,t):
|
| 293 |
if not href.startswith('http'):href='https://thanhnien.vn'+href
|
| 294 |
+
items.append({'title':t,'url':href,'via':'Thanh Niên','date':_s_date(a.parent or a)})
|
| 295 |
if len(items)>=limit:break
|
| 296 |
except:pass
|
| 297 |
return items
|
|
|
|
| 304 |
t=_clean(a.get('title','') or a.get_text(strip=True));href=a.get('href','')
|
| 305 |
if t and len(t)>15 and _has_kw(topic,t):
|
| 306 |
if not href.startswith('http'):href='https://tuoitre.vn'+href
|
| 307 |
+
items.append({'title':t,'url':href,'via':'Tuổi Trẻ','date':_s_date(a.parent or a)})
|
| 308 |
if len(items)>=limit:break
|
| 309 |
except:pass
|
| 310 |
return items
|
|
|
|
| 317 |
t=_clean(a.get('title','') or a.get_text(strip=True));href=a.get('href','')
|
| 318 |
if t and len(t)>15 and _has_kw(topic,t):
|
| 319 |
if not href.startswith('http'):href='https://thethaovanhoa.vn'+href
|
| 320 |
+
items.append({'title':t,'url':href,'via':'TT&VH','date':_s_date(a.parent or a)})
|
| 321 |
if len(items)>=limit:break
|
| 322 |
except:pass
|
| 323 |
return items
|
|
|
|
| 382 |
if ogi:
|
| 383 |
og_img = ogi.get('content', '')
|
| 384 |
if og_img.startswith('//'): og_img = 'https:' + og_img
|
| 385 |
+
# Extract date
|
| 386 |
+
pub_date = ""
|
| 387 |
+
for meta_name in ['article:published_time', 'datePublished', 'date', 'pubdate', 'og:updated_time']:
|
| 388 |
+
meta = soup.find('meta', property=meta_name) or soup.find('meta', attrs={'name': meta_name})
|
| 389 |
+
if meta and meta.get('content'):
|
| 390 |
+
pub_date = meta.get('content')[:19]
|
| 391 |
+
break
|
| 392 |
+
if not pub_date:
|
| 393 |
+
time_el = soup.find('time')
|
| 394 |
+
if time_el and time_el.get('datetime'):
|
| 395 |
+
pub_date = time_el.get('datetime')[:19]
|
| 396 |
+
# Convert to VN time
|
| 397 |
+
date_vn = ""
|
| 398 |
+
if pub_date:
|
| 399 |
+
try:
|
| 400 |
+
from datetime import datetime as _dt
|
| 401 |
+
from dateutil import parser
|
| 402 |
+
dt = parser.parse(pub_date)
|
| 403 |
+
vn = dt + timedelta(hours=7)
|
| 404 |
+
now = _dt.now(VN_TZ)
|
| 405 |
+
diff = (now - vn).total_seconds()
|
| 406 |
+
if diff < 3600:
|
| 407 |
+
mins = int(diff / 60)
|
| 408 |
+
date_vn = f"{mins} phút trước" if mins > 0 else "Vừa xong"
|
| 409 |
+
elif diff < 86400:
|
| 410 |
+
hrs = int(diff / 3600)
|
| 411 |
+
date_vn = f"{hrs} giờ trước"
|
| 412 |
+
elif diff < 172800:
|
| 413 |
+
date_vn = "Hôm qua"
|
| 414 |
+
else:
|
| 415 |
+
date_vn = vn.strftime("%d/%m/%Y %H:%M")
|
| 416 |
+
except:
|
| 417 |
+
date_vn = pub_date[:10]
|
| 418 |
h1 = soup.find('h1')
|
| 419 |
if not title and h1: title = h1.get_text(strip=True)[:200]
|
| 420 |
body = []
|
|
|
|
| 455 |
if ct: body.append({'type': 'p', 'text': ct})
|
| 456 |
if len(body) >= 2:
|
| 457 |
return {'title': _clean(title), 'summary': _clean(summary), 'og_image': og_img,
|
| 458 |
+
'body': body[:50], 'source': domain, 'url': url, 'date_vn': date_vn}
|
| 459 |
if title and (summary or og_img):
|
| 460 |
fallback = []
|
| 461 |
if og_img: fallback.append({'type': 'img', 'src': og_img})
|
| 462 |
if summary: fallback.append({'type': 'p', 'text': summary})
|
| 463 |
if fallback:
|
| 464 |
return {'title': _clean(title), 'summary': _clean(summary), 'og_image': og_img,
|
| 465 |
+
'body': fallback, 'source': domain, 'url': url, 'fallback': True, 'date_vn': date_vn}
|
| 466 |
if title:
|
| 467 |
return {'title': _clean(title), 'summary': '', 'og_image': '',
|
| 468 |
'body': [{'type': 'p', 'text': 'Nội dung đang được tải...'}],
|
| 469 |
+
'source': domain, 'url': url, 'fallback': True, 'date_vn': date_vn}
|
| 470 |
break
|
| 471 |
except Exception:
|
| 472 |
continue
|