web-scanner 2.1.4__tar.gz → 2.1.5__tar.gz

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 (42) hide show
  1. {web_scanner-2.1.4 → web_scanner-2.1.5}/PKG-INFO +2 -2
  2. {web_scanner-2.1.4 → web_scanner-2.1.5}/README.md +1 -1
  3. {web_scanner-2.1.4 → web_scanner-2.1.5}/webscanner/__init__.py +1 -1
  4. {web_scanner-2.1.4 → web_scanner-2.1.5}/webscanner/modules/seo.py +46 -7
  5. {web_scanner-2.1.4 → web_scanner-2.1.5}/.gitignore +0 -0
  6. {web_scanner-2.1.4 → web_scanner-2.1.5}/LICENSE +0 -0
  7. {web_scanner-2.1.4 → web_scanner-2.1.5}/app.py +0 -0
  8. {web_scanner-2.1.4 → web_scanner-2.1.5}/pyproject.toml +0 -0
  9. {web_scanner-2.1.4 → web_scanner-2.1.5}/requirements.txt +0 -0
  10. {web_scanner-2.1.4 → web_scanner-2.1.5}/webscanner/__main__.py +0 -0
  11. {web_scanner-2.1.4 → web_scanner-2.1.5}/webscanner/cli.py +0 -0
  12. {web_scanner-2.1.4 → web_scanner-2.1.5}/webscanner/colors.py +0 -0
  13. {web_scanner-2.1.4 → web_scanner-2.1.5}/webscanner/core/__init__.py +0 -0
  14. {web_scanner-2.1.4 → web_scanner-2.1.5}/webscanner/core/context.py +0 -0
  15. {web_scanner-2.1.4 → web_scanner-2.1.5}/webscanner/core/models.py +0 -0
  16. {web_scanner-2.1.4 → web_scanner-2.1.5}/webscanner/core/module.py +0 -0
  17. {web_scanner-2.1.4 → web_scanner-2.1.5}/webscanner/core/scanner.py +0 -0
  18. {web_scanner-2.1.4 → web_scanner-2.1.5}/webscanner/helpers.py +0 -0
  19. {web_scanner-2.1.4 → web_scanner-2.1.5}/webscanner/modules/__init__.py +0 -0
  20. {web_scanner-2.1.4 → web_scanner-2.1.5}/webscanner/modules/dns.py +0 -0
  21. {web_scanner-2.1.4 → web_scanner-2.1.5}/webscanner/modules/headers.py +0 -0
  22. {web_scanner-2.1.4 → web_scanner-2.1.5}/webscanner/modules/links.py +0 -0
  23. {web_scanner-2.1.4 → web_scanner-2.1.5}/webscanner/modules/security.py +0 -0
  24. {web_scanner-2.1.4 → web_scanner-2.1.5}/webscanner/modules/sitemap.py +0 -0
  25. {web_scanner-2.1.4 → web_scanner-2.1.5}/webscanner/modules/ssl.py +0 -0
  26. {web_scanner-2.1.4 → web_scanner-2.1.5}/webscanner/modules/subdomains.py +0 -0
  27. {web_scanner-2.1.4 → web_scanner-2.1.5}/webscanner/modules/tech.py +0 -0
  28. {web_scanner-2.1.4 → web_scanner-2.1.5}/webscanner/modules/whois.py +0 -0
  29. {web_scanner-2.1.4 → web_scanner-2.1.5}/webscanner/net/__init__.py +0 -0
  30. {web_scanner-2.1.4 → web_scanner-2.1.5}/webscanner/net/agents.py +0 -0
  31. {web_scanner-2.1.4 → web_scanner-2.1.5}/webscanner/net/data/public_suffix_list.dat +0 -0
  32. {web_scanner-2.1.4 → web_scanner-2.1.5}/webscanner/net/http.py +0 -0
  33. {web_scanner-2.1.4 → web_scanner-2.1.5}/webscanner/net/psl.py +0 -0
  34. {web_scanner-2.1.4 → web_scanner-2.1.5}/webscanner/ui/__init__.py +0 -0
  35. {web_scanner-2.1.4 → web_scanner-2.1.5}/webscanner/ui/activity.py +0 -0
  36. {web_scanner-2.1.4 → web_scanner-2.1.5}/webscanner/ui/app.py +0 -0
  37. {web_scanner-2.1.4 → web_scanner-2.1.5}/webscanner/ui/app.tcss +0 -0
  38. {web_scanner-2.1.4 → web_scanner-2.1.5}/webscanner/ui/data/countries.json +0 -0
  39. {web_scanner-2.1.4 → web_scanner-2.1.5}/webscanner/ui/export.py +0 -0
  40. {web_scanner-2.1.4 → web_scanner-2.1.5}/webscanner/ui/tables.py +0 -0
  41. {web_scanner-2.1.4 → web_scanner-2.1.5}/webscanner/ui/widgets.py +0 -0
  42. {web_scanner-2.1.4 → web_scanner-2.1.5}/webscanner/ui/worldmap.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: web-scanner
