Spaces:
Running
Running
fix: shorts_carousel.py - filter only real article URLs (c{N}a{N} pattern)
Browse files- shorts_carousel.py +30 -16
shorts_carousel.py
CHANGED
|
@@ -1,18 +1,18 @@
|
|
| 1 |
-
"""Self-contained 24h Shorts Carousel
|
| 2 |
-
|
| 3 |
-
"""
|
| 4 |
import requests
|
| 5 |
from bs4 import BeautifulSoup
|
| 6 |
|
| 7 |
HEADERS = {
|
| 8 |
-
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
|
| 9 |
"Accept-Language": "vi-VN,vi;q=0.9,en;q=0.8",
|
| 10 |
}
|
| 11 |
BASE_24H = "https://www.24h.com.vn"
|
| 12 |
|
| 13 |
|
| 14 |
def scrape_24h_news_shorts():
|
| 15 |
-
"""Scrape https://www.24h.com.vn/video/video-tin-tuc-cvd769.html
|
|
|
|
| 16 |
try:
|
| 17 |
url = f"{BASE_24H}/video/video-tin-tuc-cvd769.html"
|
| 18 |
r = requests.get(url, headers=HEADERS, timeout=15)
|
|
@@ -21,26 +21,40 @@ def scrape_24h_news_shorts():
|
|
| 21 |
articles, seen = [], set()
|
| 22 |
for a in soup.find_all("a", href=True):
|
| 23 |
href = a.get("href", "")
|
| 24 |
-
|
| 25 |
-
if not
|
| 26 |
-
continue
|
| 27 |
-
if "javascript:" in href:
|
| 28 |
continue
|
| 29 |
if not href.startswith("http"):
|
| 30 |
href = BASE_24H + href
|
| 31 |
if href in seen:
|
| 32 |
continue
|
| 33 |
seen.add(href)
|
|
|
|
|
|
|
| 34 |
img = a.find("img")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
img_src = None
|
| 36 |
if img:
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 42 |
if not img_src:
|
| 43 |
-
continue # Skip items without
|
| 44 |
articles.append({
|
| 45 |
"title": title, "link": href, "img": img_src,
|
| 46 |
"source": "24h", "is_video": True
|
|
@@ -50,7 +64,7 @@ def scrape_24h_news_shorts():
|
|
| 50 |
return []
|
| 51 |
|
| 52 |
|
| 53 |
-
# CSS
|
| 54 |
SHORTS_CSS = """
|
| 55 |
/* Shorts 9:16 Slider */
|
| 56 |
.vslide-shorts-item{flex:0 0 110px;scroll-snap-align:start;cursor:pointer;transition:transform .15s}
|
|
|
|
| 1 |
+
"""Self-contained 24h Shorts Carousel - scrapes video-tin-tuc page for actual video articles."""
|
| 2 |
+
import re
|
|
|
|
| 3 |
import requests
|
| 4 |
from bs4 import BeautifulSoup
|
| 5 |
|
| 6 |
HEADERS = {
|
| 7 |
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
|
| 8 |
"Accept-Language": "vi-VN,vi;q=0.9,en;q=0.8",
|
| 9 |
}
|
| 10 |
BASE_24H = "https://www.24h.com.vn"
|
| 11 |
|
| 12 |
|
| 13 |
def scrape_24h_news_shorts():
|
| 14 |
+
"""Scrape https://www.24h.com.vn/video/video-tin-tuc-cvd769.html
|
| 15 |
+
Returns only ACTUAL article links (pattern: -c{N}a{N}.html), not category pages."""
|
| 16 |
try:
|
| 17 |
url = f"{BASE_24H}/video/video-tin-tuc-cvd769.html"
|
| 18 |
r = requests.get(url, headers=HEADERS, timeout=15)
|
|
|
|
| 21 |
articles, seen = [], set()
|
| 22 |
for a in soup.find_all("a", href=True):
|
| 23 |
href = a.get("href", "")
|
| 24 |
+
# Only match real article URLs: ends with -c{digits}a{digits}.html
|
| 25 |
+
if not re.search(r'-c\d+a\d+\.html$', href):
|
|
|
|
|
|
|
| 26 |
continue
|
| 27 |
if not href.startswith("http"):
|
| 28 |
href = BASE_24H + href
|
| 29 |
if href in seen:
|
| 30 |
continue
|
| 31 |
seen.add(href)
|
| 32 |
+
# Get title
|
| 33 |
+
title = a.get("title", "") or ""
|
| 34 |
img = a.find("img")
|
| 35 |
+
if img and not title:
|
| 36 |
+
title = img.get("alt", "")
|
| 37 |
+
if not title:
|
| 38 |
+
title = a.get_text(strip=True)
|
| 39 |
+
if not title or len(title) < 10:
|
| 40 |
+
continue
|
| 41 |
+
# Get image - priority: data-original > data-src > src
|
| 42 |
img_src = None
|
| 43 |
if img:
|
| 44 |
+
img_src = img.get("data-original") or img.get("data-src") or img.get("src") or ""
|
| 45 |
+
if "base64" in img_src or len(img_src) < 20:
|
| 46 |
+
img_src = None
|
| 47 |
+
# Try parent element for image if not found in link
|
| 48 |
+
if not img_src:
|
| 49 |
+
parent = a.parent
|
| 50 |
+
if parent:
|
| 51 |
+
parent_img = parent.find("img")
|
| 52 |
+
if parent_img:
|
| 53 |
+
img_src = parent_img.get("data-original") or parent_img.get("data-src") or parent_img.get("src") or ""
|
| 54 |
+
if "base64" in img_src or len(img_src) < 20:
|
| 55 |
+
img_src = None
|
| 56 |
if not img_src:
|
| 57 |
+
continue # Skip items without thumbnail for visual carousel
|
| 58 |
articles.append({
|
| 59 |
"title": title, "link": href, "img": img_src,
|
| 60 |
"source": "24h", "is_video": True
|
|
|
|
| 64 |
return []
|
| 65 |
|
| 66 |
|
| 67 |
+
# CSS for the 9:16 shorts slider
|
| 68 |
SHORTS_CSS = """
|
| 69 |
/* Shorts 9:16 Slider */
|
| 70 |
.vslide-shorts-item{flex:0 0 110px;scroll-snap-align:start;cursor:pointer;transition:transform .15s}
|