bep40 commited on
Commit
bb652f7
·
verified ·
1 Parent(s): 26ce581

Hashtag search + hot topics: add bongda & bongdaplus (Jina); Jina article fallback

Browse files
Files changed (1) hide show
  1. app_v2_entry.py +80 -6
app_v2_entry.py CHANGED
@@ -71,7 +71,29 @@ def _s_vietnamnet(topic,limit=6):
71
  if len(items)>=limit:break
72
  except:pass
73
  return items
74
- def _s_bongda(topic,limit=5):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
  items=[]
76
  try:
77
  r=req.get(f"https://bongda.com.vn/tim-kiem.html?q={quote(topic)}",headers={'User-Agent':'Mozilla/5.0'},timeout=8);soup=BeautifulSoup(r.text,'lxml')
@@ -133,9 +155,9 @@ def _s_thethaovanhoa(topic,limit=5):
133
  return items
134
  def _search_all(topic,limit=36):
135
  results={}
136
- with ThreadPoolExecutor(8) as ex:
137
- futs={ex.submit(_s_vnexpress,topic,8):'vne',ex.submit(_s_dantri,topic,8):'dt',ex.submit(_s_vietnamnet,topic,6):'vnn',ex.submit(_s_bongda,topic,5):'bd',ex.submit(_s_genk,topic,5):'gk',ex.submit(_s_thanhnien,topic,6):'tn',ex.submit(_s_tuoitre,topic,6):'tt',ex.submit(_s_thethaovanhoa,topic,5):'tvh'}
138
- for f in as_completed(futs,timeout=14):
139
  try:results[futs[f]]=f.result()
140
  except:results[futs[f]]=[]
141
  srcs=list(results.values());out=[];seen=set();mx=max((len(s) for s in srcs),default=0)
@@ -145,9 +167,40 @@ def _search_all(topic,limit=36):
145
  return out[:limit]
146
  # Override article endpoint
147
  app.router.routes=[r for r in app.router.routes if not(getattr(r,'path',None)=='/api/article' and 'GET' in getattr(r,'methods',set()))]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
148
  def _scrape_generic(url):
149
  try:
150
- r=req.get(url,headers={'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36','Accept-Language':'vi-VN,vi;q=0.9'},timeout=15,allow_redirects=True);r.encoding='utf-8';soup=BeautifulSoup(r.text,'lxml')
 
 
 
 
 
151
  for tag in soup.find_all(['script','style','nav','footer','aside','form','noscript']):tag.decompose()
152
  h1=soup.find('h1');ogt=soup.find('meta',property='og:title');title=(h1.get_text(strip=True) if h1 else '')or(ogt.get('content','') if ogt else '')
153
  ogd=soup.find('meta',property='og:description');summary=ogd.get('content','') if ogd else ''
@@ -166,8 +219,12 @@ def _scrape_generic(url):
166
  im=el if el.name=='img' else el.find('img')
167
  if im:src=im.get('data-src') or im.get('src') or'';(body.append({'type':'img','src':'https:'+src if src.startswith('//') else src}) if src and'base64' not in src else None)
168
  if not body and summary:body=[{'type':'p','text':summary}]
 
 
 
169
  return{'title':_clean(title),'summary':_clean(summary),'og_image':og_img,'body':body[:50],'source':'generic','url':url}
170
- except:return None
 
171
  @app.get('/api/article')
172
  def api_article_v2(url:str=Query(...)):
173
  from main import scrape_vne_article,scrape_bbc_article,scrape_dantri_article,scrape_genk_article,scrape_ttvh_article
@@ -198,6 +255,23 @@ def _get_hot_topics():
198
  phrase=' '.join(words[i:i+n])
199
  if 8<=len(phrase)<=45:key=phrase.lower();freq[key]=freq.get(key,0)+1;display[key]=phrase
200
  except:continue
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
201
  ranked=sorted(freq.items(),key=lambda x:x[1],reverse=True);topics=[];seen=set()
202
  for key,count in ranked:
