webscout 8.2.2__py3-none-any.whl → 8.2.7__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.

Potentially problematic release.


This version of webscout might be problematic. Click here for more details.

Files changed (306) hide show
  1. webscout/AIauto.py +112 -22
  2. webscout/AIbase.py +144 -7
  3. webscout/AIutel.py +249 -131
  4. webscout/Bard.py +579 -206
  5. webscout/DWEBS.py +78 -35
  6. webscout/__init__.py +0 -1
  7. webscout/cli.py +256 -0
  8. webscout/conversation.py +307 -436
  9. webscout/exceptions.py +23 -0
  10. webscout/prompt_manager.py +56 -42
  11. webscout/version.py +1 -1
  12. webscout/webscout_search.py +65 -47
  13. webscout/webscout_search_async.py +81 -126
  14. webscout/yep_search.py +93 -43
  15. {webscout-8.2.2.dist-info → webscout-8.2.7.dist-info}/METADATA +172 -52
  16. webscout-8.2.7.dist-info/RECORD +26 -0
  17. {webscout-8.2.2.dist-info → webscout-8.2.7.dist-info}/WHEEL +1 -1
  18. webscout-8.2.7.dist-info/entry_points.txt +3 -0
  19. webscout-8.2.7.dist-info/top_level.txt +1 -0
  20. inferno/__init__.py +0 -6
  21. inferno/__main__.py +0 -9
  22. inferno/cli.py +0 -6
  23. webscout/Extra/GitToolkit/__init__.py +0 -10
  24. webscout/Extra/GitToolkit/gitapi/__init__.py +0 -12
  25. webscout/Extra/GitToolkit/gitapi/repository.py +0 -195
  26. webscout/Extra/GitToolkit/gitapi/user.py +0 -96
  27. webscout/Extra/GitToolkit/gitapi/utils.py +0 -62
  28. webscout/Extra/YTToolkit/YTdownloader.py +0 -957
  29. webscout/Extra/YTToolkit/__init__.py +0 -3
  30. webscout/Extra/YTToolkit/transcriber.py +0 -476
  31. webscout/Extra/YTToolkit/ytapi/__init__.py +0 -6
  32. webscout/Extra/YTToolkit/ytapi/channel.py +0 -307
  33. webscout/Extra/YTToolkit/ytapi/errors.py +0 -13
  34. webscout/Extra/YTToolkit/ytapi/extras.py +0 -45
  35. webscout/Extra/YTToolkit/ytapi/https.py +0 -88
  36. webscout/Extra/YTToolkit/ytapi/patterns.py +0 -61
  37. webscout/Extra/YTToolkit/ytapi/playlist.py +0 -59
  38. webscout/Extra/YTToolkit/ytapi/pool.py +0 -8
  39. webscout/Extra/YTToolkit/ytapi/query.py +0 -40
  40. webscout/Extra/YTToolkit/ytapi/stream.py +0 -63
  41. webscout/Extra/YTToolkit/ytapi/utils.py +0 -62
  42. webscout/Extra/YTToolkit/ytapi/video.py +0 -232
  43. webscout/Extra/__init__.py +0 -7
  44. webscout/Extra/autocoder/__init__.py +0 -9
  45. webscout/Extra/autocoder/autocoder.py +0 -849
  46. webscout/Extra/autocoder/autocoder_utiles.py +0 -332
  47. webscout/Extra/gguf.py +0 -682
  48. webscout/Extra/tempmail/__init__.py +0 -28
  49. webscout/Extra/tempmail/async_utils.py +0 -141
  50. webscout/Extra/tempmail/base.py +0 -161
  51. webscout/Extra/tempmail/cli.py +0 -187
  52. webscout/Extra/tempmail/emailnator.py +0 -84
  53. webscout/Extra/tempmail/mail_tm.py +0 -361
  54. webscout/Extra/tempmail/temp_mail_io.py +0 -292
  55. webscout/Extra/weather.py +0 -194
  56. webscout/Extra/weather_ascii.py +0 -76
  57. webscout/LLM.py +0 -442
  58. webscout/Litlogger/__init__.py +0 -67
  59. webscout/Litlogger/core/__init__.py +0 -6
  60. webscout/Litlogger/core/level.py +0 -23
  61. webscout/Litlogger/core/logger.py +0 -165
  62. webscout/Litlogger/handlers/__init__.py +0 -12
  63. webscout/Litlogger/handlers/console.py +0 -33
  64. webscout/Litlogger/handlers/file.py +0 -143
  65. webscout/Litlogger/handlers/network.py +0 -173
  66. webscout/Litlogger/styles/__init__.py +0 -7
  67. webscout/Litlogger/styles/colors.py +0 -249
  68. webscout/Litlogger/styles/formats.py +0 -458
  69. webscout/Litlogger/styles/text.py +0 -87
  70. webscout/Litlogger/utils/__init__.py +0 -6
  71. webscout/Litlogger/utils/detectors.py +0 -153
  72. webscout/Litlogger/utils/formatters.py +0 -200
  73. webscout/Local/__init__.py +0 -12
  74. webscout/Local/__main__.py +0 -9
  75. webscout/Local/api.py +0 -576
  76. webscout/Local/cli.py +0 -516
  77. webscout/Local/config.py +0 -75
  78. webscout/Local/llm.py +0 -287
  79. webscout/Local/model_manager.py +0 -253
  80. webscout/Local/server.py +0 -721
  81. webscout/Local/utils.py +0 -93
  82. webscout/Provider/AI21.py +0 -177
  83. webscout/Provider/AISEARCH/DeepFind.py +0 -250
  84. webscout/Provider/AISEARCH/ISou.py +0 -256
  85. webscout/Provider/AISEARCH/Perplexity.py +0 -359
  86. webscout/Provider/AISEARCH/__init__.py +0 -10
  87. webscout/Provider/AISEARCH/felo_search.py +0 -228
  88. webscout/Provider/AISEARCH/genspark_search.py +0 -208
  89. webscout/Provider/AISEARCH/hika_search.py +0 -194
  90. webscout/Provider/AISEARCH/iask_search.py +0 -436
  91. webscout/Provider/AISEARCH/monica_search.py +0 -246
  92. webscout/Provider/AISEARCH/scira_search.py +0 -324
  93. webscout/Provider/AISEARCH/webpilotai_search.py +0 -281
  94. webscout/Provider/Aitopia.py +0 -292
  95. webscout/Provider/AllenAI.py +0 -413
  96. webscout/Provider/Andi.py +0 -228
  97. webscout/Provider/Blackboxai.py +0 -229
  98. webscout/Provider/C4ai.py +0 -432
  99. webscout/Provider/ChatGPTClone.py +0 -226
  100. webscout/Provider/ChatGPTES.py +0 -237
  101. webscout/Provider/ChatGPTGratis.py +0 -194
  102. webscout/Provider/Chatify.py +0 -175
  103. webscout/Provider/Cloudflare.py +0 -273
  104. webscout/Provider/Cohere.py +0 -208
  105. webscout/Provider/DeepSeek.py +0 -196
  106. webscout/Provider/Deepinfra.py +0 -297
  107. webscout/Provider/ElectronHub.py +0 -709
  108. webscout/Provider/ExaAI.py +0 -261
  109. webscout/Provider/ExaChat.py +0 -342
  110. webscout/Provider/Free2GPT.py +0 -241
  111. webscout/Provider/GPTWeb.py +0 -193
  112. webscout/Provider/Gemini.py +0 -169
  113. webscout/Provider/GithubChat.py +0 -367
  114. webscout/Provider/Glider.py +0 -211
  115. webscout/Provider/Groq.py +0 -670
  116. webscout/Provider/HF_space/__init__.py +0 -0
  117. webscout/Provider/HF_space/qwen_qwen2.py +0 -206
  118. webscout/Provider/HeckAI.py +0 -233
  119. webscout/Provider/HuggingFaceChat.py +0 -462
  120. webscout/Provider/Hunyuan.py +0 -272
  121. webscout/Provider/Jadve.py +0 -266
  122. webscout/Provider/Koboldai.py +0 -381
  123. webscout/Provider/LambdaChat.py +0 -392
  124. webscout/Provider/Llama.py +0 -200
  125. webscout/Provider/Llama3.py +0 -204
  126. webscout/Provider/Marcus.py +0 -148
  127. webscout/Provider/Netwrck.py +0 -228
  128. webscout/Provider/OLLAMA.py +0 -396
  129. webscout/Provider/OPENAI/__init__.py +0 -25
  130. webscout/Provider/OPENAI/base.py +0 -46
  131. webscout/Provider/OPENAI/c4ai.py +0 -367
  132. webscout/Provider/OPENAI/chatgpt.py +0 -549
  133. webscout/Provider/OPENAI/chatgptclone.py +0 -460
  134. webscout/Provider/OPENAI/deepinfra.py +0 -272
  135. webscout/Provider/OPENAI/e2b.py +0 -1350
  136. webscout/Provider/OPENAI/exaai.py +0 -404
  137. webscout/Provider/OPENAI/exachat.py +0 -433
  138. webscout/Provider/OPENAI/freeaichat.py +0 -352
  139. webscout/Provider/OPENAI/glider.py +0 -316
  140. webscout/Provider/OPENAI/heckai.py +0 -337
  141. webscout/Provider/OPENAI/llmchatco.py +0 -327
  142. webscout/Provider/OPENAI/netwrck.py +0 -348
  143. webscout/Provider/OPENAI/opkfc.py +0 -488
  144. webscout/Provider/OPENAI/scirachat.py +0 -463
  145. webscout/Provider/OPENAI/sonus.py +0 -294
  146. webscout/Provider/OPENAI/standardinput.py +0 -425
  147. webscout/Provider/OPENAI/textpollinations.py +0 -285
  148. webscout/Provider/OPENAI/toolbaz.py +0 -405
  149. webscout/Provider/OPENAI/typegpt.py +0 -346
  150. webscout/Provider/OPENAI/uncovrAI.py +0 -455
  151. webscout/Provider/OPENAI/utils.py +0 -211
  152. webscout/Provider/OPENAI/venice.py +0 -413
  153. webscout/Provider/OPENAI/wisecat.py +0 -381
  154. webscout/Provider/OPENAI/writecream.py +0 -156
  155. webscout/Provider/OPENAI/x0gpt.py +0 -371
  156. webscout/Provider/OPENAI/yep.py +0 -327
  157. webscout/Provider/OpenGPT.py +0 -199
  158. webscout/Provider/Openai.py +0 -496
  159. webscout/Provider/PI.py +0 -344
  160. webscout/Provider/Perplexitylabs.py +0 -415
  161. webscout/Provider/Phind.py +0 -535
  162. webscout/Provider/PizzaGPT.py +0 -198
  163. webscout/Provider/QwenLM.py +0 -254
  164. webscout/Provider/Reka.py +0 -214
  165. webscout/Provider/StandardInput.py +0 -278
  166. webscout/Provider/TTI/AiForce/__init__.py +0 -22
  167. webscout/Provider/TTI/AiForce/async_aiforce.py +0 -224
  168. webscout/Provider/TTI/AiForce/sync_aiforce.py +0 -245
  169. webscout/Provider/TTI/FreeAIPlayground/__init__.py +0 -9
  170. webscout/Provider/TTI/FreeAIPlayground/async_freeaiplayground.py +0 -181
  171. webscout/Provider/TTI/FreeAIPlayground/sync_freeaiplayground.py +0 -180
  172. webscout/Provider/TTI/ImgSys/__init__.py +0 -23
  173. webscout/Provider/TTI/ImgSys/async_imgsys.py +0 -202
  174. webscout/Provider/TTI/ImgSys/sync_imgsys.py +0 -195
  175. webscout/Provider/TTI/MagicStudio/__init__.py +0 -2
  176. webscout/Provider/TTI/MagicStudio/async_magicstudio.py +0 -111
  177. webscout/Provider/TTI/MagicStudio/sync_magicstudio.py +0 -109
  178. webscout/Provider/TTI/Nexra/__init__.py +0 -22
  179. webscout/Provider/TTI/Nexra/async_nexra.py +0 -286
  180. webscout/Provider/TTI/Nexra/sync_nexra.py +0 -258
  181. webscout/Provider/TTI/PollinationsAI/__init__.py +0 -23
  182. webscout/Provider/TTI/PollinationsAI/async_pollinations.py +0 -311
  183. webscout/Provider/TTI/PollinationsAI/sync_pollinations.py +0 -265
  184. webscout/Provider/TTI/__init__.py +0 -12
  185. webscout/Provider/TTI/aiarta/__init__.py +0 -2
  186. webscout/Provider/TTI/aiarta/async_aiarta.py +0 -482
  187. webscout/Provider/TTI/aiarta/sync_aiarta.py +0 -440
  188. webscout/Provider/TTI/artbit/__init__.py +0 -22
  189. webscout/Provider/TTI/artbit/async_artbit.py +0 -155
  190. webscout/Provider/TTI/artbit/sync_artbit.py +0 -148
  191. webscout/Provider/TTI/fastflux/__init__.py +0 -22
  192. webscout/Provider/TTI/fastflux/async_fastflux.py +0 -261
  193. webscout/Provider/TTI/fastflux/sync_fastflux.py +0 -252
  194. webscout/Provider/TTI/huggingface/__init__.py +0 -22
  195. webscout/Provider/TTI/huggingface/async_huggingface.py +0 -199
  196. webscout/Provider/TTI/huggingface/sync_huggingface.py +0 -195
  197. webscout/Provider/TTI/piclumen/__init__.py +0 -23
  198. webscout/Provider/TTI/piclumen/async_piclumen.py +0 -268
  199. webscout/Provider/TTI/piclumen/sync_piclumen.py +0 -233
  200. webscout/Provider/TTI/pixelmuse/__init__.py +0 -4
  201. webscout/Provider/TTI/pixelmuse/async_pixelmuse.py +0 -249
  202. webscout/Provider/TTI/pixelmuse/sync_pixelmuse.py +0 -182
  203. webscout/Provider/TTI/talkai/__init__.py +0 -4
  204. webscout/Provider/TTI/talkai/async_talkai.py +0 -229
  205. webscout/Provider/TTI/talkai/sync_talkai.py +0 -207
  206. webscout/Provider/TTS/__init__.py +0 -7
  207. webscout/Provider/TTS/deepgram.py +0 -156
  208. webscout/Provider/TTS/elevenlabs.py +0 -111
  209. webscout/Provider/TTS/gesserit.py +0 -127
  210. webscout/Provider/TTS/murfai.py +0 -113
  211. webscout/Provider/TTS/parler.py +0 -111
  212. webscout/Provider/TTS/speechma.py +0 -180
  213. webscout/Provider/TTS/streamElements.py +0 -333
  214. webscout/Provider/TTS/utils.py +0 -280
  215. webscout/Provider/TeachAnything.py +0 -187
  216. webscout/Provider/TextPollinationsAI.py +0 -231
  217. webscout/Provider/TwoAI.py +0 -199
  218. webscout/Provider/Venice.py +0 -219
  219. webscout/Provider/VercelAI.py +0 -234
  220. webscout/Provider/WebSim.py +0 -228
  221. webscout/Provider/WiseCat.py +0 -196
  222. webscout/Provider/Writecream.py +0 -211
  223. webscout/Provider/WritingMate.py +0 -197
  224. webscout/Provider/Youchat.py +0 -330
  225. webscout/Provider/__init__.py +0 -198
  226. webscout/Provider/ai4chat.py +0 -202
  227. webscout/Provider/aimathgpt.py +0 -189
  228. webscout/Provider/akashgpt.py +0 -342
  229. webscout/Provider/askmyai.py +0 -158
  230. webscout/Provider/asksteve.py +0 -203
  231. webscout/Provider/bagoodex.py +0 -145
  232. webscout/Provider/cerebras.py +0 -242
  233. webscout/Provider/chatglm.py +0 -205
  234. webscout/Provider/cleeai.py +0 -213
  235. webscout/Provider/copilot.py +0 -428
  236. webscout/Provider/elmo.py +0 -234
  237. webscout/Provider/freeaichat.py +0 -271
  238. webscout/Provider/gaurish.py +0 -244
  239. webscout/Provider/geminiapi.py +0 -208
  240. webscout/Provider/geminiprorealtime.py +0 -160
  241. webscout/Provider/granite.py +0 -187
  242. webscout/Provider/hermes.py +0 -219
  243. webscout/Provider/julius.py +0 -223
  244. webscout/Provider/koala.py +0 -268
  245. webscout/Provider/labyrinth.py +0 -340
  246. webscout/Provider/learnfastai.py +0 -266
  247. webscout/Provider/lepton.py +0 -194
  248. webscout/Provider/llama3mitril.py +0 -180
  249. webscout/Provider/llamatutor.py +0 -192
  250. webscout/Provider/llmchat.py +0 -213
  251. webscout/Provider/llmchatco.py +0 -311
  252. webscout/Provider/meta.py +0 -794
  253. webscout/Provider/multichat.py +0 -325
  254. webscout/Provider/promptrefine.py +0 -193
  255. webscout/Provider/scira_chat.py +0 -277
  256. webscout/Provider/scnet.py +0 -187
  257. webscout/Provider/searchchat.py +0 -293
  258. webscout/Provider/sonus.py +0 -208
  259. webscout/Provider/talkai.py +0 -194
  260. webscout/Provider/toolbaz.py +0 -320
  261. webscout/Provider/turboseek.py +0 -219
  262. webscout/Provider/tutorai.py +0 -252
  263. webscout/Provider/typefully.py +0 -280
  264. webscout/Provider/typegpt.py +0 -232
  265. webscout/Provider/uncovr.py +0 -312
  266. webscout/Provider/x0gpt.py +0 -256
  267. webscout/Provider/yep.py +0 -376
  268. webscout/litagent/__init__.py +0 -29
  269. webscout/litagent/agent.py +0 -455
  270. webscout/litagent/constants.py +0 -60
  271. webscout/litprinter/__init__.py +0 -59
  272. webscout/scout/__init__.py +0 -8
  273. webscout/scout/core/__init__.py +0 -7
  274. webscout/scout/core/crawler.py +0 -140
  275. webscout/scout/core/scout.py +0 -568
  276. webscout/scout/core/search_result.py +0 -96
  277. webscout/scout/core/text_analyzer.py +0 -63
  278. webscout/scout/core/text_utils.py +0 -277
  279. webscout/scout/core/web_analyzer.py +0 -52
  280. webscout/scout/core.py +0 -881
  281. webscout/scout/element.py +0 -460
  282. webscout/scout/parsers/__init__.py +0 -69
  283. webscout/scout/parsers/html5lib_parser.py +0 -172
  284. webscout/scout/parsers/html_parser.py +0 -236
  285. webscout/scout/parsers/lxml_parser.py +0 -178
  286. webscout/scout/utils.py +0 -37
  287. webscout/swiftcli/__init__.py +0 -809
  288. webscout/zeroart/__init__.py +0 -55
  289. webscout/zeroart/base.py +0 -60
  290. webscout/zeroart/effects.py +0 -99
  291. webscout/zeroart/fonts.py +0 -816
  292. webscout-8.2.2.dist-info/RECORD +0 -309
  293. webscout-8.2.2.dist-info/entry_points.txt +0 -5
  294. webscout-8.2.2.dist-info/top_level.txt +0 -3
  295. webstoken/__init__.py +0 -30
  296. webstoken/classifier.py +0 -189
  297. webstoken/keywords.py +0 -216
  298. webstoken/language.py +0 -128
  299. webstoken/ner.py +0 -164
  300. webstoken/normalizer.py +0 -35
  301. webstoken/processor.py +0 -77
  302. webstoken/sentiment.py +0 -206
  303. webstoken/stemmer.py +0 -73
  304. webstoken/tagger.py +0 -60
  305. webstoken/tokenizer.py +0 -158
  306. {webscout-8.2.2.dist-info → webscout-8.2.7.dist-info/licenses}/LICENSE.md +0 -0
