dotagent-web-research 1.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. dotagent_web_research-1.2.0.dist-info/METADATA +330 -0
  2. dotagent_web_research-1.2.0.dist-info/RECORD +60 -0
  3. dotagent_web_research-1.2.0.dist-info/WHEEL +5 -0
  4. dotagent_web_research-1.2.0.dist-info/entry_points.txt +3 -0
  5. dotagent_web_research-1.2.0.dist-info/licenses/LICENSE +21 -0
  6. dotagent_web_research-1.2.0.dist-info/top_level.txt +1 -0
  7. web_scraper/__init__.py +4 -0
  8. web_scraper/ai_agent.py +203 -0
  9. web_scraper/api.py +1549 -0
  10. web_scraper/api_models.py +773 -0
  11. web_scraper/api_runtime.py +460 -0
  12. web_scraper/async_scrapers.py +392 -0
  13. web_scraper/cli.py +408 -0
  14. web_scraper/config.py +444 -0
  15. web_scraper/content_safety.py +189 -0
  16. web_scraper/duckduckgo_search.py +455 -0
  17. web_scraper/google_search.py +891 -0
  18. web_scraper/local_env.py +22 -0
  19. web_scraper/mcp_server.py +611 -0
  20. web_scraper/network_safety.py +110 -0
  21. web_scraper/playwright_scrapers.py +350 -0
  22. web_scraper/redis_cache.py +215 -0
  23. web_scraper/research/__init__.py +11 -0
  24. web_scraper/research/agent.py +2484 -0
  25. web_scraper/research/citation_verifier.py +236 -0
  26. web_scraper/research/constants.py +373 -0
  27. web_scraper/research/llm_client.py +136 -0
  28. web_scraper/research/models.py +40 -0
  29. web_scraper/research/profile_collectors.py +464 -0
  30. web_scraper/research/prompts.py +771 -0
  31. web_scraper/research/ranking.py +395 -0
  32. web_scraper/research/retry_utils.py +86 -0
  33. web_scraper/research/text_utils.py +326 -0
  34. web_scraper/research/url_utils.py +155 -0
  35. web_scraper/research_agent.py +13 -0
  36. web_scraper/scrapers.py +464 -0
  37. web_scraper/stealth.py +112 -0
  38. web_scraper/web_tools/__init__.py +5 -0
  39. web_scraper/web_tools/archive_fetcher.py +183 -0
  40. web_scraper/web_tools/browser.py +353 -0
  41. web_scraper/web_tools/budget.py +237 -0
  42. web_scraper/web_tools/content.py +770 -0
  43. web_scraper/web_tools/crawl.py +669 -0
  44. web_scraper/web_tools/crawl_store.py +180 -0
  45. web_scraper/web_tools/discovery.py +1294 -0
  46. web_scraper/web_tools/errors.py +63 -0
  47. web_scraper/web_tools/fetcher.py +407 -0
  48. web_scraper/web_tools/fields.py +534 -0
  49. web_scraper/web_tools/metrics.py +72 -0
  50. web_scraper/web_tools/pagecache.py +102 -0
  51. web_scraper/web_tools/pdf.py +164 -0
  52. web_scraper/web_tools/providers.py +71 -0
  53. web_scraper/web_tools/relevance.py +93 -0
  54. web_scraper/web_tools/robots.py +71 -0
  55. web_scraper/web_tools/search.py +321 -0
  56. web_scraper/web_tools/service.py +2050 -0
  57. web_scraper/web_tools/settings.py +366 -0
  58. web_scraper/web_tools/sources.py +388 -0
  59. web_scraper/web_tools/urls.py +219 -0
  60. web_scraper/yahoo_search.py +189 -0