203
  is_dup=any(len(set(e.split())&set(key.split()))/max(len(set(e.split())),len(set(key.split())),1)>0.6 for e in seen)
 
71
  if len(items)>=limit:break
72
  except:pass
73
  return items
74
+ def _s_bongda(topic,limit=6):
75
+ """bongda.com.vn via Jina homepage (site is JS-protected); keyword-filtered."""
76
+ items=[]
77
+ try:
78
+ from main import scrape_bongda_jina
79
+ for a in scrape_bongda_jina(40):
80
+ if _has_kw(topic,a.get('title','')):
81
+ items.append({'title':a['title'],'url':a['link'],'via':'Bóng Đá'})
82
+ if len(items)>=limit:break
83
+ except:pass
84
+ return items
85
+ def _s_bongdaplus(topic,limit=6):
86
+ """bongdaplus.vn via Jina homepage (JS SPA); keyword-filtered."""
87
+ items=[]
88
+ try:
89
+ from main import scrape_bongdaplus_jina
90
+ for a in scrape_bongdaplus_jina(40):
91
+ if _has_kw(topic,a.get('title','')):
92
+ items.append({'title':a['title'],'url':a['link'],'via':'Bóng Đá+'})
93
+ if len(items)>=limit:break
94
+ except:pass
95
+ return items
96
+ def _s_bongda_OLD(topic,limit=5):
97
  items=[]
98
  try:
99
  r=req.get(f"https://bongda.com.vn/tim-kiem.html?q={quote(topic)}",headers={'User-Agent':'Mozilla/5.0'},timeout=8);soup=BeautifulSoup(r.text,'lxml')
 
155
  return items
156
  def _search_all(topic,limit=36):
157
  results={}
158
+ with ThreadPoolExecutor(10) as ex:
159
+ futs={ex.submit(_s_vnexpress,topic,8):'vne',ex.submit(_s_dantri,topic,8):'dt',ex.submit(_s_vietnamnet,topic,6):'vnn',ex.submit(_s_bongda,topic,6):'bd',ex.submit(_s_bongdaplus,topic,6):'bdp',ex.submit(_s_genk,topic,5):'gk',ex.submit(_s_thanhnien,topic,6):'tn',ex.submit(_s_tuoitre,topic,6):'tt',ex.submit(_s_thethaovanhoa,topic,5):'tvh'}
160
+ for f in as_completed(futs,timeout=16):
161
  try:results[futs[f]]=f.result()
162
  except:results[futs[f]]=[]
163
  srcs=list(results.values());out=[];seen=set();mx=max((len(s) for s in srcs),default=0)
 
167
  return out[:limit]
168
  # Override article endpoint
169
  app.router.routes=[r for r in app.router.routes if not(getattr(r,'path',None)=='/api/article' and 'GET' in getattr(r,'methods',set()))]
170
+ def _jina_article(url):
171
+ """Read an article via r.jina.ai (for Cloudflare/SPA sites: vatvostudio, bongdaplus)."""
172
+ try:
173
+ r=req.get("https://r.jina.ai/"+url,headers={'Accept':'text/markdown','X-Return-Format':'markdown','User-Agent':'Mozilla/5.0'},timeout=35)
174
+ if r.status_code!=200 or not r.text:return None
175
+ lines=[x.rstrip() for x in r.text.splitlines()]
176
+ title='';og_img='';summary='';body=[]
177
+ for ln in lines[:40]:
178
+ if ln.startswith('Title:') and not title:title=ln.replace('Title:','',1).strip()
179
+ # first content image
180
+ for ln in lines:
181
+ mi=re.search(r'!\[[^\]]*\]\((https?://[^)]+)\)',ln)
182
+ if mi and 'wp-content' in mi.group(1) or (mi and 'media' in mi.group(1)):og_img=mi.group(1);break
183
+ intxt=False
184
+ for ln in lines:
185
+ if ln.startswith('Markdown Content:'):intxt=True;continue
186
+ if not intxt:continue
187
+ t=re.sub(r'!\[[^\]]*\]\([^)]+\)','',ln) # drop images
188
+ t=re.sub(r'\[([^\]]+)\]\([^)]+\)',r'\1',t) # unwrap links
189
+ t=re.sub(r'[#>*_`]+','',t).strip()
190
+ if len(t)>=40:body.append({'type':'p','text':t})
191
+ if len(body)>=60:break
192
+ if not title and body:title=body[0]['text'][:90]
193
+ if not body:return None
194
+ return {'title':_clean(title),'summary':_clean(body[0]['text'][:200]) if body else '','og_image':og_img,'body':body,'source':'jina','url':url}
195
+ except:return None
196
  def _scrape_generic(url):