@@ -11,7 +11,7 @@ from time import time
11
11
  from types import TracebackType
12
12
  from typing import Any, Dict, List, Optional, Type, Union, cast, AsyncIterator
13
13
 
14
- import httpx
14
+ import curl_cffi.requests
15
15
  from lxml.etree import _Element
16
16
  from lxml.html import HTMLParser as LHTMLParser
17
17
  from lxml.html import document_fromstring
@@ -31,18 +31,14 @@ from .utils import (
31
31
  class AsyncWEBS:
32
32
  """Asynchronous webscout class to get search results."""
33
33
 
34
+ # curl_cffi supports different browser versions than httpx
34
35
  _impersonates = (
35
- "chrome_100", "chrome_101", "chrome_104", "chrome_105", "chrome_106", "chrome_107",
36
- "chrome_108", "chrome_109", "chrome_114", "chrome_116", "chrome_117", "chrome_118",
37
- "chrome_119", "chrome_120", "chrome_123", "chrome_124", "chrome_126", "chrome_127",
38
- "chrome_128", "chrome_129", "chrome_130", "chrome_131", "chrome_133",
39
- "safari_ios_16.5", "safari_ios_17.2", "safari_ios_17.4.1", "safari_ios_18.1.1",
40
- "safari_15.3", "safari_15.5", "safari_15.6.1", "safari_16", "safari_16.5",
41
- "safari_17.0", "safari_17.2.1", "safari_17.4.1", "safari_17.5",
42
- "safari_18", "safari_18.2",
43
- "safari_ipad_18",
44
- "edge_101", "edge_122", "edge_127", "edge_131",
45
- "firefox_109", "firefox_117", "firefox_128", "firefox_133", "firefox_135",
36
+ "chrome99", "chrome100", "chrome101", "chrome104", "chrome107", "chrome110",
37
+ "chrome116", "chrome119", "chrome120", "chrome123", "chrome124", "chrome131", "chrome133a",
38
+ "chrome99_android", "chrome131_android",
39
+ "safari15_3", "safari15_5", "safari17_0", "safari17_2_ios", "safari18_0", "safari18_0_ios",
40
+ "edge99", "edge101",
41
+ "firefox133", "firefox135",
46
42
  )
47
43
  _impersonates_os = ("android", "ios", "linux", "macos", "windows")
48
44
  _chat_models = {
@@ -94,11 +90,14 @@ class AsyncWEBS:
94
90
  self.headers = headers if headers else {}
95
91
  self.headers.update(default_headers)
96
92
 
97
- self.client = httpx.AsyncClient(
93
+ # Use curl_cffi AsyncSession instead of httpx.AsyncClient
94
+ impersonate_browser = choice(self._impersonates)
95
+ self.timeout = timeout
96
+ self.client = curl_cffi.requests.AsyncSession(
98
97
  headers=self.headers,
99
- proxies=self.proxy,
98
+ proxies={'http': self.proxy, 'https': self.proxy} if self.proxy else None,
100
99
  timeout=timeout,
101
- follow_redirects=False,
100
+ impersonate=impersonate_browser,
102
101
  verify=verify,
103
102
  )
104
103
  self.sleep_timestamp = 0.0
@@ -147,17 +146,33 @@ class AsyncWEBS:
147
146
  """Make HTTP request with proper rate limiting."""
148
147
  await self._sleep()
149
148
  try:
150
- resp = await self.client.request(
151
- method,
152
- url,
153
- params=params,
154
- content=content,
155
- data=data,
156
- headers=headers,
157
- cookies=cookies,
158
- json=json,
159
- timeout=timeout or self.timeout,
160
- )
149
+ # curl_cffi doesn't accept cookies=True in request methods
150
+ request_kwargs = {
151
+ "params": params,
152
+ "headers": headers,
153
+ "json": json,
154
+ "timeout": timeout or self.timeout,
155
+ }
156
+
157
+ # Add cookies if they're a dict, not a bool
158
+ if isinstance(cookies, dict):
159
+ request_kwargs["cookies"] = cookies
160
+
161
+ if method == "GET":
162
+ # curl_cffi uses data instead of content
163
+ if content:
164
+ request_kwargs["data"] = content
165
+ resp = await self.client.get(url, **request_kwargs)
166
+ elif method == "POST":
167
+ # handle both data and content
168
+ if data or content:
169
+ request_kwargs["data"] = data or content
170
+ resp = await self.client.post(url, **request_kwargs)
171
+ else:
172
+ # handle both data and content
173
+ if data or content:
174
+ request_kwargs["data"] = data or content
175
+ resp = await self.client.request(method, url, **request_kwargs)
161
176
  except Exception as ex:
162
177
  if "time" in str(ex).lower():
163
178
  raise TimeoutE(f"{url} {type(ex).__name__}: {ex}") from ex
@@ -278,7 +293,8 @@ class AsyncWEBS:
278
293
  self._chat_vqd_hash = resp.headers.get("x-vqd-hash-1", "")
279
294
  chunks = []
280
295
 
281
- async for chunk in resp.aiter_bytes():
296
+ # curl_cffi uses aiter_content instead of aiter_bytes
297
+ async for chunk in resp.aiter_content(chunk_size=1024):
282
298
  lines = chunk.split(b"data:")
283
299
  for line in lines:
284
300
  if line := line.strip():
@@ -286,20 +302,24 @@ class AsyncWEBS:
286
302
  break
287
303
  if line == b"[DONE][LIMIT_CONVERSATION]":
288
304
  raise ConversationLimitException("ERR_CONVERSATION_LIMIT")
289
- x = json_loads(line)
290
- if isinstance(x, dict):
291
- if x.get("action") == "error":
292
- err_message = x.get("type", "")
293
- if x.get("status") == 429:
294
- raise (
295
- ConversationLimitException(err_message)
296
- if err_message == "ERR_CONVERSATION_LIMIT"
297
- else RatelimitE(err_message)
298
- )
299
- raise WebscoutE(err_message)
300
- elif message := x.get("message"):
301
- chunks.append(message)
302
- yield message
305
+ try:
306
+ x = json_loads(line)
307
+ if isinstance(x, dict):
308
+ if x.get("action") == "error":
309
+ err_message = x.get("type", "")
310
+ if x.get("status") == 429:
311
+ raise (
312
+ ConversationLimitException(err_message)
313
+ if err_message == "ERR_CONVERSATION_LIMIT"
314
+ else RatelimitE(err_message)
315
+ )
316
+ raise WebscoutE(err_message)
317
+ elif message := x.get("message"):
318
+ chunks.append(message)
319
+ yield message
320
+ except Exception:
321
+ # Skip invalid JSON data
322
+ continue
303
323
 
304
324
  # If we get here, the request was successful
305
325
  result = "".join(chunks)
@@ -421,7 +441,8 @@ class AsyncWEBS:
421
441
  if b"No results." in resp_content:
422
442
  return results
423
443
 
424
- tree = document_fromstring(resp_content, self.parser)
444
+ # curl_cffi returns bytes, not a file-like object
445
+ tree = document_fromstring(resp_content)
425
446
  elements = tree.xpath("//div[h2]")
426
447
  if not isinstance(elements, list):
427
448
  return results
@@ -494,7 +515,8 @@ class AsyncWEBS:
494
515
  if b"No more results." in resp_content:
495
516
  return results
496
517
 
497
- tree = document_fromstring(resp_content, self.parser)
518
+ # curl_cffi returns bytes, not a file-like object
519
+ tree = document_fromstring(resp_content)
498
520
  elements = tree.xpath("//table[last()]//tr")
499
521
  if not isinstance(elements, list):
500
522
  return results
@@ -583,21 +605,8 @@ class AsyncWEBS:
583
605
  RatelimitE: Inherits from WebscoutE, raised for exceeding API request rate limits.
584
606
  TimeoutE: Inherits from WebscoutE, raised for API request timeouts.
585
607
  """
586
- result = await self._loop.run_in_executor(
587
- self._executor,
588
- super().images,
589
- keywords,
590
- region,
591
- safesearch,
592
- timelimit,
593
- size,
594
- color,
595
- type_image,
596
- layout,
597
- license_image,
598
- max_results,
599
- )
600
- return result
608
+ # These methods are not implemented in the async version yet
609
+ raise NotImplementedError("aimages method is not implemented yet")
601
610
 
602
611
  async def avideos(
603
612
  self,
@@ -630,19 +639,8 @@ class AsyncWEBS:
630
639
  RatelimitE: Inherits from WebscoutE, raised for exceeding API request rate limits.
631
640
  TimeoutE: Inherits from WebscoutE, raised for API request timeouts.
632
641
  """
633
- result = await self._loop.run_in_executor(
634
- self._executor,
635
- super().videos,
636
- keywords,
637
- region,
638
- safesearch,
639
- timelimit,
640
- resolution,
641
- duration,
642
- license_videos,
643
- max_results,
644
- )
645
- return result
642
+ # These methods are not implemented in the async version yet
643
+ raise NotImplementedError("avideos method is not implemented yet")
646
644
 
647
645
  async def anews(
648
646
  self,
@@ -669,16 +667,8 @@ class AsyncWEBS:
669
667
  RatelimitE: Inherits from WebscoutE, raised for exceeding API request rate limits.
670
668
  TimeoutE: Inherits from WebscoutE, raised for API request timeouts.
671
669
  """
672
- result = await self._loop.run_in_executor(
673
- self._executor,
674
- super().news,
675
- keywords,
676
- region,
677
- safesearch,
678
- timelimit,
679
- max_results,
680
- )
681
- return result
670
+ # These methods are not implemented in the async version yet
671
+ raise NotImplementedError("anews method is not implemented yet")
682
672
 
683
673
  async def aanswers(
684
674
  self,
@@ -697,12 +687,8 @@ class AsyncWEBS:
697
687
  RatelimitE: Inherits from WebscoutE, raised for exceeding API request rate limits.
698
688
  TimeoutE: Inherits from WebscoutE, raised for API request timeouts.
699
689
  """
700
- result = await self._loop.run_in_executor(
701
- self._executor,
702
- super().answers,
703
- keywords,
704
- )
705
- return result
690
+ # These methods are not implemented in the async version yet
691
+ raise NotImplementedError("aanswers method is not implemented yet")
706
692
 
707
693
  async def asuggestions(
708
694
  self,
@@ -723,13 +709,8 @@ class AsyncWEBS:
723
709
  RatelimitE: Inherits from WebscoutE, raised for exceeding API request rate limits.
724
710
  TimeoutE: Inherits from WebscoutE, raised for API request timeouts.
725
711
  """
726
- result = await self._loop.run_in_executor(
727
- self._executor,
728
- super().suggestions,
729
- keywords,
730
- region,
731
- )
732
- return result
712
+ # These methods are not implemented in the async version yet
713
+ raise NotImplementedError("asuggestions method is not implemented yet")
733
714
 
734
715
  async def amaps(
735
716
  self,
@@ -771,23 +752,8 @@ class AsyncWEBS:
771
752
  RatelimitE: Inherits from WebscoutE, raised for exceeding API request rate limits.
772
753
  TimeoutE: Inherits from WebscoutE, raised for API request timeouts.
773
754
  """
774
- result = await self._loop.run_in_executor(
775
- self._executor,
776
- super().maps,
777
- keywords,
778
- place,
779
- street,
780
- city,
781
- county,
782
- state,
783
- country,
784
- postalcode,
785
- latitude,
786
- longitude,
787
- radius,
788
- max_results,
789
- )
790
- return result
755
+ # These methods are not implemented in the async version yet
756
+ raise NotImplementedError("amaps method is not implemented yet")
791
757
 
792
758
  async def atranslate(
793
759
  self,
@@ -810,14 +776,8 @@ class AsyncWEBS:
810
776
  RatelimitE: Inherits from WebscoutE, raised for exceeding API request rate limits.
811
777
  TimeoutE: Inherits from WebscoutE, raised for API request timeouts.
812
778
  """
813
- result = await self._loop.run_in_executor(
814
- self._executor,
815
- super().translate,
816
- keywords,
817
- from_,
818
- to,
819
- )
820
- return result
779
+ # These methods are not implemented in the async version yet
780
+ raise NotImplementedError("atranslate method is not implemented yet")
821
781
 
822
782
  async def aweather(
823
783
  self,
@@ -868,10 +828,5 @@ class AsyncWEBS:
868
828
  RatelimitE: Inherits from WebscoutE, raised for exceeding API request rate limits.
869
829
  TimeoutE: Inherits from WebscoutE, raised for API request timeouts.
870
830
  """
871
- result = await self._loop.run_in_executor(
872
- self._executor,
873
- super().weather,
874
- location,
875
- language,
876
- )
877
- return result
831
+ # These methods are not implemented in the async version yet
832
+ raise NotImplementedError("aweather method is not implemented yet")
webscout/yep_search.py CHANGED
@@ -1,12 +1,13 @@
1
- import cloudscraper
1
+ from curl_cffi.requests import Session
2
2
  from urllib.parse import urlencode
3
3
  from webscout.litagent import LitAgent
4
4
  from typing import List, Dict, Optional, Tuple
5
5
  from concurrent.futures import ThreadPoolExecutor
6
6
  import json
7
+
7
8
  class YepSearch:
8
9
  """Yep.com search class to get search results."""
9
-
10
+
10
11
  _executor: ThreadPoolExecutor = ThreadPoolExecutor()
11
12
 
12
13
  def __init__(
@@ -14,47 +15,54 @@ class YepSearch:
14
15
  timeout: int = 20,
15
16
  proxies: Dict[str, str] | None = None,
16
17
  verify: bool = True,
18
+ impersonate: str = "chrome110"
17
19
  ):
18
20
  """Initialize YepSearch.
19
-
21
+
20
22
  Args:
21
23
  timeout: Timeout value for the HTTP client. Defaults to 20.
22
24
  proxies: Proxy configuration for requests. Defaults to None.
23
25
  verify: Verify SSL certificates. Defaults to True.
26
+ impersonate: Browser profile to impersonate for curl_cffi. Defaults to "chrome110".
24
27
  """
25
28
  self.base_url = "https://api.yep.com/fs/2/search"
26
29
  self.timeout = timeout
27
- self.session = cloudscraper.create_scraper()
30
+ # Initialize curl_cffi session
31
+ self.session = Session(
32
+ proxies=proxies,
33
+ verify=verify,
34
+ impersonate=impersonate,
35
+ timeout=timeout # Set timeout directly in session
36
+ )
28
37
  self.session.headers.update({
29
38
  "Accept": "*/*",
30
39
  "Accept-Language": "en-US,en;q=0.9,en-IN;q=0.8",
31
40
  "DNT": "1",
32
41
  "Origin": "https://yep.com",
33
42
  "Referer": "https://yep.com/",
43
+ # Sec-Ch-Ua headers are often handled by impersonate, but keeping them might be safer
34
44
  "Sec-Ch-Ua": '"Not(A:Brand";v="99", "Microsoft Edge";v="133", "Chromium";v="133"',
35
45
  "Sec-Ch-Ua-Mobile": "?0",
36
46
  "Sec-Ch-Ua-Platform": '"Windows"',
37
47
  "Sec-Fetch-Dest": "empty",
38
48
  "Sec-Fetch-Mode": "cors",
39
49
  "Sec-Fetch-Site": "same-site",
40
- "User-Agent": LitAgent().random()
50
+ "User-Agent": LitAgent().random() # Keep custom User-Agent or rely on impersonate
41
51
  })
42
- if proxies:
43
- self.session.proxies.update(proxies)
44
- self.session.verify = verify
52
+ # Proxies and verify are handled by the Session constructor now
45
53
 
46
54
  def _remove_html_tags(self, text: str) -> str:
47
55
  """Remove HTML tags from text using simple string manipulation.
48
-
56
+
49
57
  Args:
50
58
  text: String containing HTML tags
51
-
59
+
52
60
  Returns:
53
61
  Clean text without HTML tags
54
62
  """
55
63
  result = ""
56
64
  in_tag = False
57
-
65
+
58
66
  for char in text:
59
67
  if char == '<':
60
68
  in_tag = True
@@ -62,7 +70,7 @@ class YepSearch:
62
70
  in_tag = False
63
71
  elif not in_tag:
64
72
  result += char
65
-
73
+
66
74
  # Replace common HTML entities
67
75
  replacements = {
68
76
  '&nbsp;': ' ',
@@ -72,21 +80,21 @@ class YepSearch:
72
80
  '&quot;': '"',
73
81
  '&apos;': "'",
74
82
  }
75
-
83
+
76
84
  for entity, replacement in replacements.items():
77
85
  result = result.replace(entity, replacement)
78
-
86
+
79
87
  return result.strip()
80
88
 
81
89
  def format_results(self, raw_results: dict) -> List[Dict]:
82
90
  """Format raw API results into a consistent structure."""
83
91
  formatted_results = []
84
-
92
+
85
93
  if not raw_results or len(raw_results) < 2:
86
94
  return formatted_results
87
95
 
88
96
  results = raw_results[1].get('results', [])
89
-
97
+
90
98
  for result in results:
91
99
  formatted_result = {
92
100
  "title": self._remove_html_tags(result.get("title", "")),
@@ -97,7 +105,7 @@ class YepSearch:
97
105
  "type": result.get("type", "organic"),
98
106
  "first_seen": result.get("first_seen", None)
99
107
  }
100
-
108
+
101
109
  # Add sitelinks if they exist
102
110
  if "sitelinks" in result:
103
111
  sitelinks = []
@@ -105,7 +113,7 @@ class YepSearch:
105
113
  sitelinks.extend(result["sitelinks"]["full"])
106
114
  if "short" in result["sitelinks"]:
107
115
  sitelinks.extend(result["sitelinks"]["short"])
108
-
116
+
109
117
  if sitelinks:
110
118
  formatted_result["sitelinks"] = [
111
119
  {
@@ -114,9 +122,9 @@ class YepSearch:
114
122
  }
115
123
  for link in sitelinks
116
124
  ]
117
-
125
+
118
126
  formatted_results.append(formatted_result)
119
-
127
+
120
128
  return formatted_results
121
129
 
122
130
  def text(
@@ -154,20 +162,25 @@ class YepSearch:
154
162
  "safeSearch": safe_setting,
155
163
  "type": "web"
156
164
  }
157
-
165
+
158
166
  url = f"{self.base_url}?{urlencode(params)}"
159
167
  try:
160
- response = self.session.get(url, timeout=self.timeout)
168
+ # Use the session timeout defined in __init__
169
+ response = self.session.get(url)
161
170
  response.raise_for_status()
162
171
  raw_results = response.json()
163
-
172
+
164
173
  formatted_results = self.format_results(raw_results)
165
-
174
+
166
175
  if max_results:
167
176
  return formatted_results[:max_results]
168
177
  return formatted_results
169
178
  except Exception as e:
170
- raise Exception(f"Yep search failed: {str(e)}")
179
+ # Provide more specific error context if possible
180
+ if hasattr(e, 'response') and e.response is not None:
181
+ raise Exception(f"Yep search failed with status {e.response.status_code}: {str(e)}")
182
+ else:
183
+ raise Exception(f"Yep search failed: {str(e)}")
171
184
 
172
185
  def images(
173
186
  self,
@@ -210,23 +223,24 @@ class YepSearch:
210
223
  "safeSearch": safe_setting,
211
224
  "type": "images"
212
225
  }
213
-
226
+
214
227
  url = f"{self.base_url}?{urlencode(params)}"
215
228
  try:
216
- response = self.session.get(url, timeout=self.timeout)
229
+ # Use the session timeout defined in __init__
230
+ response = self.session.get(url)
217
231
  response.raise_for_status()
218
232
  raw_results = response.json()
219
-
233
+
220
234
  if not raw_results or len(raw_results) < 2:
221
235
  return []
222
236
 
223
237
  formatted_results = []
224
238
  results = raw_results[1].get('results', [])
225
-
239
+
226
240
  for result in results:
227
241
  if result.get("type") != "Image":
228
242
  continue
229
-
243
+
230
244
  formatted_result = {
231
245
  "title": self._remove_html_tags(result.get("title", "")),
232
246
  "image": result.get("image_id", ""),
@@ -236,19 +250,23 @@ class YepSearch:
236
250
  "width": result.get("width", 0),
237
251
  "source": result.get("visual_url", "")
238
252
  }
239
-
253
+
240
254
  # Add high-res thumbnail if available
241
255
  if "srcset" in result:
242
256
  formatted_result["thumbnail_hd"] = result["srcset"].split(",")[1].strip().split(" ")[0]
243
-
257
+
244
258
  formatted_results.append(formatted_result)
245
-
259
+
246
260
  if max_results:
247
261
  return formatted_results[:max_results]
248
262
  return formatted_results
249
-
263
+
250
264
  except Exception as e:
251
- raise Exception(f"Yep image search failed: {str(e)}")
265
+ # Provide more specific error context if possible
266
+ if hasattr(e, 'response') and e.response is not None:
267
+ raise Exception(f"Yep image search failed with status {e.response.status_code}: {str(e)}")
268
+ else:
269
+ raise Exception(f"Yep image search failed: {str(e)}")
252
270
 
253
271
  def suggestions(
254
272
  self,
@@ -275,23 +293,55 @@ class YepSearch:
275
293
  "type": "web",
276
294
  "gl": region
277
295
  }
278
-
296
+
279
297
  url = f"https://api.yep.com/ac/?{urlencode(params)}"
280
-
298
+
281
299
  try:
282
- response = self.session.get(url, timeout=self.timeout)
300
+ # Use the session timeout defined in __init__
301
+ response = self.session.get(url)
283
302
  response.raise_for_status()
284
303
  data = response.json()
285
304
  # Return suggestions list if response format is valid
286
305
  if isinstance(data, list) and len(data) > 1 and isinstance(data[1], list):
287
306
  return data[1]
288
307
  return []
289
-
308
+
290
309
  except Exception as e:
291
- raise Exception(f"Yep suggestions failed: {str(e)}")
310
+ # Provide more specific error context if possible
311
+ if hasattr(e, 'response') and e.response is not None:
312
+ raise Exception(f"Yep suggestions failed with status {e.response.status_code}: {str(e)}")
313
+ else:
314
+ raise Exception(f"Yep suggestions failed: {str(e)}")
292
315
 
293
316
 
294
317
  if __name__ == "__main__":
295
- yep = YepSearch()
296
- r = yep.suggestions("hi", region="all")
297
- print(r)
318
+ from rich import print
319
+ yep = YepSearch(
320
+ timeout=20, # Optional: Set custom timeout
321
+ proxies=None, # Optional: Use proxies
322
+ verify=True # Optional: SSL verification
323
+ )
324
+
325
+ # Text Search
326
+ text_results = yep.text(
327
+ keywords="artificial intelligence",
328
+ region="all", # Optional: Region for results
329
+ safesearch="moderate", # Optional: "on", "moderate", "off"
330
+ max_results=10 # Optional: Limit number of results
331
+ )
332
+
333
+ # Image Search
334
+ image_results = yep.images(
335
+ keywords="nature photography",
336
+ region="all",
337
+ safesearch="moderate",
338
+ max_results=10
339
+ )
340
+
341
+ # Get search suggestions
342
+ suggestions = yep.suggestions("hist")
343
+ print(text_results)
344
+ print("---" * 30)
345
+ print(image_results)
346
+ print("---" * 30)
347
+ print(suggestions)