3
- Version: 2.1.4
3
+ Version: 2.1.5
4
4
  Summary: An async Textual TUI for website reconnaissance — DNS, WHOIS, TLS, security, tech-stack and SEO, with no paid APIs.
5
5
  Project-URL: Homepage, https://github.com/iamramizk/web-scanner
6
6
  Project-URL: Repository, https://github.com/iamramizk/web-scanner
@@ -55,7 +55,7 @@ Description-Content-Type: text/markdown
55
55
  _An async Textual TUI for website reconnaissance — DNS, WHOIS, TLS, security, tech-stack and SEO, with no paid APIs._
56
56
 
57
57
  [![Python](https://img.shields.io/badge/Python-3.11%2B-3776AB?style=for-the-badge&logo=python&logoColor=white)](https://www.python.org/downloads/)
58
- [![PyPI Downloads](https://img.shields.io/pypi/dm/web-scanner?style=for-the-badge&logo=pypi&logoColor=white&color=006DAD)](https://pypi.org/project/web-scanner/)
58
+ [![PyPI Downloads](https://img.shields.io/pepy/dt/web-scanner?style=for-the-badge&logo=pypi&logoColor=white&color=006DAD)](https://pypi.org/project/web-scanner/)
59
59
  [![Release](https://img.shields.io/github/v/release/iamramizk/web-scanner?style=for-the-badge&logo=github&color=2EA44F)](https://github.com/iamramizk/web-scanner/releases/latest)
60
60
 
61
61
  </div>
@@ -5,7 +5,7 @@
5
5
  _An async Textual TUI for website reconnaissance — DNS, WHOIS, TLS, security, tech-stack and SEO, with no paid APIs._
6
6
 
7
7
  [![Python](https://img.shields.io/badge/Python-3.11%2B-3776AB?style=for-the-badge&logo=python&logoColor=white)](https://www.python.org/downloads/)
8
- [![PyPI Downloads](https://img.shields.io/pypi/dm/web-scanner?style=for-the-badge&logo=pypi&logoColor=white&color=006DAD)](https://pypi.org/project/web-scanner/)
8
+ [![PyPI Downloads](https://img.shields.io/pepy/dt/web-scanner?style=for-the-badge&logo=pypi&logoColor=white&color=006DAD)](https://pypi.org/project/web-scanner/)
9
9
  [![Release](https://img.shields.io/github/v/release/iamramizk/web-scanner?style=for-the-badge&logo=github&color=2EA44F)](https://github.com/iamramizk/web-scanner/releases/latest)
10
10
 
11
11
  </div>
@@ -1,3 +1,3 @@
1
1
  """web-scanner v2 — async website reconnaissance TUI package."""
2
2
 
3
- __version__ = "2.1.4"
3
+ __version__ = "2.1.5"
@@ -39,11 +39,38 @@ SOCIAL_DOMAINS = (
39
39
  )
40
40
 
41
41
  _STOPWORDS = frozenset(
42
- "the a an and or but if of to in on for with as by at from is are was were be "
43
- "been being this that these those it its you your we our us they their he she "
44
- "his her not no so do does did has have had can will would should could may "
45
- "might just than then there here what which who when where how all any some "
46
- "more most other into out up down over under about also new get one two".split()
42
+ (
43
+ # core articles / conjunctions / prepositions / pronouns
44
+ "the a an and or but if of to in on for with as by at from is are was were be "
45
+ "been being this that these those it its you your we our us they their he she "
46
+ "his her not no so do does did has have had can will would should could may "
47
+ "might just than then there here what which who when where how all any some "
48
+ "more most other into out up down over under about also new get one two "
49
+ # additional pronouns / possessives
50
+ "him them myself yourself himself herself itself ourselves themselves "
51
+ "whom whose mine yours ours hers theirs "
52
+ # modals / auxiliaries
53
+ "shall must cannot ought need used "
54
+ # conjunctions / subordinators
55
+ "nor yet because while since until unless although though whether whereas "
56
+ "whenever wherever whoever whatever "
57
+ # prepositions
58
+ "off per via upon above below beneath between among amongst throughout during "
59
+ "before after against without within along across behind beyond near toward "
60
+ "towards onto "
61
+ # determiners / quantifiers
62
+ "each every either neither both few many much such own same only very too quite "
63
+ "rather enough none another several "
64
+ # common adverbs / fillers
65
+ "back even still again ever never always often once now thus hence therefore "
66
+ "however moreover otherwise indeed perhaps maybe actually really simply almost "
67
+ "already else yes why well like "
68
+ # contractions (the tokenizer keeps apostrophes, so these survive verbatim)
69
+ "don't doesn't didn't isn't aren't wasn't weren't won't wouldn't can't couldn't "
70
+ "shouldn't hasn't haven't hadn't i'm i've i'll i'd you're you've you'll you'd "
71
+ "we're we've we'll we'd they're they've they'll it's he's she's that's there's "
72
+ "here's what's who's let's"
73
+ ).split()
47
74
  )
48
75
 
49
76
 
@@ -99,6 +126,18 @@ def _len_line(text: str, lo: int, hi: int) -> str:
99
126
  return f"[{MUTED}]{escape(text)}[/]\n[{colour}]{n} chars · rec. {lo}-{hi}[/]"
100
127
 
101
128
 
129
+ def _text(el) -> str:
130
+ """Element text as the browser's `textContent` sees it, whitespace collapsed.
131
+
132
+ `get_text(strip=True)` strips each child text node then joins with no
133
+ separator, so words split across spans/newlines fuse ("One <span>Two</span>
134
+ <span>Three</span>" -> "OneTwoThree"). Taking the raw text and re-joining on
135
+ whitespace keeps the gaps that actually exist between nodes without inventing
136
+ any where the DOM has none (e.g. `<br>`).
137
+ """
138
+ return " ".join(el.get_text().split())
139
+
140
+
102
141
  class SeoModule(ScanModule):
103
142
  name = "seo"
104
143
  label = "SEO"
@@ -141,7 +180,7 @@ class SeoModule(ScanModule):
141
180
 
142
181
  # --- Content ---
143
182
  title_el = soup.find("title")
144
- title = title_el.get_text(strip=True) if title_el else None
183
+ title = _text(title_el) if title_el else None
145
184
  desc_el = soup.find("meta", attrs={"name": "description"})
146
185
  desc = (desc_el.get("content") or "").strip() if desc_el else None
147
186
  content: dict[str, object] = {
@@ -149,7 +188,7 @@ class SeoModule(ScanModule):
149
188
  "Description": _len_line(desc, *DESC_RANGE) if desc else "-",
150
189
  }
151
190
  for lvl in range(1, 4):
152
- content[f"H{lvl}"] = [h.get_text(strip=True) for h in soup.find_all(f"h{lvl}")]
191
+ content[f"H{lvl}"] = [_text(h) for h in soup.find_all(f"h{lvl}")]
153
192
  content["Socials"] = sorted({
154
193
  tag["href"].rstrip("/")
155
194
  for tag in soup.find_all("a", href=True)
File without changes
File without changes
File without changes
File without changes