197
  try:
198
+ r=req.get(url,headers={'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36','Accept-Language':'vi-VN,vi;q=0.9'},timeout=15,allow_redirects=True);r.encoding='utf-8'
199
+ # JS SPA / Cloudflare-protected sites -> read via Jina
200
+ if r.status_code>=400 or 'bongdaplus.vn' in url or 'vatvostudio.vn' in url or 'Just a moment' in r.text[:600]:
201
+ jr=_jina_article(url)
202
+ if jr and jr.get('body'):return jr
203
+ soup=BeautifulSoup(r.text,'lxml')
204
  for tag in soup.find_all(['script','style','nav','footer','aside','form','noscript']):tag.decompose()
205
  h1=soup.find('h1');ogt=soup.find('meta',property='og:title');title=(h1.get_text(strip=True) if h1 else '')or(ogt.get('content','') if ogt else '')
206
  ogd=soup.find('meta',property='og:description');summary=ogd.get('content','') if ogd else ''
 
219
  im=el if el.name=='img' else el.find('img')
220
  if im:src=im.get('data-src') or im.get('src') or'';(body.append({'type':'img','src':'https:'+src if src.startswith('//') else src}) if src and'base64' not in src else None)
221
  if not body and summary:body=[{'type':'p','text':summary}]
222
+ if not body:
223
+ jr=_jina_article(url)
224
+ if jr and jr.get('body'):return jr
225
  return{'title':_clean(title),'summary':_clean(summary),'og_image':og_img,'body':body[:50],'source':'generic','url':url}
226
+ except:
227
+ return _jina_article(url)
228
  @app.get('/api/article')
229
  def api_article_v2(url:str=Query(...)):
230
  from main import scrape_vne_article,scrape_bbc_article,scrape_dantri_article,scrape_genk_article,scrape_ttvh_article
 
255
  phrase=' '.join(words[i:i+n])
256
  if 8<=len(phrase)<=45:key=phrase.lower();freq[key]=freq.get(key,0)+1;display[key]=phrase
257
  except:continue
258
+ # Add bongda.com.vn + bongdaplus.vn (JS sites, no RSS) via Jina-scraped homepage titles
259
+ def _ingest_titles(titles):
260
+ for title in titles:
261
+ title=_clean(title)
262
+ if not title:continue
263
+ title=re.sub(r'\s*[-|].*$','',title)
264
+ words=[w for w in re.findall(r'[A-Za-zÀ-ỹ0-9]+',title) if len(w)>2 and w.lower() not in _STOP]
265
+ if len(words)<2:continue
266
+ for n in(3,4,2):
267
+ for i in range(max(0,len(words)-n+1)):
268
+ phrase=' '.join(words[i:i+n])
269
+ if 8<=len(phrase)<=45:key=phrase.lower();freq[key]=freq.get(key,0)+1;display[key]=phrase
270
+ try:
271
+ from main import scrape_bongda_jina,scrape_bongdaplus_jina
272
+ _ingest_titles([a.get('title','') for a in scrape_bongda_jina(20)])
273
+ _ingest_titles([a.get('title','') for a in scrape_bongdaplus_jina(20)])
274
+ except:pass
275
  ranked=sorted(freq.items(),key=lambda x:x[1],reverse=True);topics=[];seen=set()
276
  for key,count in ranked:
277
  is_dup=any(len(set(e.split())&set(key.split()))/max(len(set(e.split())),len(set(key.split())),1)>0.6 for e in seen)