webscout 8.3.7__py3-none-any.whl → 2025.10.11__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.
- webscout/AIauto.py +250 -250
- webscout/AIbase.py +379 -379
- webscout/AIutel.py +60 -60
- webscout/Bard.py +1012 -1012
- webscout/Bing_search.py +417 -417
- webscout/DWEBS.py +529 -529
- webscout/Extra/Act.md +309 -309
- webscout/Extra/GitToolkit/__init__.py +10 -10
- webscout/Extra/GitToolkit/gitapi/README.md +110 -110
- webscout/Extra/GitToolkit/gitapi/__init__.py +11 -11
- webscout/Extra/GitToolkit/gitapi/repository.py +195 -195
- webscout/Extra/GitToolkit/gitapi/user.py +96 -96
- webscout/Extra/GitToolkit/gitapi/utils.py +61 -61
- webscout/Extra/YTToolkit/README.md +375 -375
- webscout/Extra/YTToolkit/YTdownloader.py +956 -956
- webscout/Extra/YTToolkit/__init__.py +2 -2
- webscout/Extra/YTToolkit/transcriber.py +475 -475
- webscout/Extra/YTToolkit/ytapi/README.md +44 -44
- webscout/Extra/YTToolkit/ytapi/__init__.py +6 -6
- webscout/Extra/YTToolkit/ytapi/channel.py +307 -307
- webscout/Extra/YTToolkit/ytapi/errors.py +13 -13
- webscout/Extra/YTToolkit/ytapi/extras.py +118 -118
- webscout/Extra/YTToolkit/ytapi/https.py +88 -88
- webscout/Extra/YTToolkit/ytapi/patterns.py +61 -61
- webscout/Extra/YTToolkit/ytapi/playlist.py +58 -58
- webscout/Extra/YTToolkit/ytapi/pool.py +7 -7
- webscout/Extra/YTToolkit/ytapi/query.py +39 -39
- webscout/Extra/YTToolkit/ytapi/stream.py +62 -62
- webscout/Extra/YTToolkit/ytapi/utils.py +62 -62
- webscout/Extra/YTToolkit/ytapi/video.py +232 -232
- webscout/Extra/autocoder/__init__.py +9 -9
- webscout/Extra/autocoder/autocoder.py +1105 -1105
- webscout/Extra/autocoder/autocoder_utiles.py +332 -332
- webscout/Extra/gguf.md +429 -429
- webscout/Extra/gguf.py +1213 -1213
- webscout/Extra/tempmail/README.md +487 -487
- webscout/Extra/tempmail/__init__.py +27 -27
- webscout/Extra/tempmail/async_utils.py +140 -140
- webscout/Extra/tempmail/base.py +160 -160
- webscout/Extra/tempmail/cli.py +186 -186
- webscout/Extra/tempmail/emailnator.py +84 -84
- webscout/Extra/tempmail/mail_tm.py +360 -360
- webscout/Extra/tempmail/temp_mail_io.py +291 -291
- webscout/Extra/weather.md +281 -281
- webscout/Extra/weather.py +193 -193
- webscout/Litlogger/README.md +10 -10
- webscout/Litlogger/__init__.py +15 -15
- webscout/Litlogger/formats.py +13 -13
- webscout/Litlogger/handlers.py +121 -121
- webscout/Litlogger/levels.py +13 -13
- webscout/Litlogger/logger.py +134 -134
- webscout/Provider/AISEARCH/Perplexity.py +332 -332
- webscout/Provider/AISEARCH/README.md +279 -279
- webscout/Provider/AISEARCH/__init__.py +16 -1
- webscout/Provider/AISEARCH/felo_search.py +206 -206
- webscout/Provider/AISEARCH/genspark_search.py +323 -323
- webscout/Provider/AISEARCH/hika_search.py +185 -185
- webscout/Provider/AISEARCH/iask_search.py +410 -410
- webscout/Provider/AISEARCH/monica_search.py +219 -219
- webscout/Provider/AISEARCH/scira_search.py +316 -316
- webscout/Provider/AISEARCH/stellar_search.py +177 -177
- webscout/Provider/AISEARCH/webpilotai_search.py +255 -255
- webscout/Provider/Aitopia.py +314 -314
- webscout/Provider/Apriel.py +306 -0
- webscout/Provider/ChatGPTClone.py +236 -236
- webscout/Provider/ChatSandbox.py +343 -343
- webscout/Provider/Cloudflare.py +324 -324
- webscout/Provider/Cohere.py +208 -208
- webscout/Provider/Deepinfra.py +370 -366
- webscout/Provider/ExaAI.py +260 -260
- webscout/Provider/ExaChat.py +308 -308
- webscout/Provider/Flowith.py +221 -221
- webscout/Provider/GMI.py +293 -0
- webscout/Provider/Gemini.py +164 -164
- webscout/Provider/GeminiProxy.py +167 -167
- webscout/Provider/GithubChat.py +371 -372
- webscout/Provider/Groq.py +800 -800
- webscout/Provider/HeckAI.py +383 -383
- webscout/Provider/Jadve.py +282 -282
- webscout/Provider/K2Think.py +307 -307
- webscout/Provider/Koboldai.py +205 -205
- webscout/Provider/LambdaChat.py +423 -423
- webscout/Provider/Nemotron.py +244 -244
- webscout/Provider/Netwrck.py +248 -248
- webscout/Provider/OLLAMA.py +395 -395
- webscout/Provider/OPENAI/Cloudflare.py +393 -393
- webscout/Provider/OPENAI/FalconH1.py +451 -451
- webscout/Provider/OPENAI/FreeGemini.py +296 -296
- webscout/Provider/OPENAI/K2Think.py +431 -431
- webscout/Provider/OPENAI/NEMOTRON.py +240 -240
- webscout/Provider/OPENAI/PI.py +427 -427
- webscout/Provider/OPENAI/README.md +959 -959
- webscout/Provider/OPENAI/TogetherAI.py +345 -345
- webscout/Provider/OPENAI/TwoAI.py +465 -465
- webscout/Provider/OPENAI/__init__.py +33 -18
- webscout/Provider/OPENAI/base.py +248 -248
- webscout/Provider/OPENAI/chatglm.py +528 -0
- webscout/Provider/OPENAI/chatgpt.py +592 -592
- webscout/Provider/OPENAI/chatgptclone.py +521 -521
- webscout/Provider/OPENAI/chatsandbox.py +202 -202
- webscout/Provider/OPENAI/deepinfra.py +318 -314
- webscout/Provider/OPENAI/e2b.py +1665 -1665
- webscout/Provider/OPENAI/exaai.py +420 -420
- webscout/Provider/OPENAI/exachat.py +452 -452
- webscout/Provider/OPENAI/friendli.py +232 -232
- webscout/Provider/OPENAI/{refact.py → gmi.py} +324 -274
- webscout/Provider/OPENAI/groq.py +364 -364
- webscout/Provider/OPENAI/heckai.py +314 -314
- webscout/Provider/OPENAI/llmchatco.py +337 -337
- webscout/Provider/OPENAI/netwrck.py +355 -355
- webscout/Provider/OPENAI/oivscode.py +290 -290
- webscout/Provider/OPENAI/opkfc.py +518 -518
- webscout/Provider/OPENAI/pydantic_imports.py +1 -1
- webscout/Provider/OPENAI/scirachat.py +535 -535
- webscout/Provider/OPENAI/sonus.py +308 -308
- webscout/Provider/OPENAI/standardinput.py +442 -442
- webscout/Provider/OPENAI/textpollinations.py +340 -340
- webscout/Provider/OPENAI/toolbaz.py +419 -416
- webscout/Provider/OPENAI/typefully.py +362 -362
- webscout/Provider/OPENAI/utils.py +295 -295
- webscout/Provider/OPENAI/venice.py +436 -436
- webscout/Provider/OPENAI/wisecat.py +387 -387
- webscout/Provider/OPENAI/writecream.py +166 -166
- webscout/Provider/OPENAI/x0gpt.py +378 -378
- webscout/Provider/OPENAI/yep.py +389 -389
- webscout/Provider/OpenGPT.py +230 -230
- webscout/Provider/Openai.py +243 -243
- webscout/Provider/PI.py +405 -405
- webscout/Provider/Perplexitylabs.py +430 -430
- webscout/Provider/QwenLM.py +272 -272
- webscout/Provider/STT/__init__.py +16 -1
- webscout/Provider/Sambanova.py +257 -257
- webscout/Provider/StandardInput.py +309 -309
- webscout/Provider/TTI/README.md +82 -82
- webscout/Provider/TTI/__init__.py +33 -18
- webscout/Provider/TTI/aiarta.py +413 -413
- webscout/Provider/TTI/base.py +136 -136
- webscout/Provider/TTI/bing.py +243 -243
- webscout/Provider/TTI/gpt1image.py +149 -149
- webscout/Provider/TTI/imagen.py +196 -196
- webscout/Provider/TTI/infip.py +211 -211
- webscout/Provider/TTI/magicstudio.py +232 -232
- webscout/Provider/TTI/monochat.py +219 -219
- webscout/Provider/TTI/piclumen.py +214 -214
- webscout/Provider/TTI/pixelmuse.py +232 -232
- webscout/Provider/TTI/pollinations.py +232 -232
- webscout/Provider/TTI/together.py +288 -288
- webscout/Provider/TTI/utils.py +12 -12
- webscout/Provider/TTI/venice.py +367 -367
- webscout/Provider/TTS/README.md +192 -192
- webscout/Provider/TTS/__init__.py +33 -18
- webscout/Provider/TTS/parler.py +110 -110
- webscout/Provider/TTS/streamElements.py +333 -333
- webscout/Provider/TTS/utils.py +280 -280
- webscout/Provider/TeachAnything.py +237 -237
- webscout/Provider/TextPollinationsAI.py +310 -310
- webscout/Provider/TogetherAI.py +356 -356
- webscout/Provider/TwoAI.py +312 -312
- webscout/Provider/TypliAI.py +311 -311
- webscout/Provider/UNFINISHED/ChatHub.py +208 -208
- webscout/Provider/UNFINISHED/ChutesAI.py +313 -313
- webscout/Provider/UNFINISHED/GizAI.py +294 -294
- webscout/Provider/UNFINISHED/Marcus.py +198 -198
- webscout/Provider/UNFINISHED/Qodo.py +477 -477
- webscout/Provider/UNFINISHED/VercelAIGateway.py +338 -338
- webscout/Provider/UNFINISHED/XenAI.py +324 -324
- webscout/Provider/UNFINISHED/Youchat.py +330 -330
- webscout/Provider/UNFINISHED/liner.py +334 -0
- webscout/Provider/UNFINISHED/liner_api_request.py +262 -262
- webscout/Provider/UNFINISHED/puterjs.py +634 -634
- webscout/Provider/UNFINISHED/samurai.py +223 -223
- webscout/Provider/UNFINISHED/test_lmarena.py +119 -119
- webscout/Provider/Venice.py +250 -250
- webscout/Provider/VercelAI.py +256 -256
- webscout/Provider/WiseCat.py +231 -231
- webscout/Provider/WrDoChat.py +366 -366
- webscout/Provider/__init__.py +33 -18
- webscout/Provider/ai4chat.py +174 -174
- webscout/Provider/akashgpt.py +331 -331
- webscout/Provider/cerebras.py +446 -446
- webscout/Provider/chatglm.py +394 -301
- webscout/Provider/cleeai.py +211 -211
- webscout/Provider/elmo.py +282 -282
- webscout/Provider/geminiapi.py +208 -208
- webscout/Provider/granite.py +261 -261
- webscout/Provider/hermes.py +263 -263
- webscout/Provider/julius.py +223 -223
- webscout/Provider/learnfastai.py +309 -309
- webscout/Provider/llama3mitril.py +214 -214
- webscout/Provider/llmchat.py +243 -243
- webscout/Provider/llmchatco.py +290 -290
- webscout/Provider/meta.py +801 -801
- webscout/Provider/oivscode.py +309 -309
- webscout/Provider/scira_chat.py +383 -383
- webscout/Provider/searchchat.py +292 -292
- webscout/Provider/sonus.py +258 -258
- webscout/Provider/toolbaz.py +370 -367
- webscout/Provider/turboseek.py +273 -273
- webscout/Provider/typefully.py +207 -207
- webscout/Provider/yep.py +372 -372
- webscout/__init__.py +30 -31
- webscout/__main__.py +5 -5
- webscout/auth/api_key_manager.py +189 -189
- webscout/auth/config.py +175 -175
- webscout/auth/models.py +185 -185
- webscout/auth/routes.py +664 -664
- webscout/auth/simple_logger.py +236 -236
- webscout/cli.py +523 -523
- webscout/conversation.py +438 -438
- webscout/exceptions.py +361 -361
- webscout/litagent/Readme.md +298 -298
- webscout/litagent/__init__.py +28 -28
- webscout/litagent/agent.py +581 -581
- webscout/litagent/constants.py +59 -59
- webscout/litprinter/__init__.py +58 -58
- webscout/models.py +181 -181
- webscout/optimizers.py +419 -419
- webscout/prompt_manager.py +288 -288
- webscout/sanitize.py +1078 -1078
- webscout/scout/README.md +401 -401
- webscout/scout/__init__.py +8 -8
- webscout/scout/core/__init__.py +6 -6
- webscout/scout/core/crawler.py +297 -297
- webscout/scout/core/scout.py +706 -706
- webscout/scout/core/search_result.py +95 -95
- webscout/scout/core/text_analyzer.py +62 -62
- webscout/scout/core/text_utils.py +277 -277
- webscout/scout/core/web_analyzer.py +51 -51
- webscout/scout/element.py +599 -599
- webscout/scout/parsers/__init__.py +69 -69
- webscout/scout/parsers/html5lib_parser.py +172 -172
- webscout/scout/parsers/html_parser.py +236 -236
- webscout/scout/parsers/lxml_parser.py +178 -178
- webscout/scout/utils.py +37 -37
- webscout/swiftcli/Readme.md +323 -323
- webscout/swiftcli/__init__.py +95 -95
- webscout/swiftcli/core/__init__.py +7 -7
- webscout/swiftcli/core/cli.py +308 -308
- webscout/swiftcli/core/context.py +104 -104
- webscout/swiftcli/core/group.py +241 -241
- webscout/swiftcli/decorators/__init__.py +28 -28
- webscout/swiftcli/decorators/command.py +221 -221
- webscout/swiftcli/decorators/options.py +220 -220
- webscout/swiftcli/decorators/output.py +302 -302
- webscout/swiftcli/exceptions.py +21 -21
- webscout/swiftcli/plugins/__init__.py +9 -9
- webscout/swiftcli/plugins/base.py +135 -135
- webscout/swiftcli/plugins/manager.py +269 -269
- webscout/swiftcli/utils/__init__.py +59 -59
- webscout/swiftcli/utils/formatting.py +252 -252
- webscout/swiftcli/utils/parsing.py +267 -267
- webscout/update_checker.py +117 -117
- webscout/version.py +1 -1
- webscout/webscout_search.py +1183 -1183
- webscout/webscout_search_async.py +649 -649
- webscout/yep_search.py +346 -346
- webscout/zeroart/README.md +89 -89
- webscout/zeroart/__init__.py +134 -134
- webscout/zeroart/base.py +66 -66
- webscout/zeroart/effects.py +100 -100
- webscout/zeroart/fonts.py +1238 -1238
- {webscout-8.3.7.dist-info → webscout-2025.10.11.dist-info}/METADATA +937 -937
- webscout-2025.10.11.dist-info/RECORD +300 -0
- webscout/Provider/AISEARCH/DeepFind.py +0 -254
- webscout/Provider/OPENAI/Qwen3.py +0 -303
- webscout/Provider/OPENAI/qodo.py +0 -630
- webscout/Provider/OPENAI/xenai.py +0 -514
- webscout/tempid.py +0 -134
- webscout-8.3.7.dist-info/RECORD +0 -301
- {webscout-8.3.7.dist-info → webscout-2025.10.11.dist-info}/WHEEL +0 -0
- {webscout-8.3.7.dist-info → webscout-2025.10.11.dist-info}/entry_points.txt +0 -0
- {webscout-8.3.7.dist-info → webscout-2025.10.11.dist-info}/licenses/LICENSE.md +0 -0
- {webscout-8.3.7.dist-info → webscout-2025.10.11.dist-info}/top_level.txt +0 -0
|
@@ -1,236 +1,236 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Scout HTML Parser - Advanced HTML Parsing with Python's Built-in Parser
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
import html
|
|
6
|
-
import re
|
|
7
|
-
from html.parser import HTMLParser as StdHTMLParser
|
|
8
|
-
from typing import List
|
|
9
|
-
|
|
10
|
-
from ..element import Tag, NavigableString
|
|
11
|
-
|
|
12
|
-
class HTMLParser:
|
|
13
|
-
"""
|
|
14
|
-
Advanced HTML parser using Python's built-in HTMLParser.
|
|
15
|
-
Provides robust parsing with enhanced error handling and flexibility.
|
|
16
|
-
"""
|
|
17
|
-
|
|
18
|
-
def __init__(self):
|
|
19
|
-
"""
|
|
20
|
-
Initialize the HTML parser with advanced parsing capabilities.
|
|
21
|
-
"""
|
|
22
|
-
self._root = Tag('html')
|
|
23
|
-
self._current_tag = self._root
|
|
24
|
-
self._tag_stack = [self._root]
|
|
25
|
-
self._parsing_errors = []
|
|
26
|
-
|
|
27
|
-
def parse(self, markup: str) -> Tag:
|
|
28
|
-
"""
|
|
29
|
-
Parse HTML markup and return the root tag.
|
|
30
|
-
|
|
31
|
-
Args:
|
|
32
|
-
markup (str): HTML content to parse
|
|
33
|
-
|
|
34
|
-
Returns:
|
|
35
|
-
Tag: Parsed HTML document root
|
|
36
|
-
"""
|
|
37
|
-
try:
|
|
38
|
-
# Preprocess markup to handle common issues
|
|
39
|
-
markup = self._preprocess_markup(markup)
|
|
40
|
-
|
|
41
|
-
# Create a standard HTML parser
|
|
42
|
-
parser = _ScoutHTMLParser(self)
|
|
43
|
-
parser.feed(markup)
|
|
44
|
-
parser.close()
|
|
45
|
-
|
|
46
|
-
return self._root
|
|
47
|
-
except Exception as e:
|
|
48
|
-
self._parsing_errors.append(str(e))
|
|
49
|
-
return self._root
|
|
50
|
-
|
|
51
|
-
def _preprocess_markup(self, markup: str) -> str:
|
|
52
|
-
"""
|
|
53
|
-
Preprocess HTML markup to handle common parsing issues.
|
|
54
|
-
|
|
55
|
-
Args:
|
|
56
|
-
markup (str): Raw HTML markup
|
|
57
|
-
|
|
58
|
-
Returns:
|
|
59
|
-
str: Preprocessed HTML markup
|
|
60
|
-
"""
|
|
61
|
-
# Decode HTML entities
|
|
62
|
-
markup = html.unescape(markup)
|
|
63
|
-
|
|
64
|
-
# Handle unclosed tags (basic approach)
|
|
65
|
-
markup = re.sub(r'<(br|img|input|hr|meta)([^>]*?)(?<!/)>', r'<\1\2 />', markup, flags=re.IGNORECASE)
|
|
66
|
-
|
|
67
|
-
# Remove comments (optional, can be configurable)
|
|
68
|
-
markup = re.sub(r'<!--.*?-->', '', markup, flags=re.DOTALL)
|
|
69
|
-
|
|
70
|
-
return markup
|
|
71
|
-
|
|
72
|
-
def add_tag(self, tag: Tag):
|
|
73
|
-
"""
|
|
74
|
-
Add a tag to the current parsing context.
|
|
75
|
-
|
|
76
|
-
Args:
|
|
77
|
-
tag (Tag): Tag to add
|
|
78
|
-
"""
|
|
79
|
-
# Set parent-child relationships
|
|
80
|
-
tag.parent = self._current_tag
|
|
81
|
-
self._current_tag.contents.append(tag)
|
|
82
|
-
|
|
83
|
-
# Update current tag if it's an opening tag
|
|
84
|
-
self._current_tag = tag
|
|
85
|
-
self._tag_stack.append(tag)
|
|
86
|
-
|
|
87
|
-
def add_text(self, text: str):
|
|
88
|
-
"""
|
|
89
|
-
Add text content to the current tag.
|
|
90
|
-
|
|
91
|
-
Args:
|
|
92
|
-
text (str): Text content
|
|
93
|
-
"""
|
|
94
|
-
if text.strip():
|
|
95
|
-
text_node = NavigableString(text)
|
|
96
|
-
text_node.parent = self._current_tag
|
|
97
|
-
self._current_tag.contents.append(text_node)
|
|
98
|
-
|
|
99
|
-
def close_tag(self):
|
|
100
|
-
"""
|
|
101
|
-
Close the current tag and return to parent context.
|
|
102
|
-
"""
|
|
103
|
-
if len(self._tag_stack) > 1:
|
|
104
|
-
self._tag_stack.pop()
|
|
105
|
-
self._current_tag = self._tag_stack[-1]
|
|
106
|
-
|
|
107
|
-
def get_parsing_errors(self) -> List[str]:
|
|
108
|
-
"""
|
|
109
|
-
Retrieve parsing errors encountered during HTML processing.
|
|
110
|
-
|
|
111
|
-
Returns:
|
|
112
|
-
List[str]: List of parsing error messages
|
|
113
|
-
"""
|
|
114
|
-
return self._parsing_errors
|
|
115
|
-
|
|
116
|
-
class _ScoutHTMLParser(StdHTMLParser):
|
|
117
|
-
"""
|
|
118
|
-
Internal HTML parser that integrates with Scout's parsing mechanism.
|
|
119
|
-
"""
|
|
120
|
-
def __init__(self, scout_parser: HTMLParser):
|
|
121
|
-
"""
|
|
122
|
-
Initialize the parser with a Scout HTML parser.
|
|
123
|
-
|
|
124
|
-
Args:
|
|
125
|
-
scout_parser (HTMLParser): Scout's HTML parser instance
|
|
126
|
-
"""
|
|
127
|
-
super().__init__(convert_charrefs=True)
|
|
128
|
-
self._scout_parser = scout_parser
|
|
129
|
-
|
|
130
|
-
def handle_starttag(self, tag: str, attrs: List[tuple]):
|
|
131
|
-
"""
|
|
132
|
-
Handle opening tags during parsing.
|
|
133
|
-
|
|
134
|
-
Args:
|
|
135
|
-
tag (str): Tag name
|
|
136
|
-
attrs (List[tuple]): Tag attributes
|
|
137
|
-
"""
|
|
138
|
-
# Convert attrs to dictionary
|
|
139
|
-
attrs_dict = dict(attrs)
|
|
140
|
-
|
|
141
|
-
# Create Tag instance
|
|
142
|
-
new_tag = Tag(tag, attrs_dict)
|
|
143
|
-
|
|
144
|
-
# Add tag to the parsing context
|
|
145
|
-
self._scout_parser.add_tag(new_tag)
|
|
146
|
-
|
|
147
|
-
def handle_endtag(self, tag: str):
|
|
148
|
-
"""
|
|
149
|
-
Handle closing tags during parsing.
|
|
150
|
-
|
|
151
|
-
Args:
|
|
152
|
-
tag (str): Tag name
|
|
153
|
-
"""
|
|
154
|
-
# Close the current tag
|
|
155
|
-
self._scout_parser.close_tag()
|
|
156
|
-
|
|
157
|
-
def handle_data(self, data: str):
|
|
158
|
-
"""
|
|
159
|
-
Handle text data during parsing.
|
|
160
|
-
|
|
161
|
-
Args:
|
|
162
|
-
data (str): Text content
|
|
163
|
-
"""
|
|
164
|
-
# Add text to the current tag
|
|
165
|
-
self._scout_parser.add_text(data)
|
|
166
|
-
|
|
167
|
-
def handle_comment(self, data: str):
|
|
168
|
-
"""
|
|
169
|
-
Handle HTML comments (optional, can be configured).
|
|
170
|
-
|
|
171
|
-
Args:
|
|
172
|
-
data (str): Comment content
|
|
173
|
-
"""
|
|
174
|
-
# Optionally handle comments
|
|
175
|
-
comment_tag = Tag('comment')
|
|
176
|
-
comment_tag.attrs['content'] = data
|
|
177
|
-
self._scout_parser.add_tag(comment_tag)
|
|
178
|
-
|
|
179
|
-
def handle_decl(self, decl: str):
|
|
180
|
-
"""
|
|
181
|
-
Handle HTML declarations.
|
|
182
|
-
|
|
183
|
-
Args:
|
|
184
|
-
decl (str): Declaration content
|
|
185
|
-
"""
|
|
186
|
-
# Create a special tag for declarations
|
|
187
|
-
decl_tag = Tag('!DOCTYPE')
|
|
188
|
-
decl_tag.attrs['content'] = decl
|
|
189
|
-
self._scout_parser.add_tag(decl_tag)
|
|
190
|
-
|
|
191
|
-
def handle_pi(self, data: str):
|
|
192
|
-
"""
|
|
193
|
-
Handle processing instructions.
|
|
194
|
-
|
|
195
|
-
Args:
|
|
196
|
-
data (str): Processing instruction content
|
|
197
|
-
"""
|
|
198
|
-
# Create a special tag for processing instructions
|
|
199
|
-
pi_tag = Tag('?')
|
|
200
|
-
pi_tag.attrs['content'] = data
|
|
201
|
-
self._scout_parser.add_tag(pi_tag)
|
|
202
|
-
|
|
203
|
-
def handle_entityref(self, name: str):
|
|
204
|
-
"""
|
|
205
|
-
Handle HTML entity references.
|
|
206
|
-
|
|
207
|
-
Args:
|
|
208
|
-
name (str): Entity reference name
|
|
209
|
-
"""
|
|
210
|
-
# Convert entity references to their actual characters
|
|
211
|
-
char = html.entities.html5.get(name, f'&{name};')
|
|
212
|
-
self._scout_parser.add_text(char)
|
|
213
|
-
|
|
214
|
-
def handle_charref(self, name: str):
|
|
215
|
-
"""
|
|
216
|
-
Handle character references.
|
|
217
|
-
|
|
218
|
-
Args:
|
|
219
|
-
name (str): Character reference name
|
|
220
|
-
"""
|
|
221
|
-
# Convert character references to their actual characters
|
|
222
|
-
try:
|
|
223
|
-
if name.startswith('x'):
|
|
224
|
-
char = chr(int(name[1:], 16))
|
|
225
|
-
else:
|
|
226
|
-
char = chr(int(name))
|
|
227
|
-
self._scout_parser.add_text(char)
|
|
228
|
-
except ValueError:
|
|
229
|
-
# Fallback for invalid references
|
|
230
|
-
self._scout_parser.add_text(f'&#{name};')
|
|
231
|
-
|
|
232
|
-
def close(self):
|
|
233
|
-
"""
|
|
234
|
-
Finalize parsing and perform cleanup.
|
|
235
|
-
"""
|
|
236
|
-
super().close()
|
|
1
|
+
"""
|
|
2
|
+
Scout HTML Parser - Advanced HTML Parsing with Python's Built-in Parser
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import html
|
|
6
|
+
import re
|
|
7
|
+
from html.parser import HTMLParser as StdHTMLParser
|
|
8
|
+
from typing import List
|
|
9
|
+
|
|
10
|
+
from ..element import Tag, NavigableString
|
|
11
|
+
|
|
12
|
+
class HTMLParser:
|
|
13
|
+
"""
|
|
14
|
+
Advanced HTML parser using Python's built-in HTMLParser.
|
|
15
|
+
Provides robust parsing with enhanced error handling and flexibility.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(self):
|
|
19
|
+
"""
|
|
20
|
+
Initialize the HTML parser with advanced parsing capabilities.
|
|
21
|
+
"""
|
|
22
|
+
self._root = Tag('html')
|
|
23
|
+
self._current_tag = self._root
|
|
24
|
+
self._tag_stack = [self._root]
|
|
25
|
+
self._parsing_errors = []
|
|
26
|
+
|
|
27
|
+
def parse(self, markup: str) -> Tag:
|
|
28
|
+
"""
|
|
29
|
+
Parse HTML markup and return the root tag.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
markup (str): HTML content to parse
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
Tag: Parsed HTML document root
|
|
36
|
+
"""
|
|
37
|
+
try:
|
|
38
|
+
# Preprocess markup to handle common issues
|
|
39
|
+
markup = self._preprocess_markup(markup)
|
|
40
|
+
|
|
41
|
+
# Create a standard HTML parser
|
|
42
|
+
parser = _ScoutHTMLParser(self)
|
|
43
|
+
parser.feed(markup)
|
|
44
|
+
parser.close()
|
|
45
|
+
|
|
46
|
+
return self._root
|
|
47
|
+
except Exception as e:
|
|
48
|
+
self._parsing_errors.append(str(e))
|
|
49
|
+
return self._root
|
|
50
|
+
|
|
51
|
+
def _preprocess_markup(self, markup: str) -> str:
|
|
52
|
+
"""
|
|
53
|
+
Preprocess HTML markup to handle common parsing issues.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
markup (str): Raw HTML markup
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
str: Preprocessed HTML markup
|
|
60
|
+
"""
|
|
61
|
+
# Decode HTML entities
|
|
62
|
+
markup = html.unescape(markup)
|
|
63
|
+
|
|
64
|
+
# Handle unclosed tags (basic approach)
|
|
65
|
+
markup = re.sub(r'<(br|img|input|hr|meta)([^>]*?)(?<!/)>', r'<\1\2 />', markup, flags=re.IGNORECASE)
|
|
66
|
+
|
|
67
|
+
# Remove comments (optional, can be configurable)
|
|
68
|
+
markup = re.sub(r'<!--.*?-->', '', markup, flags=re.DOTALL)
|
|
69
|
+
|
|
70
|
+
return markup
|
|
71
|
+
|
|
72
|
+
def add_tag(self, tag: Tag):
|
|
73
|
+
"""
|
|
74
|
+
Add a tag to the current parsing context.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
tag (Tag): Tag to add
|
|
78
|
+
"""
|
|
79
|
+
# Set parent-child relationships
|
|
80
|
+
tag.parent = self._current_tag
|
|
81
|
+
self._current_tag.contents.append(tag)
|
|
82
|
+
|
|
83
|
+
# Update current tag if it's an opening tag
|
|
84
|
+
self._current_tag = tag
|
|
85
|
+
self._tag_stack.append(tag)
|
|
86
|
+
|
|
87
|
+
def add_text(self, text: str):
|
|
88
|
+
"""
|
|
89
|
+
Add text content to the current tag.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
text (str): Text content
|
|
93
|
+
"""
|
|
94
|
+
if text.strip():
|
|
95
|
+
text_node = NavigableString(text)
|
|
96
|
+
text_node.parent = self._current_tag
|
|
97
|
+
self._current_tag.contents.append(text_node)
|
|
98
|
+
|
|
99
|
+
def close_tag(self):
|
|
100
|
+
"""
|
|
101
|
+
Close the current tag and return to parent context.
|
|
102
|
+
"""
|
|
103
|
+
if len(self._tag_stack) > 1:
|
|
104
|
+
self._tag_stack.pop()
|
|
105
|
+
self._current_tag = self._tag_stack[-1]
|
|
106
|
+
|
|
107
|
+
def get_parsing_errors(self) -> List[str]:
|
|
108
|
+
"""
|
|
109
|
+
Retrieve parsing errors encountered during HTML processing.
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
List[str]: List of parsing error messages
|
|
113
|
+
"""
|
|
114
|
+
return self._parsing_errors
|
|
115
|
+
|
|
116
|
+
class _ScoutHTMLParser(StdHTMLParser):
|
|
117
|
+
"""
|
|
118
|
+
Internal HTML parser that integrates with Scout's parsing mechanism.
|
|
119
|
+
"""
|
|
120
|
+
def __init__(self, scout_parser: HTMLParser):
|
|
121
|
+
"""
|
|
122
|
+
Initialize the parser with a Scout HTML parser.
|
|
123
|
+
|
|
124
|
+
Args:
|
|
125
|
+
scout_parser (HTMLParser): Scout's HTML parser instance
|
|
126
|
+
"""
|
|
127
|
+
super().__init__(convert_charrefs=True)
|
|
128
|
+
self._scout_parser = scout_parser
|
|
129
|
+
|
|
130
|
+
def handle_starttag(self, tag: str, attrs: List[tuple]):
|
|
131
|
+
"""
|
|
132
|
+
Handle opening tags during parsing.
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
tag (str): Tag name
|
|
136
|
+
attrs (List[tuple]): Tag attributes
|
|
137
|
+
"""
|
|
138
|
+
# Convert attrs to dictionary
|
|
139
|
+
attrs_dict = dict(attrs)
|
|
140
|
+
|
|
141
|
+
# Create Tag instance
|
|
142
|
+
new_tag = Tag(tag, attrs_dict)
|
|
143
|
+
|
|
144
|
+
# Add tag to the parsing context
|
|
145
|
+
self._scout_parser.add_tag(new_tag)
|
|
146
|
+
|
|
147
|
+
def handle_endtag(self, tag: str):
|
|
148
|
+
"""
|
|
149
|
+
Handle closing tags during parsing.
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
tag (str): Tag name
|
|
153
|
+
"""
|
|
154
|
+
# Close the current tag
|
|
155
|
+
self._scout_parser.close_tag()
|
|
156
|
+
|
|
157
|
+
def handle_data(self, data: str):
|
|
158
|
+
"""
|
|
159
|
+
Handle text data during parsing.
|
|
160
|
+
|
|
161
|
+
Args:
|
|
162
|
+
data (str): Text content
|
|
163
|
+
"""
|
|
164
|
+
# Add text to the current tag
|
|
165
|
+
self._scout_parser.add_text(data)
|
|
166
|
+
|
|
167
|
+
def handle_comment(self, data: str):
|
|
168
|
+
"""
|
|
169
|
+
Handle HTML comments (optional, can be configured).
|
|
170
|
+
|
|
171
|
+
Args:
|
|
172
|
+
data (str): Comment content
|
|
173
|
+
"""
|
|
174
|
+
# Optionally handle comments
|
|
175
|
+
comment_tag = Tag('comment')
|
|
176
|
+
comment_tag.attrs['content'] = data
|
|
177
|
+
self._scout_parser.add_tag(comment_tag)
|
|
178
|
+
|
|
179
|
+
def handle_decl(self, decl: str):
|
|
180
|
+
"""
|
|
181
|
+
Handle HTML declarations.
|
|
182
|
+
|
|
183
|
+
Args:
|
|
184
|
+
decl (str): Declaration content
|
|
185
|
+
"""
|
|
186
|
+
# Create a special tag for declarations
|
|
187
|
+
decl_tag = Tag('!DOCTYPE')
|
|
188
|
+
decl_tag.attrs['content'] = decl
|
|
189
|
+
self._scout_parser.add_tag(decl_tag)
|
|
190
|
+
|
|
191
|
+
def handle_pi(self, data: str):
|
|
192
|
+
"""
|
|
193
|
+
Handle processing instructions.
|
|
194
|
+
|
|
195
|
+
Args:
|
|
196
|
+
data (str): Processing instruction content
|
|
197
|
+
"""
|
|
198
|
+
# Create a special tag for processing instructions
|
|
199
|
+
pi_tag = Tag('?')
|
|
200
|
+
pi_tag.attrs['content'] = data
|
|
201
|
+
self._scout_parser.add_tag(pi_tag)
|
|
202
|
+
|
|
203
|
+
def handle_entityref(self, name: str):
|
|
204
|
+
"""
|
|
205
|
+
Handle HTML entity references.
|
|
206
|
+
|
|
207
|
+
Args:
|
|
208
|
+
name (str): Entity reference name
|
|
209
|
+
"""
|
|
210
|
+
# Convert entity references to their actual characters
|
|
211
|
+
char = html.entities.html5.get(name, f'&{name};')
|
|
212
|
+
self._scout_parser.add_text(char)
|
|
213
|
+
|
|
214
|
+
def handle_charref(self, name: str):
|
|
215
|
+
"""
|
|
216
|
+
Handle character references.
|
|
217
|
+
|
|
218
|
+
Args:
|
|
219
|
+
name (str): Character reference name
|
|
220
|
+
"""
|
|
221
|
+
# Convert character references to their actual characters
|
|
222
|
+
try:
|
|
223
|
+
if name.startswith('x'):
|
|
224
|
+
char = chr(int(name[1:], 16))
|
|
225
|
+
else:
|
|
226
|
+
char = chr(int(name))
|
|
227
|
+
self._scout_parser.add_text(char)
|
|
228
|
+
except ValueError:
|
|
229
|
+
# Fallback for invalid references
|
|
230
|
+
self._scout_parser.add_text(f'&#{name};')
|
|
231
|
+
|
|
232
|
+
def close(self):
|
|
233
|
+
"""
|
|
234
|
+
Finalize parsing and perform cleanup.
|
|
235
|
+
"""
|
|
236
|
+
super().close()
|