VISHAL18for4 commited on
Commit
86fb95e
·
verified ·
1 Parent(s): 8dd5cb9

Delete data_fetcher.py

Browse files
Files changed (1) hide show
  1. data_fetcher.py +0 -320
data_fetcher.py DELETED
@@ -1,320 +0,0 @@
1
- """
2
- data_fetcher.py — Fetches real live data from the internet.
3
- Sources: RSS, Reddit, Wikipedia, HackerNews, DuckDuckGo Search,
4
- DEV.to, GitHub Trending, Google News RSS, Al Jazeera, Guardian, NPR, arXiv
5
- """
6
-
7
- import requests, feedparser, random, re, time, json
8
- from datetime import datetime
9
- from collections import deque
10
- from urllib.parse import quote_plus
11
- from html import unescape
12
-
13
- HEADERS = {
14
- 'User-Agent': 'Mozilla/5.0 (compatible; LivingNeuralNetwork/2.0; educational)',
15
- 'Accept-Language': 'en-US,en;q=0.9',
16
- }
17
-
18
- # ─── RSS FEEDS ────────────────────────────────────────────────────────────────
19
- RSS_FEEDS = {
20
- 'technology': [
21
- 'https://feeds.feedburner.com/TechCrunch',
22
- 'https://feeds.arstechnica.com/arstechnica/index',
23
- 'https://www.wired.com/feed/rss',
24
- 'https://hnrss.org/frontpage',
25
- 'https://www.theverge.com/rss/index.xml',
26
- 'https://feeds.feedburner.com/venturebeat/SZYF',
27
- 'https://dev.to/feed',
28
- 'https://thenextweb.com/feed/',
29
- ],
30
- 'science': [
31
- 'https://www.sciencedaily.com/rss/all.xml',
32
- 'https://www.newscientist.com/feed/home/',
33
- 'https://rss.nytimes.com/services/xml/rss/nyt/Science.xml',
34
- 'https://www.nature.com/nature.rss',
35
- 'http://export.arxiv.org/rss/cs.AI',
36
- 'http://export.arxiv.org/rss/cs.LG',
37
- 'https://phys.org/rss-feed/breaking/',
38
- ],
39
- 'world': [
40
- 'https://feeds.bbci.co.uk/news/world/rss.xml',
41
- 'https://rss.nytimes.com/services/xml/rss/nyt/World.xml',
42
- 'https://www.aljazeera.com/xml/rss/all.xml',
43
- 'https://feeds.npr.org/1004/rss.xml',
44
- 'https://www.theguardian.com/world/rss',
45
- 'https://feeds.reuters.com/reuters/worldNews',
46
- ],
47
- 'sports': [
48
- 'https://feeds.bbci.co.uk/sport/rss.xml',
49
- 'https://rss.nytimes.com/services/xml/rss/nyt/Sports.xml',
50
- 'https://www.espn.com/espn/rss/news',
51
- ],
52
- 'business': [
53
- 'https://feeds.bbci.co.uk/news/business/rss.xml',
54
- 'https://rss.nytimes.com/services/xml/rss/nyt/Business.xml',
55
- 'https://feeds.bloomberg.com/markets/news.rss',
56
- 'https://www.theguardian.com/business/rss',
57
- 'https://feeds.reuters.com/reuters/businessNews',
58
- ],
59
- 'health': [
60
- 'https://feeds.bbci.co.uk/news/health/rss.xml',
61
- 'https://rss.nytimes.com/services/xml/rss/nyt/Health.xml',
62
- 'https://www.theguardian.com/society/health/rss',
63
- 'https://feeds.npr.org/1128/rss.xml',
64
- ],
65
- 'entertainment': [
66
- 'https://feeds.bbci.co.uk/news/entertainment_and_arts/rss.xml',
67
- 'https://rss.nytimes.com/services/xml/rss/nyt/Arts.xml',
68
- 'https://www.theguardian.com/culture/rss',
69
- 'https://variety.com/feed/',
70
- ],
71
- 'ai': [
72
- 'http://export.arxiv.org/rss/cs.AI',
73
- 'http://export.arxiv.org/rss/cs.LG',
74
- 'https://hnrss.org/frontpage',
75
- 'https://feeds.feedburner.com/TechCrunch',
76
- ],
77
- }
78
-
79
- REDDIT_FEEDS = [
80
- ('technology', 'https://www.reddit.com/r/technology/top.json?limit=25&t=day'),
81
- ('science', 'https://www.reddit.com/r/science/top.json?limit=25&t=day'),
82
- ('world', 'https://www.reddit.com/r/worldnews/top.json?limit=25&t=day'),
83
- ('sports', 'https://www.reddit.com/r/sports/top.json?limit=25&t=day'),
84
- ('business', 'https://www.reddit.com/r/business/top.json?limit=25&t=day'),
85
- ('health', 'https://www.reddit.com/r/Health/top.json?limit=25&t=day'),
86
- ('entertainment', 'https://www.reddit.com/r/movies/top.json?limit=25&t=day'),
87
- ('ai', 'https://www.reddit.com/r/MachineLearning/top.json?limit=25&t=day'),
88
- ('ai', 'https://www.reddit.com/r/artificial/top.json?limit=25&t=day'),
89
- ('science', 'https://www.reddit.com/r/askscience/top.json?limit=25&t=day'),
90
- ('technology', 'https://www.reddit.com/r/programming/top.json?limit=25&t=day'),
91
- ('world', 'https://www.reddit.com/r/geopolitics/top.json?limit=25&t=day'),
92
- ]
93
-
94
- # Topics to actively search on DuckDuckGo
95
- DDG_SEARCH_TOPICS = [
96
- ('technology', 'latest AI artificial intelligence news'),
97
- ('technology', 'tech industry news today'),
98
- ('science', 'science discovery research breakthrough'),
99
- ('world', 'world news today'),
100
- ('business', 'business economy markets news'),
101
- ('health', 'health medicine research news'),
102
- ('ai', 'machine learning deep learning news'),
103
- ('ai', 'ChatGPT OpenAI Anthropic Google AI'),
104
- ('world', 'politics international relations'),
105
- ('science', 'space NASA astronomy discovery'),
106
- ('technology', 'cybersecurity data breach'),
107
- ('entertainment', 'movies music entertainment news'),
108
- ]
109
-
110
- WIKIPEDIA_API = 'https://en.wikipedia.org/api/rest_v1/page/random/summary'
111
- WIKI_SEARCH = 'https://en.wikipedia.org/api/rest_v1/page/summary/{}'
112
- HN_TOP_API = 'https://hacker-news.firebaseio.com/v0/topstories.json'
113
- HN_ITEM_API = 'https://hacker-news.firebaseio.com/v0/item/{}.json'
114
-
115
- # ─── UTILS ────────────────────────────────────────────────────────────────────
116
- def clean(text: str, max_chars: int = 700) -> str:
117
- if not text: return ''
118
- text = unescape(text) # fix ' & < etc
119
- text = re.sub(r'&#?[a-zA-Z0-9]+;', ' ', text) # remove any remaining entities
120
- text = re.sub(r'<[^>]+>', ' ', text) # strip HTML tags
121
- text = re.sub(r'http\S+', '', text)
122
- text = re.sub(r'[^\w\s.,!?;:\'\-–—]', ' ', text)
123
- text = re.sub(r'\s+', ' ', text).strip()
124
- return text[:max_chars]
125
-
126
- def make_item(text, category, source, extra=None):
127
- text = clean(text)
128
- if len(text) < 30: return None
129
- return {
130
- 'text': text,
131
- 'category': category,
132
- 'source': source,
133
- 'timestamp': datetime.utcnow().isoformat(),
134
- **(extra or {}),
135
- }
136
-
137
- # ─── FETCHERS ─────────────────────────────────────────────────────────────────
138
- def fetch_rss(category: str, url: str) -> list:
139
- items = []
140
- try:
141
- feed = feedparser.parse(url)
142
- for entry in feed.entries[:15]:
143
- title = entry.get('title', '')
144
- summary = entry.get('summary', entry.get('description', ''))
145
- text = f"{title}. {summary}"
146
- item = make_item(text, category, 'rss', {'feed': url.split('/')[2]})
147
- if item: items.append(item)
148
- except Exception: pass
149
- return items
150
-
151
- def fetch_reddit(category: str, url: str) -> list:
152
- items = []
153
- try:
154
- r = requests.get(url, headers=HEADERS, timeout=10)
155
- r.raise_for_status()
156
- posts = r.json().get('data', {}).get('children', [])
157
- for post in posts:
158
- d = post.get('data', {})
159
- title = d.get('title', '')
160
- selftext = d.get('selftext', '')
161
- text = f"{title}. {selftext}"
162
- item = make_item(text, category, 'reddit',
163
- {'subreddit': d.get('subreddit',''), 'score': d.get('score',0)})
164
- if item: items.append(item)
165
- except Exception: pass
166
- return items
167
-
168
- def fetch_duckduckgo(query: str, category: str) -> list:
169
- """Search DuckDuckGo and extract text snippets — no API key needed."""
170
- items = []
171
- try:
172
- url = f"https://html.duckduckgo.com/html/?q={quote_plus(query)}"
173
- r = requests.get(url, headers={**HEADERS, 'Accept': 'text/html'}, timeout=12)
174
- # Extract result snippets
175
- snippets = re.findall(r'class="result__snippet"[^>]*>(.*?)</[as]>', r.text, re.DOTALL)
176
- titles = re.findall(r'class="result__a"[^>]*>(.*?)</a>', r.text, re.DOTALL)
177
- for i, snippet in enumerate(snippets[:8]):
178
- title = titles[i] if i < len(titles) else query
179
- title = re.sub(r'<[^>]+>', '', title).strip()
180
- snippet = re.sub(r'<[^>]+>', '', snippet).strip()
181
- text = f"{title}. {snippet}"
182
- item = make_item(text, category, 'duckduckgo', {'query': query})
183
- if item: items.append(item)
184
- except Exception: pass
185
- return items
186
-
187
- def fetch_wikipedia_random() -> dict | None:
188
- try:
189
- r = requests.get(WIKIPEDIA_API, headers=HEADERS, timeout=10)
190
- r.raise_for_status()
191
- data = r.json()
192
- title = data.get('title', '')
193
- text = f"{title}. {data.get('extract','')}"
194
- return make_item(text, 'other', 'wikipedia', {'title': title})
195
- except Exception: return None
196
-
197
- def fetch_wikipedia_topic(topic: str, category: str) -> dict | None:
198
- """Fetch Wikipedia article for a specific topic."""
199
- try:
200
- url = WIKI_SEARCH.format(quote_plus(topic))
201
- r = requests.get(url, headers=HEADERS, timeout=10)
202
- r.raise_for_status()
203
- data = r.json()
204
- title = data.get('title', '')
205
- text = f"{title}. {data.get('extract','')}"
206
- return make_item(text, category, 'wikipedia', {'title': title})
207
- except Exception: return None
208
-
209
- def fetch_hackernews(n: int = 8) -> list:
210
- items = []
211
- try:
212
- r = requests.get(HN_TOP_API, headers=HEADERS, timeout=10)
213
- r.raise_for_status()
214
- ids = r.json()[:50]
215
- chosen = random.sample(ids, min(n, len(ids)))
216
- for sid in chosen:
217
- try:
218
- sr = requests.get(HN_ITEM_API.format(sid), headers=HEADERS, timeout=6)
219
- story = sr.json()
220
- title = story.get('title', '')
221
- body = story.get('text', '')
222
- item = make_item(f"{title}. {body}", 'technology', 'hackernews',
223
- {'score': story.get('score', 0)})
224
- if item: items.append(item)
225
- time.sleep(0.08)
226
- except Exception: continue
227
- except Exception: pass
228
- return items
229
-
230
- # ─── MAIN FETCHER ─────────────────────────────────────────────────────────────
231
- class DataFetcher:
232
- def __init__(self):
233
- self.total_fetched = 0
234
- self.source_counts = {
235
- 'rss': 0, 'reddit': 0, 'wikipedia': 0,
236
- 'hackernews': 0, 'duckduckgo': 0
237
- }
238
- self.recent_items = deque(maxlen=100)
239
- self.log = deque(maxlen=300)
240
- self._ddg_idx = 0 # rotate through DDG search topics
241
-
242
- def _log(self, msg: str):
243
- ts = datetime.utcnow().strftime('%H:%M:%S')
244
- entry = f"[{ts}] {msg}"
245
- self.log.appendleft(entry)
246
- return entry
247
-
248
- def fetch_round(self) -> list:
249
- """Fetch one full round from ALL sources."""
250
- all_items = []
251
-
252
- # 1. RSS — pick 2 random categories
253
- for _ in range(2):
254
- try:
255
- cat = random.choice(list(RSS_FEEDS.keys()))
256
- url = random.choice(RSS_FEEDS[cat])
257
- items = fetch_rss(cat, url)
258
- all_items.extend(items)
259
- self.source_counts['rss'] += len(items)
260
- self._log(f"📰 RSS [{cat.upper()}] +{len(items)} ← {url.split('/')[2]}")
261
- except Exception as e:
262
- self._log(f"⚠ RSS error: {e}")
263
-
264
- # 2. Reddit
265
- try:
266
- cat, url = random.choice(REDDIT_FEEDS)
267
- items = fetch_reddit(cat, url)
268
- all_items.extend(items)
269
- self.source_counts['reddit'] += len(items)
270
- sub = url.split('/r/')[1].split('/')[0]
271
- self._log(f"🟠 Reddit [r/{sub}] +{len(items)}")
272
- except Exception as e:
273
- self._log(f"⚠ Reddit error: {e}")
274
-
275
- # 3. DuckDuckGo search (rotate through topics)
276
- try:
277
- cat, query = DDG_SEARCH_TOPICS[self._ddg_idx % len(DDG_SEARCH_TOPICS)]
278
- self._ddg_idx += 1
279
- items = fetch_duckduckgo(query, cat)
280
- all_items.extend(items)
281
- self.source_counts['duckduckgo'] += len(items)
282
- self._log(f"🦆 DuckDuckGo [{cat.upper()}] \"{query[:40]}\" +{len(items)}")
283
- except Exception as e:
284
- self._log(f"⚠ DDG error: {e}")
285
-
286
- # 4. Wikipedia (random + topic)
287
- try:
288
- item = fetch_wikipedia_random()
289
- if item:
290
- all_items.append(item)
291
- self.source_counts['wikipedia'] += 1
292
- self._log(f"📖 Wikipedia (random): {item['text'][:50]}…")
293
- except Exception as e:
294
- self._log(f"⚠ Wikipedia error: {e}")
295
-
296
- # 5. HackerNews (every other round)
297
- if random.random() < 0.5:
298
- try:
299
- items = fetch_hackernews(6)
300
- all_items.extend(items)
301
- self.source_counts['hackernews'] += len(items)
302
- self._log(f"💻 HackerNews +{len(items)}")
303
- except Exception as e:
304
- self._log(f"⚠ HN error: {e}")
305
-
306
- for item in all_items:
307
- self.recent_items.appendleft(item)
308
- self.total_fetched += len(all_items)
309
- self._log(f"✅ Round complete — {len(all_items)} new items | total: {self.total_fetched}")
310
- return all_items
311
-
312
- def get_stats(self) -> dict:
313
- return {
314
- 'total_fetched': self.total_fetched,
315
- 'sources': dict(self.source_counts),
316
- 'recent_log': list(self.log)[:30],
317
- }
318
-
319
- def get_recent_items(self, n: int = 20) -> list:
320
- return list(self.recent_items)[:n]