@@ -0,0 +1,330 @@
1
+ Metadata-Version: 2.4
2
+ Name: dotagent-web-research
3
+ Version: 1.2.0
4
+ Summary: MCP server for web search and reading (search, scrape, map, extract, crawl) that Dot, an always-on agent for macOS, uses
5
+ Author-email: Senol Dogan <senoldogan@users.noreply.github.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/senoldogann/web-research-stack
8
+ Project-URL: Repository, https://github.com/senoldogann/web-research-stack.git
9
+ Project-URL: Issues, https://github.com/senoldogann/web-research-stack/issues
10
+ Project-URL: Documentation, https://github.com/senoldogann/web-research-stack/tree/main/docs
11
+ Keywords: mcp,model-context-protocol,web-search,web-scraping,crawler,dot,agent
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Requires-Python: >=3.9
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Requires-Dist: httpx>=0.26.0
23
+ Requires-Dist: curl_cffi>=0.7.3
24
+ Requires-Dist: beautifulsoup4>=4.12.0
25
+ Requires-Dist: lxml>=4.9.0
26
+ Requires-Dist: defusedxml>=0.7.1
27
+ Requires-Dist: fastapi>=0.109.0
28
+ Requires-Dist: uvicorn[standard]>=0.27.0
29
+ Requires-Dist: pydantic>=2.5.0
30
+ Requires-Dist: python-dotenv>=1.1.0
31
+ Requires-Dist: click>=8.1.0
32
+ Requires-Dist: tqdm>=4.66.0
33
+ Requires-Dist: pypdf>=5.1.0
34
+ Provides-Extra: dev
35
+ Requires-Dist: pytest>=7.4.0; extra == "dev"
36
+ Requires-Dist: pytest-asyncio>=0.23.0; extra == "dev"
37
+ Requires-Dist: pytest-cov>=4.1.0; extra == "dev"
38
+ Requires-Dist: httpx>=0.26.0; extra == "dev"
39
+ Requires-Dist: ruff>=0.1.0; extra == "dev"
40
+ Requires-Dist: redis[hiredis]>=5.0.0; extra == "dev"
41
+ Provides-Extra: playwright
42
+ Requires-Dist: playwright>=1.40.0; extra == "playwright"
43
+ Requires-Dist: playwright-stealth>=1.0.6; extra == "playwright"
44
+ Provides-Extra: camoufox
45
+ Requires-Dist: camoufox[geoip]>=0.4.0; extra == "camoufox"
46
+ Provides-Extra: redis
47
+ Requires-Dist: redis[hiredis]>=5.0.0; extra == "redis"
48
+ Dynamic: license-file
49
+
50
+ # Web Research Stack / Web Araştırma Yığını
51
+
52
+ <div align="center">
53
+ https://github.com/senoldogann/web-research-stack/raw/main/GITHUB-video.mp4
54
+ <br/>
55
+ <em>2-Minute Demonstration / 2 Dakikalık Tanıtım Videosu</em>
56
+ </div>
57
+
58
+ [English](#english) | [Türkçe](#türkçe)
59
+
60
+ ---
61
+
62
+ ## English
63
+
64
+ ### Overview
65
+
66
+ **Web Scraper** is a powerful, production-ready web scraping and research tool with full-stack capabilities. It combines a Python FastAPI backend with a modern React Next.js frontend, featuring LLM-powered research capabilities with citation support.
67
+
68
+ ### Install
69
+
70
+ The MCP server for web search and reading that Dot, the always-on agent for macOS, uses ships as a Python package, `dotagent-web-research` (the import package is `web_scraper`):
71
+
72
+ ```bash
73
+ pip install dotagent-web-research
74
+ # or, as a command of its own
75
+ uv tool install dotagent-web-research
76
+ ```
77
+
78
+ It installs two commands: `web-research-mcp`, the MCP server over stdio (tools `web_search`, `web_scrape`, `web_map`, `web_extract`, `web_crawl_start`, `web_crawl_status`, `web_crawl_results`, `web_crawl_cancel`), and `web-scraper`, the CLI and REST server described below. The headless-browser fallbacks are extras: `pip install "dotagent-web-research[camoufox]"` or `"[playwright]"`.
79
+
80
+ **How Dot uses it.** Dot's package depends on this one, so a fresh Dot install brings it. Dot starts `web-research-mcp` as one of its MCP servers (named `web-research`) and its tools reach the agent as `mcp__web-research__web_search`, `mcp__web-research__web_scrape` and so on. In Dot's `config.toml` it is one table; [docs/web-tools.md](docs/web-tools.md) has it and the settings (`WEB_TOOLS_*`) the server reads.
81
+
82
+ ```toml
83
+ [[mcp]]
84
+ name = "web-research"
85
+ command = "web-research-mcp"
86
+ ```
87
+
88
+ ### Features
89
+
90
+ - **Dual Interface**: CLI commands and REST API
91
+ - **Local Web Research Tools**: HTTP-first multi-angle search with field-weighted BM25, weighted angle consensus, freshness filters, caching and provider cooldown, plus scrape, map, background crawl, and structured extract, exposed over REST and a Dot-ready stdio MCP server (headless-only browser fallback)
92
+ - **LLM Research Tool**: AI-powered web research with citations
93
+ - **Async Architecture**: High-performance concurrent scraping
94
+ - **Production Ready**: Docker, monitoring (Prometheus/Grafana), rate limiting, circuit breaker
95
+ - **Security First**: SSRF protection, input validation, security headers
96
+ - **Multi-language UI**: English and Turkish language support
97
+ - **Streaming Support**: Real-time research progress via Server-Sent Events (SSE)
98
+ - **Code Block Preservation**: Scraper preserves code blocks from source pages with syntax highlighting
99
+ - **Internationalized Status Messages**: Research progress messages adapt to query language (TR/EN)
100
+ - **Resilient JSON Parsing**: 3-stage recovery for LLM synthesis output (strict → repair → retry)
101
+
102
+ ### Tech Stack
103
+
104
+ | Component | Technology |
105
+ |-----------|-------------|
106
+ | Backend | Python 3.9+, FastAPI, asyncio |
107
+ | Frontend | React 19, Next.js 15, TypeScript, Tailwind CSS |
108
+ | Database | SQLite (history & caching) |
109
+ | HTTP Client | httpx, curl_cffi |
110
+ | HTML Parsing | BeautifulSoup4, lxml |
111
+ | AI/ML | Ollama, OpenAI API compatible |
112
+
113
+ ### Quick Start
114
+
115
+ ```bash
116
+ # Clone the repository
117
+ git clone https://github.com/senoldogann/web-research-stack.git
118
+ cd web-research-stack
119
+
120
+ # Install dependencies
121
+ pip install -e .
122
+
123
+ # Start the API server
124
+ web-scraper serve
125
+
126
+ # Or use Docker
127
+ docker compose up --build
128
+ ```
129
+
130
+ ### Google search stays off your screen
131
+
132
+ Google no longer serves results to plain HTTP clients, and headless Chrome is caught within one request. A headed Chrome gets the real page, so the searcher starts Chrome itself, **minimises its window over CDP before the first search**, and keeps one browser and one tab for the process: ~2 s to start (a brief flash of a small window), then 0.4–1 s per search. If Chrome is missing or refused, **Camoufox** — a patched headless Firefox — takes over.
133
+
134
+ ```bash
135
+ pip install -e ".[playwright]" # the Chrome path
136
+ pip install -e ".[camoufox]" && python -m camoufox fetch # the headless fallback
137
+ ```
138
+
139
+ - `GOOGLE_ALLOW_VISIBLE_BROWSER=true` — leave the Chrome window on screen (debugging).
140
+ - `GOOGLE_USE_PLAYWRIGHT=false` — skip Google entirely and rely on DuckDuckGo.
141
+
142
+ ### API Endpoints
143
+
144
+ | Endpoint | Method | Description |
145
+ |----------|--------|-------------|
146
+ | `/` | GET | API information |
147
+ | `/health` | GET | Health check |
148
+ | `/api/v1/metrics` | GET | Prometheus metrics |
149
+ | `/api/v1/tools` | GET | Tool manifest for LLM |
150
+ | `/api/v1/scrape` | POST | Scrape a single URL |
151
+ | `/api/v1/scrape/batch` | POST | Batch scrape multiple URLs |
152
+ | `/api/v1/tools/web-research` | POST | Run AI-powered research |
153
+ | `/api/v1/tools/web-research/stream` | POST | Stream research progress |
154
+ | `/api/v1/web/search` | POST | Discover URLs with a search adapter |
155
+ | `/api/v1/web/scrape` | POST | Scrape one page to Markdown, metadata, and links |
156
+ | `/api/v1/web/map` | POST | List URLs from sitemaps and page links |
157
+ | `/api/v1/web/extract` | POST | Extract structured fields with provenance |
158
+ | `/api/v1/web/crawl` | POST | Start a background crawl and return its job id |
159
+ | `/api/v1/web/crawl/{job_id}` | GET | Crawl progress and counters |
160
+ | `/api/v1/web/crawl/{job_id}/results` | GET | Paginated crawl results |
161
+ | `/api/v1/web/crawl/{job_id}/cancel` | POST | Cancel a queued or in-flight crawl |
162
+ | `/api/v1/web/stats` | GET | Web tool latency, cache, and browser state |
163
+ | `/api/v1/research` | POST | Legacy research endpoint |
164
+ | `/api/v1/research/stream` | POST | Legacy streaming research |
165
+
166
+ ### Example: Research Request
167
+
168
+ ```bash
169
+ curl -X POST "http://127.0.0.1:8000/api/v1/tools/web-research" \
170
+ -H "Content-Type: application/json" \
171
+ -H "X-API-Key: your-api-key" \
172
+ -d '{
173
+ "query": "Latest developments in AI",
174
+ "max_sources": 5,
175
+ "deep_mode": false,
176
+ "provider": "ollama"
177
+ }'
178
+ ```
179
+
180
+ ### Documentation
181
+
182
+ - [Installation Guide](./docs/installation-tr.md#installation-guide)
183
+ - [User Guide](./docs/user-guide.md#english)
184
+ - [API Documentation](./docs/api.md#english)
185
+ - [LLM Integration](./docs/llm-tool-integration.md)
186
+ - [Local Web Tools](./docs/web-tools.md)
187
+ - [Production Deployment](./docs/production-deployment.md)
188
+
189
+ ### License
190
+
191
+ MIT License
192
+
193
+ ---
194
+
195
+ ## Türkçe
196
+
197
+ ### Genel Bakış
198
+
199
+ **Web Scraper**, güçlü ve üretim ortamına hazır bir web kazıma ve araştırma aracıdır. Python FastAPI backend ile modern React Next.js frontend'i birleştirir, alıntılı LLM destekli araştırma özelliklerine sahiptir.
200
+
201
+ ### Kurulum
202
+
203
+ Dot'un (macOS için her zaman açık ajan) kullandığı web arama ve okuma MCP sunucusu bir Python paketi olarak gelir: `dotagent-web-research` (içe aktarılan paket `web_scraper`):
204
+
205
+ ```bash
206
+ pip install dotagent-web-research
207
+ # veya kendi başına bir komut olarak
208
+ uv tool install dotagent-web-research
209
+ ```
210
+
211
+ İki komut kurar: stdio üzerinden MCP sunucusu `web-research-mcp` ve aşağıda anlatılan CLI ile REST sunucusu `web-scraper`. Headless tarayıcı yedekleri ek pakettir: `pip install "dotagent-web-research[camoufox]"` veya `"[playwright]"`.
212
+
213
+ **Dot bunu nasıl kullanır.** Dot'un paketi buna bağlıdır; yeni bir Dot kurulumu bunu da getirir. Dot `web-research-mcp`'yi MCP sunucularından biri olarak (`web-research` adıyla) başlatır; araçlar ajana `mcp__web-research__web_search`, `mcp__web-research__web_scrape` gibi görünür. Ayrıntı ve ayarlar için [docs/web-tools.md](docs/web-tools.md).
214
+
215
+ ### Özellikler
216
+
217
+ - **Çift Arayüz**: CLI komutları ve REST API
218
+ - **Yerel Web Araştırma Araçları**: Alan ağırlıklı BM25, ağırlıklı açı uzlaşması, güncellik filtreleri, önbellek ve sağlayıcı soğuması içeren çok açılı HTTP öncelikli arama; kazıma, haritalama, arka plan taraması ve yapılandırılmış çıkarım; REST ve Dot'a hazır stdio MCP sunucusu (yalnızca headless tarayıcı yedeği)
219
+ - **LLM Araştırma Aracı**: Alıntılı AI destekli web araştırması
220
+ - **Asenkron Mimari**: Yüksek performanslı eşzamanlı kazıma
221
+ - **Üretim Hazır**: Docker, izleme (Prometheus/Grafana), hız sınırlama, circuit breaker
222
+ - **Güvenlik Öncelikli**: SSRF koruması, giriş doğrulama, güvenlik başlıkları
223
+ - **Çok Dilli UI**: İngilizce ve Türkçe dil desteği
224
+ - **Yayın Akışı**: Sunucu Tarafından Gönderilen Olaylar (SSE) ile gerçek zamanlı araştırma ilerlemesi
225
+ - **Kod Bloğu Koruma**: Kaynak sayfalardan kod blokları söz dizimi vurgulamasıyla korunur
226
+ - **Uluslararasılaştırılmış Durum Mesajları**: Araştırma ilerleme mesajları sorgu diline göre uyarlanır (TR/EN)
227
+ - **Dayanıklı JSON Ayrıştırma**: LLM sentez çıktısı için 3 aşamalı kurtarma (katı → onarım → yeniden deneme)
228
+
229
+ ### Teknoloji Yığını
230
+
231
+ | Bileşen | Teknoloji |
232
+ |---------|-----------|
233
+ | Backend | Python 3.9+, FastAPI, asyncio |
234
+ | Frontend | React 19, Next.js 15, TypeScript, Tailwind CSS |
235
+ | Veritabanı | SQLite (geçmiş ve önbellek) |
236
+ | HTTP İstemcisi | httpx, curl_cffi |
237
+ | HTML Ayrıştırma | BeautifulSoup4, lxml |
238
+ | AI/ML | Ollama, OpenAI API uyumlu |
239
+
240
+ ### Hızlı Başlangıç
241
+
242
+ ```bash
243
+ # Depoyu klonla
244
+ git clone https://github.com/senoldogann/web-research-stack.git
245
+ cd web-research-stack
246
+
247
+ # Bağımlılıkları yükle
248
+ pip install -e .
249
+
250
+ # API sunucusunu başlat
251
+ web-scraper serve
252
+
253
+ # Veya Docker kullan
254
+ docker compose up --build
255
+ ```
256
+
257
+ ### Google araması ekranda görünmez
258
+
259
+ Google artık düz HTTP istemcilerine sonuç döndürmüyor ve headless Chrome tek istekte yakalanıyor. Başlı (headed) Chrome gerçek sayfayı alıyor; bu yüzden arayıcı Chrome'u kendisi başlatıp **penceresini ilk aramadan önce CDP ile küçültüyor** ve süreç boyunca tek tarayıcı + tek sekme kullanıyor: başlatma ~2 sn (küçük bir pencere kısa süre görünür), sonrası arama başına 0,4–1 sn. Chrome yoksa ya da reddederse **Camoufox** — yamalı, headless Firefox — devralıyor.
260
+
261
+ ```bash
262
+ pip install -e ".[playwright]" # Chrome yolu
263
+ pip install -e ".[camoufox]" && python -m camoufox fetch # görünmez yedek
264
+ ```
265
+
266
+ - `GOOGLE_ALLOW_VISIBLE_BROWSER=true` — Chrome penceresi ekranda kalsın (hata ayıklama).
267
+ - `GOOGLE_USE_PLAYWRIGHT=false` — Google'ı tamamen atla, DuckDuckGo kullan.
268
+
269
+ ### API Uç Noktaları
270
+
271
+ | Uç Nokta | Metod | Açıklama |
272
+ |----------|-------|----------|
273
+ | `/` | GET | API bilgileri |
274
+ | `/health` | GET | Sağlık kontrolü |
275
+ | `/api/v1/metrics` | GET | Prometheus metrikleri |
276
+ | `/api/v1/tools` | GET | LLM için araç manifestosu |
277
+ | `/api/v1/scrape` | POST | Tek URL kazıma |
278
+ | `/api/v1/scrape/batch` | POST | Toplu URL kazıma |
279
+ | `/api/v1/tools/web-research` | POST | AI destekli araştırma |
280
+ | `/api/v1/tools/web-research/stream` | POST | Araştırma ilerlemesi yayını |
281
+ | `/api/v1/web/search` | POST | Arama adaptörüyle URL keşfi |
282
+ | `/api/v1/web/scrape` | POST | Tek sayfayı Markdown, üst veri ve bağlantılara dönüştür |
283
+ | `/api/v1/web/map` | POST | Site haritaları ve sayfa bağlantılarından URL listele |
284
+ | `/api/v1/web/extract` | POST | Kaynak bilgisiyle yapılandırılmış alan çıkar |
285
+ | `/api/v1/web/crawl` | POST | Arka plan taraması başlat, iş kimliğini hemen döndür |
286
+ | `/api/v1/web/crawl/{job_id}` | GET | Tarama ilerlemesi ve sayaçları |
287
+ | `/api/v1/web/crawl/{job_id}/results` | GET | Sayfalanmış tarama sonuçları |
288
+ | `/api/v1/web/crawl/{job_id}/cancel` | POST | Sıradaki veya sürmekte olan taramayı iptal et |
289
+ | `/api/v1/web/stats` | GET | Web aracı gecikmesi, önbellek ve tarayıcı durumu |
290
+ | `/api/v1/research` | POST | Eski araştırma uç noktası |
291
+ | `/api/v1/research/stream` | POST | Eski yayın akışı araştırması |
292
+
293
+ ### Örnek: Araştırma İsteği
294
+
295
+ ```bash
296
+ curl -X POST "http://127.0.0.1:8000/api/v1/tools/web-research" \
297
+ -H "Content-Type: application/json" \
298
+ -H "X-API-Key: sizin-api-anahtariniz" \
299
+ -d '{
300
+ "query": "Yapay zeka son gelişmeler",
301
+ "max_sources": 5,
302
+ "deep_mode": false,
303
+ "provider": "ollama"
304
+ }'
305
+ ```
306
+
307
+ ### Dokümantasyon
308
+
309
+ - [Kurulum Rehberi](./docs/installation-tr.md#kurulum-rehberi)
310
+ - [Kullanım Kılavuzu](./docs/user-guide.md#türkçe)
311
+ - [API Dokümantasyonu](./docs/api.md#türkçe)
312
+ - [LLM Entegrasyonu](./docs/llm-tool-integration.md)
313
+ - [Yerel Web Araçları](./docs/web-tools.md)
314
+ - [Üretim Dağıtımı](./docs/production-deployment.md)
315
+
316
+ ### Lisans
317
+
318
+ MIT Lisansı
319
+
320
+ ---
321
+
322
+ ## Contributing / Katkıda Bulunma
323
+
324
+ Contributions are welcome! Please read our [Contributing Guide](./CONTRIBUTING.md).
325
+
326
+ Katkılarınızı bekliyoruz! Lütfen [Katkı Rehberimizi](./CONTRIBUTING.md) okuyun.
327
+
328
+ ---
329
+
330
+ &copy; 2026 Senol Dogan. MIT License.
@@ -0,0 +1,60 @@
1
+ dotagent_web_research-1.2.0.dist-info/licenses/LICENSE,sha256=97vp88nO0uKuooYG2QSz0rNfjuhHcMMij_dtgZn2guk,1068
2
+ web_scraper/__init__.py,sha256=GJbvh_9N3M0uqnw8QgHhJoyGWYhD6JjLQLf43ZJF-qY,123
3
+ web_scraper/ai_agent.py,sha256=_Kks-ps0vsw8R1tH9oFz2QybI8IBn8makEpgXAB5Hyk,6923
4
+ web_scraper/api.py,sha256=U0oemES_Llqx9gh9DOvYF_bkXT5OeQFFYfTDwpvBCW4,59546
5
+ web_scraper/api_models.py,sha256=ZEtQcPYrs5GrHt_pFPlzcDDnDy1Y3NVCxlWcDPZr1tM,25917
6
+ web_scraper/api_runtime.py,sha256=17q8pyNjZJqHMb_5Q2uolCXM2zAbF5y7UcuLQkB3wIk,16352
7
+ web_scraper/async_scrapers.py,sha256=C2_Sc1aILlqj1goeow1vUKxnoOHZkodK-LX8ooOtnLQ,14061
8
+ web_scraper/cli.py,sha256=CEbcafI1aDNq6lH6RLZZOaK0tLIwFh6WO_wp9-uBarc,11555
9
+ web_scraper/config.py,sha256=wWmcFM2nglJ_7mG_C27x4t-z-krZoCfQ5UYMT951s90,19097
10
+ web_scraper/content_safety.py,sha256=zs6BlWBbLRW7j-AU3VZ5W4N4ku8Xa-XpgFogLIoYR4o,6591
11
+ web_scraper/duckduckgo_search.py,sha256=sA4sDuFPE54M4Zbw4zW0qxTFfrJXYslzn4a-0G2Ua1Q,16599
12
+ web_scraper/google_search.py,sha256=oA7n5pUYJXzFPAerHElZGyGfhcowbo5Uz2it-wUvLag,33781
13
+ web_scraper/local_env.py,sha256=JugcZkJ5MjNhnAq7gukl1JnWFbAKPgpTtQCHtCktnMQ,951
14
+ web_scraper/mcp_server.py,sha256=7zZLcR3F8ICM6VTgLNgJk6RRYrnC1DOXvG6Sn63aXwE,24947
15
+ web_scraper/network_safety.py,sha256=xB-4cwjZn6TEt4JZj6YLppLQ_q0zcHePW-bBDPE3neU,3557
16
+ web_scraper/playwright_scrapers.py,sha256=eyMGPcUkxTKrhaVcIqDDtjsJnli8Da0T-Awaay1QnOc,12309
17
+ web_scraper/redis_cache.py,sha256=VoAlwxAchJ5remLIkS9Q_BQ4uN7jWrNGfkLMAcrnlQM,7610
18
+ web_scraper/research_agent.py,sha256=1r9cuvRxv5FQzj3GVrKu7lfu5Z9ju33UEk6mW44SMQo,474
19
+ web_scraper/scrapers.py,sha256=tbp16ZTw_3770yMyuYmc3TPm5k_eKUmIhRNGVm_5Zgc,15968
20
+ web_scraper/stealth.py,sha256=mfdb2x5OIteHVPdon11b7yU8qiX6REAabWfQJ5oe4jc,4746
21
+ web_scraper/yahoo_search.py,sha256=YpBHIOFUYCWJAqHE6hNKsIsiBiB_vDap-2e4wp7LzSQ,8164
22
+ web_scraper/research/__init__.py,sha256=UpZ9S3JM7OSAK5HRp5cfjaYtBAry70hZR1ipCduUSms,359
23
+ web_scraper/research/agent.py,sha256=YFl5M5p1uZMYzSFameU3s38yhhEzjxxv7exBsyrVkAg,96949
24
+ web_scraper/research/citation_verifier.py,sha256=xLA3WOT4Kcmmw5JrifFFNhkRzd9TzhMLMfhLMv482e4,6349
25
+ web_scraper/research/constants.py,sha256=8R2UTpW5bp7KEEg9ULnJfBneQpA6NtufwqgMBbUVbaY,13903
26
+ web_scraper/research/llm_client.py,sha256=GFy1C9a2j8fTrKkZfqbIoIfPr5OGaSJbcoVK_Dcex1A,5151
27
+ web_scraper/research/models.py,sha256=BcDlAwv0g6KogaO6lRgV5M14qaLvX9w7p-NkTQXhjPM,1185
28
+ web_scraper/research/profile_collectors.py,sha256=kEiIvl7znmdnUy6wJIbSxhP2EGc8oWXcVd9_B0OC7JA,17406
29
+ web_scraper/research/prompts.py,sha256=0kRRd83_XQAYH9APo8cuaibS2k4aCvbq2MJfSXNGb34,34855
30
+ web_scraper/research/ranking.py,sha256=D9rnP4HooMIOMhcg1bPV49Nn3Otex-bL4B-DnGbVEXs,13220
31
+ web_scraper/research/retry_utils.py,sha256=wwE5W8mT5HF4_Es-uBXXe_RC1mbq3lehLfpCOlpDhDU,2527
32
+ web_scraper/research/text_utils.py,sha256=VTzUPXnyRSs7W_u762rN0bJlMUEBQFg6f686heyBaWY,8999
33
+ web_scraper/research/url_utils.py,sha256=tJfvCr6SByhmwtt_70yFIPCso3HCvxcotKj2XIveBWo,4223
34
+ web_scraper/web_tools/__init__.py,sha256=oKm85LqRlpR-Qf2CqtyV5Yic_ebZUQVUH1fCV690cf8,231
35
+ web_scraper/web_tools/archive_fetcher.py,sha256=8_5VGKijga8ugTuelCHwFEJPRZhaJDOMuyT8RWj0mF8,7518
36
+ web_scraper/web_tools/browser.py,sha256=mpyA8LDWhaZp0FbxMO-72xODdfOZqG11K9dsbM0k49k,12587
37
+ web_scraper/web_tools/budget.py,sha256=dlWTkU5-sIRirNedMkAv8bFURQIBeAMMWISkwNRcgMI,8603
38
+ web_scraper/web_tools/content.py,sha256=mzCxfo2RiJEgJmTTIO_hlwF2te445qVAfuRilGLUWc4,27268
39
+ web_scraper/web_tools/crawl.py,sha256=xlrgN-ea829Hu4i-CwwXz3WYKHgWgV681yFugCIR1v0,25742
40
+ web_scraper/web_tools/crawl_store.py,sha256=ZIabI-JJDk022NcB0wGP3H7CEK7GU8dOGoUHaOSgkrM,6700
41
+ web_scraper/web_tools/discovery.py,sha256=pnKBrz3pBIeLNCioqta3g7dZ4ph04YLngWnGD4iH0tM,41838
42
+ web_scraper/web_tools/errors.py,sha256=iRpiNT3oC5b3EQPKKK6p0wg0oeP141gILZxxzirZ4Uw,1627
43
+ web_scraper/web_tools/fetcher.py,sha256=eq3rtW-YjyXBBc0ODp5BLyeB20InDE4d6xkYdsgj3KA,15857
44
+ web_scraper/web_tools/fields.py,sha256=-aKmYI2QxQSm8LCF5oCN-nV_V6KEjET9pEbY5hhLUG0,18603
45
+ web_scraper/web_tools/metrics.py,sha256=BHXrGU1T99RcIV0UtlPBy5lCiaNK4VlzlNeRrX4bs08,2523
46
+ web_scraper/web_tools/pagecache.py,sha256=99kaYf1UCIIDuodU_ejvulfRx4LLjIp3SpQpKtfQMgk,3693
47
+ web_scraper/web_tools/pdf.py,sha256=RMHmEvGpGb4AXYwf1BHxr0xTT3ZHi0jpBa4vKOFOmw4,5811
48
+ web_scraper/web_tools/providers.py,sha256=SYNyz0t1yeicphiTYOdh8QAkPTVAYLtJR9c24cWxi6g,2490
49
+ web_scraper/web_tools/relevance.py,sha256=u3g3YIjpMHL1xqA95ufAoTgiUo55iaFTcPFK2f9kRBE,3398
50
+ web_scraper/web_tools/robots.py,sha256=yJOxRbuRRS-TY1gi8obFdF5Rtinwxn7aRrcqn0aubiU,2500
51
+ web_scraper/web_tools/search.py,sha256=qhVrhfU4N2Cr4NcXcP6SunSqIdmjV-5p1wxKTWTTDb8,10554
52
+ web_scraper/web_tools/service.py,sha256=7Bg52eiGVNMEbC1Azkb2cYZLq-hHCfzM6lb3D7kiOEc,81863
53
+ web_scraper/web_tools/settings.py,sha256=guMDA0Kqst1UWycbH9ysy3vhTpugBf_qB1OtPhFi1oc,17335
54
+ web_scraper/web_tools/sources.py,sha256=tyDCWOrr2ES2UZwzBGoQZjUJQyfW3LqfxG8jTZDI2Ss,9160
55
+ web_scraper/web_tools/urls.py,sha256=zgOVmmgZsSHrfTZJ4X7QcyCSNiixA6r-CIUV1P06DE8,7337
56
+ dotagent_web_research-1.2.0.dist-info/METADATA,sha256=SgndqzxZg7I-s9yzyICcfh3CK3O3plIoeoUllZTlR44,14717
57
+ dotagent_web_research-1.2.0.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
58
+ dotagent_web_research-1.2.0.dist-info/entry_points.txt,sha256=xdDR2nX0ek-BRCuNK3hDbn8v4rQm95Hf9ZlptbdBuLc,100
59
+ dotagent_web_research-1.2.0.dist-info/top_level.txt,sha256=0ynyaW_79oDijS6kQjJfk67X11tO-Qm6XfuhhuziCkk,12
60
+ dotagent_web_research-1.2.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.2.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ web-research-mcp = web_scraper.mcp_server:main
3
+ web-scraper = web_scraper.cli:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Senol Dogan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ web_scraper
@@ -0,0 +1,4 @@
1
+ """Web Scraper - A powerful web scraping tool with CLI and API support."""
2
+
3
+ __version__ = "1.2.0"
4
+ __author__ = "Developer"
@@ -0,0 +1,203 @@
1
+ """AI Agent for natural language web scraping commands using Ollama."""
2
+
3
+ import json
4
+ import re
5
+ from typing import Optional
6
+
7
+ import httpx
8
+
9
+ from web_scraper.config import config
10
+
11
+
12
+ class OllamaAgent:
13
+ """AI agent that processes natural language commands for web scraping."""
14
+
15
+ def __init__(self, model: Optional[str] = None, host: Optional[str] = None):
16
+ """
17
+ Initialize the Ollama agent.
18
+
19
+ Args:
20
+ model: Ollama model name (default: gpt-oss:120b-cloud)
21
+ host: Ollama API host (default: http://localhost:11434)
22
+ """
23
+ self.model = model or config.default_research_model
24
+ self.host = host or config.ollama_host
25
+ self.api_url = f"{self.host}/api/generate"
26
+
27
+ async def process_command(self, command: str) -> dict:
28
+ """
29
+ Process a natural language command and return scraping parameters.
30
+
31
+ Args:
32
+ command: Natural language command (e.g., "scrape anthropic site and get me the last 10 news")
33
+
34
+ Returns:
35
+ Dictionary with URL, scraping mode, and filters
36
+ """
37
+ prompt = self._create_prompt(command)
38
+
39
+ try:
40
+ async with httpx.AsyncClient() as client:
41
+ response = await client.post(
42
+ self.api_url,
43
+ json={
44
+ "model": self.model,
45
+ "prompt": prompt,
46
+ "stream": False,
47
+ },
48
+ timeout=config.research_source_selection_timeout_seconds,
49
+ )
50
+ response.raise_for_status()
51
+
52
+ result = response.json()
53
+ ai_response = result.get("response", "")
54
+
55
+ # Parse the AI response
56
+ return self._parse_response(ai_response, command)
57
+
58
+ except Exception:
59
+ # Fallback to manual parsing if AI fails
60
+ return self._fallback_parse(command)
61
+
62
+ def _create_prompt(self, command: str) -> str:
63
+ """Create a prompt for the AI model."""
64
+ return f"""You are a web scraping assistant. Extract the following information from the user's command and return ONLY a JSON object:
65
+
66
+ User command: "{command}"
67
+
68
+ Extract:
69
+ 1. url: The website URL to scrape (add https:// if missing)
70
+ 2. mode: "playwright" if JavaScript-heavy site (like React/Vue/Angular) or protected by Cloudflare, otherwise "basic"
71
+ 3. max_items: Number of items to extract (news, articles, etc.) if specified
72
+ 4. filters: Any specific content to look for (news, blog posts, products, etc.)
73
+
74
+ Return ONLY this JSON format:
75
+ {{
76
+ "url": "https://example.com",
77
+ "mode": "basic",
78
+ "max_items": 10,
79
+ "filters": "news"
80
+ }}
81
+
82
+ If URL cannot be determined, use empty string. If max_items not specified, use 0 (all)."""
83
+
84
+ def _parse_response(self, response: str, original_command: str) -> dict:
85
+ """Parse the AI response into a structured dictionary."""
86
+ try:
87
+ # Try to extract JSON from the response
88
+ json_match = re.search(r"\{[^}]*\}", response, re.DOTALL)
89
+ if json_match:
90
+ data = json.loads(json_match.group())
91
+ return {
92
+ "url": data.get("url", ""),
93
+ "mode": data.get("mode", "basic"),
94
+ "max_items": data.get("max_items", 0),
95
+ "filters": data.get("filters", ""),
96
+ "original_command": original_command,
97
+ }
98
+ except json.JSONDecodeError:
99
+ pass
100
+
101
+ return self._fallback_parse(original_command)
102
+
103
+ def _fallback_parse(self, command: str) -> dict:
104
+ """Fallback parser when AI is not available."""
105
+ # Extract URL from command
106
+ url_pattern = r"(https?://[^\s]+|www\.[^\s]+|[a-zA-Z0-9-]+\.(com|org|net|io|dev|co)[^\s]*)"
107
+ url_match = re.search(url_pattern, command)
108
+
109
+ url = ""
110
+ if url_match:
111
+ url = url_match.group(1)
112
+ if not url.startswith("http"):
113
+ url = f"https://{url}"
114
+
115
+ # Extract number
116
+ number_match = re.search(r"(\d+)\s*(news|articles|posts|items)?", command, re.IGNORECASE)
117
+ max_items = int(number_match.group(1)) if number_match else 0
118
+
119
+ # Determine if playwright needed
120
+ mode = (
121
+ "playwright"
122
+ if any(
123
+ word in command.lower() for word in ["javascript", "react", "dynamic", "cloudflare"]
124
+ )
125
+ else "basic"
126
+ )
127
+
128
+ # Extract filters
129
+ filters = ""
130
+ if "news" in command.lower():
131
+ filters = "news"
132
+ elif "blog" in command.lower():
133
+ filters = "blog"
134
+ elif "article" in command.lower():
135
+ filters = "articles"
136
+
137
+ return {
138
+ "url": url,
139
+ "mode": mode,
140
+ "max_items": max_items,
141
+ "filters": filters,
142
+ "original_command": command,
143
+ }
144
+
145
+ async def summarize_content(self, content: str, filters: str = "", max_items: int = 0) -> str:
146
+ """
147
+ Summarize scraped content using AI.
148
+
149
+ Args:
150
+ content: The scraped content
151
+ filters: Type of content to focus on
152
+ max_items: Maximum number of items to extract
153
+
154
+ Returns:
155
+ Formatted summary
156
+ """
157
+ # Truncate content if too long
158
+ if len(content) > config.research_deep_content_limit_chars:
159
+ content = content[: config.research_deep_content_limit_chars] + "..."
160
+
161
+ prompt = f"""Analyze this website content and extract the key information{f" about {filters}" if filters else ""}.
162
+
163
+ {f"Extract up to {max_items} items if applicable." if max_items > 0 else ""}
164
+
165
+ Content:
166
+ {content}
167
+
168
+ Format your response as a clean, structured output with:
169
+ 1. Main topics/sections found
170
+ 2. Key information points
171
+ 3. Any lists or enumerated items
172
+
173
+ Keep it concise and well-organized."""
174
+
175
+ try:
176
+ async with httpx.AsyncClient() as client:
177
+ response = await client.post(
178
+ self.api_url,
179
+ json={
180
+ "model": self.model,
181
+ "prompt": prompt,
182
+ "stream": False,
183
+ },
184
+ timeout=config.research_synthesis_timeout_seconds,
185
+ )
186
+ response.raise_for_status()
187
+
188
+ result = response.json()
189
+ return result.get("response", "Could not generate summary.")
190
+
191
+ except Exception as e:
192
+ return f"Error generating summary: {str(e)}"
193
+
194
+ def is_available(self) -> bool:
195
+ """Check if Ollama is running. Stays sync — called before the event loop."""
196
+ try:
197
+ response = httpx.get(
198
+ f"{self.host}/api/tags",
199
+ timeout=min(5.0, config.research_planning_timeout_seconds),
200
+ )
201
+ return response.status_code == 200
202
+ except Exception:
203
+ return False