stealth-web-crawler 0.8.0__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 (181) hide show
  1. stealth_web_crawler-0.8.0/LICENSE +21 -0
  2. stealth_web_crawler-0.8.0/PKG-INFO +547 -0
  3. stealth_web_crawler-0.8.0/README.md +479 -0
  4. stealth_web_crawler-0.8.0/pyproject.toml +185 -0
  5. stealth_web_crawler-0.8.0/setup.cfg +4 -0
  6. stealth_web_crawler-0.8.0/src/stealth_web_crawler.egg-info/PKG-INFO +547 -0
  7. stealth_web_crawler-0.8.0/src/stealth_web_crawler.egg-info/SOURCES.txt +179 -0
  8. stealth_web_crawler-0.8.0/src/stealth_web_crawler.egg-info/dependency_links.txt +1 -0
  9. stealth_web_crawler-0.8.0/src/stealth_web_crawler.egg-info/entry_points.txt +5 -0
  10. stealth_web_crawler-0.8.0/src/stealth_web_crawler.egg-info/requires.txt +46 -0
  11. stealth_web_crawler-0.8.0/src/stealth_web_crawler.egg-info/top_level.txt +1 -0
  12. stealth_web_crawler-0.8.0/src/web_crawler/__init__.py +292 -0
  13. stealth_web_crawler-0.8.0/src/web_crawler/_ssrf.py +302 -0
  14. stealth_web_crawler-0.8.0/src/web_crawler/_types.py +224 -0
  15. stealth_web_crawler-0.8.0/src/web_crawler/ai/__init__.py +152 -0
  16. stealth_web_crawler-0.8.0/src/web_crawler/ai/_jsonutil.py +79 -0
  17. stealth_web_crawler-0.8.0/src/web_crawler/ai/_reverse_actions.py +377 -0
  18. stealth_web_crawler-0.8.0/src/web_crawler/ai/_reverse_prompt.py +166 -0
  19. stealth_web_crawler-0.8.0/src/web_crawler/ai/_reverse_support.py +398 -0
  20. stealth_web_crawler-0.8.0/src/web_crawler/ai/agent.py +293 -0
  21. stealth_web_crawler-0.8.0/src/web_crawler/ai/analyzer.py +527 -0
  22. stealth_web_crawler-0.8.0/src/web_crawler/ai/captcha.py +824 -0
  23. stealth_web_crawler-0.8.0/src/web_crawler/ai/checkpoint.py +323 -0
  24. stealth_web_crawler-0.8.0/src/web_crawler/ai/confidence.py +506 -0
  25. stealth_web_crawler-0.8.0/src/web_crawler/ai/dom_pruner.py +454 -0
  26. stealth_web_crawler-0.8.0/src/web_crawler/ai/extractor.py +210 -0
  27. stealth_web_crawler-0.8.0/src/web_crawler/ai/guardrails.py +590 -0
  28. stealth_web_crawler-0.8.0/src/web_crawler/ai/hooks.py +549 -0
  29. stealth_web_crawler-0.8.0/src/web_crawler/ai/image_captcha.py +635 -0
  30. stealth_web_crawler-0.8.0/src/web_crawler/ai/judge.py +239 -0
  31. stealth_web_crawler-0.8.0/src/web_crawler/ai/llm.py +720 -0
  32. stealth_web_crawler-0.8.0/src/web_crawler/ai/loop.py +318 -0
  33. stealth_web_crawler-0.8.0/src/web_crawler/ai/planner.py +216 -0
  34. stealth_web_crawler-0.8.0/src/web_crawler/ai/recorder.py +516 -0
  35. stealth_web_crawler-0.8.0/src/web_crawler/ai/reverse_agent.py +1181 -0
  36. stealth_web_crawler-0.8.0/src/web_crawler/ai/schema.py +273 -0
  37. stealth_web_crawler-0.8.0/src/web_crawler/ai/watchdog.py +339 -0
  38. stealth_web_crawler-0.8.0/src/web_crawler/app/__init__.py +1 -0
  39. stealth_web_crawler-0.8.0/src/web_crawler/app/_crawler_cli.py +159 -0
  40. stealth_web_crawler-0.8.0/src/web_crawler/app/_crawler_context.py +194 -0
  41. stealth_web_crawler-0.8.0/src/web_crawler/app/_crawler_core.py +319 -0
  42. stealth_web_crawler-0.8.0/src/web_crawler/app/_crawler_discovery.py +168 -0
  43. stealth_web_crawler-0.8.0/src/web_crawler/app/_crawler_download.py +326 -0
  44. stealth_web_crawler-0.8.0/src/web_crawler/app/_crawler_post.py +237 -0
  45. stealth_web_crawler-0.8.0/src/web_crawler/app/_crawler_scan.py +210 -0
  46. stealth_web_crawler-0.8.0/src/web_crawler/app/_report_context.py +282 -0
  47. stealth_web_crawler-0.8.0/src/web_crawler/app/_report_extract.py +291 -0
  48. stealth_web_crawler-0.8.0/src/web_crawler/app/_report_html.py +358 -0
  49. stealth_web_crawler-0.8.0/src/web_crawler/app/_report_manifests.py +144 -0
  50. stealth_web_crawler-0.8.0/src/web_crawler/app/_report_markdown.py +311 -0
  51. stealth_web_crawler-0.8.0/src/web_crawler/app/_ui_helpers.py +399 -0
  52. stealth_web_crawler-0.8.0/src/web_crawler/app/_ui_http.py +647 -0
  53. stealth_web_crawler-0.8.0/src/web_crawler/app/_ui_runner.py +341 -0
  54. stealth_web_crawler-0.8.0/src/web_crawler/app/_ui_state.py +290 -0
  55. stealth_web_crawler-0.8.0/src/web_crawler/app/crawler.py +262 -0
  56. stealth_web_crawler-0.8.0/src/web_crawler/app/crawler_models.py +39 -0
  57. stealth_web_crawler-0.8.0/src/web_crawler/app/crawler_net.py +591 -0
  58. stealth_web_crawler-0.8.0/src/web_crawler/app/crawler_report.py +102 -0
  59. stealth_web_crawler-0.8.0/src/web_crawler/app/db.py +458 -0
  60. stealth_web_crawler-0.8.0/src/web_crawler/app/py.typed +0 -0
  61. stealth_web_crawler-0.8.0/src/web_crawler/app/static/index.html +1787 -0
  62. stealth_web_crawler-0.8.0/src/web_crawler/app/ui.py +117 -0
  63. stealth_web_crawler-0.8.0/src/web_crawler/compat.py +77 -0
  64. stealth_web_crawler-0.8.0/src/web_crawler/crawler.py +187 -0
  65. stealth_web_crawler-0.8.0/src/web_crawler/exceptions.py +194 -0
  66. stealth_web_crawler-0.8.0/src/web_crawler/fetchers/__init__.py +66 -0
  67. stealth_web_crawler-0.8.0/src/web_crawler/fetchers/_base.py +186 -0
  68. stealth_web_crawler-0.8.0/src/web_crawler/fetchers/_download.py +516 -0
  69. stealth_web_crawler-0.8.0/src/web_crawler/fetchers/_pin.py +250 -0
  70. stealth_web_crawler-0.8.0/src/web_crawler/fetchers/camoufox.py +273 -0
  71. stealth_web_crawler-0.8.0/src/web_crawler/fetchers/dynamic.py +887 -0
  72. stealth_web_crawler-0.8.0/src/web_crawler/fetchers/fetcher.py +1481 -0
  73. stealth_web_crawler-0.8.0/src/web_crawler/fetchers/proxy.py +144 -0
  74. stealth_web_crawler-0.8.0/src/web_crawler/fetchers/session.py +660 -0
  75. stealth_web_crawler-0.8.0/src/web_crawler/fetchers/stealthy.py +230 -0
  76. stealth_web_crawler-0.8.0/src/web_crawler/mcp/__init__.py +34 -0
  77. stealth_web_crawler-0.8.0/src/web_crawler/mcp/_host.py +73 -0
  78. stealth_web_crawler-0.8.0/src/web_crawler/mcp/_server_helpers.py +144 -0
  79. stealth_web_crawler-0.8.0/src/web_crawler/mcp/_specs.py +325 -0
  80. stealth_web_crawler-0.8.0/src/web_crawler/mcp/_ssrf_gate.py +103 -0
  81. stealth_web_crawler-0.8.0/src/web_crawler/mcp/_tools_pentest.py +285 -0
  82. stealth_web_crawler-0.8.0/src/web_crawler/mcp/_tools_reverse.py +344 -0
  83. stealth_web_crawler-0.8.0/src/web_crawler/mcp/_transport.py +494 -0
  84. stealth_web_crawler-0.8.0/src/web_crawler/mcp/cli.py +680 -0
  85. stealth_web_crawler-0.8.0/src/web_crawler/mcp/server.py +546 -0
  86. stealth_web_crawler-0.8.0/src/web_crawler/parser/__init__.py +18 -0
  87. stealth_web_crawler-0.8.0/src/web_crawler/parser/adaptive.py +152 -0
  88. stealth_web_crawler-0.8.0/src/web_crawler/parser/links.py +225 -0
  89. stealth_web_crawler-0.8.0/src/web_crawler/parser/selector.py +649 -0
  90. stealth_web_crawler-0.8.0/src/web_crawler/parser/storage.py +131 -0
  91. stealth_web_crawler-0.8.0/src/web_crawler/parser/visual.py +254 -0
  92. stealth_web_crawler-0.8.0/src/web_crawler/pentest/__init__.py +75 -0
  93. stealth_web_crawler-0.8.0/src/web_crawler/pentest/_common.py +69 -0
  94. stealth_web_crawler-0.8.0/src/web_crawler/pentest/dir_bruter.py +252 -0
  95. stealth_web_crawler-0.8.0/src/web_crawler/pentest/header_check.py +352 -0
  96. stealth_web_crawler-0.8.0/src/web_crawler/pentest/port_scanner.py +322 -0
  97. stealth_web_crawler-0.8.0/src/web_crawler/pentest/report.py +86 -0
  98. stealth_web_crawler-0.8.0/src/web_crawler/pentest/subdomain.py +182 -0
  99. stealth_web_crawler-0.8.0/src/web_crawler/pentest/vuln_scanner.py +301 -0
  100. stealth_web_crawler-0.8.0/src/web_crawler/py.typed +0 -0
  101. stealth_web_crawler-0.8.0/src/web_crawler/response.py +101 -0
  102. stealth_web_crawler-0.8.0/src/web_crawler/robots.py +166 -0
  103. stealth_web_crawler-0.8.0/src/web_crawler/spider/__init__.py +57 -0
  104. stealth_web_crawler-0.8.0/src/web_crawler/spider/autothrottle.py +251 -0
  105. stealth_web_crawler-0.8.0/src/web_crawler/spider/cache.py +432 -0
  106. stealth_web_crawler-0.8.0/src/web_crawler/spider/crawl.py +133 -0
  107. stealth_web_crawler-0.8.0/src/web_crawler/spider/feed.py +156 -0
  108. stealth_web_crawler-0.8.0/src/web_crawler/spider/sitemap.py +257 -0
  109. stealth_web_crawler-0.8.0/src/web_crawler/spider/spider.py +1069 -0
  110. stealth_web_crawler-0.8.0/src/web_crawler/spider/validation.py +260 -0
  111. stealth_web_crawler-0.8.0/tests/test_adaptive.py +180 -0
  112. stealth_web_crawler-0.8.0/tests/test_adaptive_api.py +121 -0
  113. stealth_web_crawler-0.8.0/tests/test_agent.py +740 -0
  114. stealth_web_crawler-0.8.0/tests/test_agent_block.py +113 -0
  115. stealth_web_crawler-0.8.0/tests/test_ai_init.py +168 -0
  116. stealth_web_crawler-0.8.0/tests/test_analyzer.py +840 -0
  117. stealth_web_crawler-0.8.0/tests/test_app_crawler.py +4260 -0
  118. stealth_web_crawler-0.8.0/tests/test_app_ui.py +2273 -0
  119. stealth_web_crawler-0.8.0/tests/test_async_fetcher.py +75 -0
  120. stealth_web_crawler-0.8.0/tests/test_autothrottle.py +629 -0
  121. stealth_web_crawler-0.8.0/tests/test_benchmark_regression.py +127 -0
  122. stealth_web_crawler-0.8.0/tests/test_cache.py +695 -0
  123. stealth_web_crawler-0.8.0/tests/test_camoufox_fetcher.py +864 -0
  124. stealth_web_crawler-0.8.0/tests/test_captcha.py +1726 -0
  125. stealth_web_crawler-0.8.0/tests/test_checkpoint.py +595 -0
  126. stealth_web_crawler-0.8.0/tests/test_cli_run.py +189 -0
  127. stealth_web_crawler-0.8.0/tests/test_compat.py +51 -0
  128. stealth_web_crawler-0.8.0/tests/test_confidence.py +837 -0
  129. stealth_web_crawler-0.8.0/tests/test_crawl_spider.py +255 -0
  130. stealth_web_crawler-0.8.0/tests/test_crawler.py +108 -0
  131. stealth_web_crawler-0.8.0/tests/test_crawler_unit.py +454 -0
  132. stealth_web_crawler-0.8.0/tests/test_db.py +482 -0
  133. stealth_web_crawler-0.8.0/tests/test_dom_pruner.py +609 -0
  134. stealth_web_crawler-0.8.0/tests/test_dom_traversal.py +89 -0
  135. stealth_web_crawler-0.8.0/tests/test_download.py +1308 -0
  136. stealth_web_crawler-0.8.0/tests/test_dynamic_fetcher.py +2006 -0
  137. stealth_web_crawler-0.8.0/tests/test_e2e_features.py +415 -0
  138. stealth_web_crawler-0.8.0/tests/test_e2e_reverse_agent.py +309 -0
  139. stealth_web_crawler-0.8.0/tests/test_entry_points.py +61 -0
  140. stealth_web_crawler-0.8.0/tests/test_exceptions.py +789 -0
  141. stealth_web_crawler-0.8.0/tests/test_extractor.py +211 -0
  142. stealth_web_crawler-0.8.0/tests/test_feed.py +754 -0
  143. stealth_web_crawler-0.8.0/tests/test_fetcher.py +2829 -0
  144. stealth_web_crawler-0.8.0/tests/test_guardrails.py +910 -0
  145. stealth_web_crawler-0.8.0/tests/test_hooks.py +117 -0
  146. stealth_web_crawler-0.8.0/tests/test_image_captcha.py +1212 -0
  147. stealth_web_crawler-0.8.0/tests/test_judge.py +681 -0
  148. stealth_web_crawler-0.8.0/tests/test_lazy_import.py +106 -0
  149. stealth_web_crawler-0.8.0/tests/test_links.py +270 -0
  150. stealth_web_crawler-0.8.0/tests/test_llm.py +890 -0
  151. stealth_web_crawler-0.8.0/tests/test_loop.py +443 -0
  152. stealth_web_crawler-0.8.0/tests/test_mcp_cli.py +1089 -0
  153. stealth_web_crawler-0.8.0/tests/test_mcp_server.py +3290 -0
  154. stealth_web_crawler-0.8.0/tests/test_net_perf.py +702 -0
  155. stealth_web_crawler-0.8.0/tests/test_parser.py +707 -0
  156. stealth_web_crawler-0.8.0/tests/test_pentest.py +1158 -0
  157. stealth_web_crawler-0.8.0/tests/test_pentest_common.py +143 -0
  158. stealth_web_crawler-0.8.0/tests/test_planner.py +419 -0
  159. stealth_web_crawler-0.8.0/tests/test_proxy.py +115 -0
  160. stealth_web_crawler-0.8.0/tests/test_public_api.py +118 -0
  161. stealth_web_crawler-0.8.0/tests/test_recorder.py +799 -0
  162. stealth_web_crawler-0.8.0/tests/test_response.py +89 -0
  163. stealth_web_crawler-0.8.0/tests/test_reverse_actions.py +193 -0
  164. stealth_web_crawler-0.8.0/tests/test_reverse_agent_browser.py +949 -0
  165. stealth_web_crawler-0.8.0/tests/test_reverse_agent_integration.py +205 -0
  166. stealth_web_crawler-0.8.0/tests/test_reverse_agent_loop.py +1943 -0
  167. stealth_web_crawler-0.8.0/tests/test_reverse_agent_unit.py +3058 -0
  168. stealth_web_crawler-0.8.0/tests/test_robots.py +356 -0
  169. stealth_web_crawler-0.8.0/tests/test_schema.py +414 -0
  170. stealth_web_crawler-0.8.0/tests/test_session.py +1003 -0
  171. stealth_web_crawler-0.8.0/tests/test_sitemap_spider.py +596 -0
  172. stealth_web_crawler-0.8.0/tests/test_spider.py +2145 -0
  173. stealth_web_crawler-0.8.0/tests/test_spider_stats.py +707 -0
  174. stealth_web_crawler-0.8.0/tests/test_ssrf_guard.py +353 -0
  175. stealth_web_crawler-0.8.0/tests/test_ssrf_pin.py +653 -0
  176. stealth_web_crawler-0.8.0/tests/test_stealthy_fetcher.py +823 -0
  177. stealth_web_crawler-0.8.0/tests/test_types.py +335 -0
  178. stealth_web_crawler-0.8.0/tests/test_validation.py +273 -0
  179. stealth_web_crawler-0.8.0/tests/test_visual.py +331 -0
  180. stealth_web_crawler-0.8.0/tests/test_vuln_scanner.py +603 -0
  181. stealth_web_crawler-0.8.0/tests/test_watchdog.py +529 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 xiaomaozjj666
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,547 @@
1
+ Metadata-Version: 2.4
2
+ Name: stealth-web-crawler
3
+ Version: 0.8.0
4
+ Summary: A Scrapling-aligned stealth web scraping library: adaptive selectors, TLS-fingerprint HTTP, JS rendering, and a spider framework.
5
+ Author: web-crawler contributors
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/xiaomaozjj666/web-crawler
8
+ Project-URL: Repository, https://github.com/xiaomaozjj666/web-crawler
9
+ Project-URL: Documentation, https://xiaomaozjj666.github.io/web-crawler/
10
+ Project-URL: Changelog, https://github.com/xiaomaozjj666/web-crawler/blob/master/CHANGELOG.md
11
+ Project-URL: Issues, https://github.com/xiaomaozjj666/web-crawler/issues
12
+ Keywords: web-scraping,scraping,automation,browser-automation,data-extraction,html-parsing,undetectable,playwright,web-crawler,crawling,headless,scraper,chrome
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Intended Audience :: Information Technology
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Programming Language :: Python :: 3.14
24
+ Classifier: Programming Language :: Python :: Implementation :: CPython
25
+ Classifier: Topic :: Internet :: WWW/HTTP
26
+ Classifier: Topic :: Internet :: WWW/HTTP :: Browsers
27
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
28
+ Classifier: Topic :: Text Processing :: Markup :: HTML
29
+ Classifier: Typing :: Typed
30
+ Requires-Python: >=3.10
31
+ Description-Content-Type: text/markdown
32
+ License-File: LICENSE
33
+ Requires-Dist: httpx[http2]<1,>=0.28.1
34
+ Requires-Dist: beautifulsoup4<5,>=4.12
35
+ Requires-Dist: lxml<7,>=5.2
36
+ Requires-Dist: cssselect<2,>=1.6.0; python_version >= "3.11"
37
+ Requires-Dist: cssselect<2,>=1.5.0; python_version < "3.11"
38
+ Provides-Extra: visual
39
+ Requires-Dist: Pillow<13,>=12.3.0; extra == "visual"
40
+ Provides-Extra: all
41
+ Requires-Dist: curl_cffi<1,>=0.9; extra == "all"
42
+ Requires-Dist: playwright<2,>=1.60.0; extra == "all"
43
+ Requires-Dist: pydantic<3,>=2.14.1; extra == "all"
44
+ Provides-Extra: camoufox
45
+ Requires-Dist: camoufox[geoip]<1,>=0.5.8; extra == "camoufox"
46
+ Provides-Extra: captcha
47
+ Requires-Dist: ddddocr<2,>=1.6.1; extra == "captcha"
48
+ Requires-Dist: numpy<3,>=2.2.6; extra == "captcha"
49
+ Provides-Extra: crypto
50
+ Requires-Dist: pycryptodome<5,>=4.0.1; extra == "crypto"
51
+ Provides-Extra: mcp
52
+ Requires-Dist: mcp<3,>=2.3.0; extra == "mcp"
53
+ Requires-Dist: camoufox[geoip]<1,>=0.5.8; extra == "mcp"
54
+ Requires-Dist: pydantic<3,>=2.14.1; extra == "mcp"
55
+ Provides-Extra: dev
56
+ Requires-Dist: pytest<10,>=9.1.1; extra == "dev"
57
+ Requires-Dist: pytest-cov<8,>=7.1.0; extra == "dev"
58
+ Requires-Dist: pytest-asyncio<2,>=1.4.0; extra == "dev"
59
+ Requires-Dist: ruff<0.18,>=0.17.1; extra == "dev"
60
+ Requires-Dist: mypy<3,>=2.4.0; extra == "dev"
61
+ Requires-Dist: types-beautifulsoup4<5,>=4.12.0.20250516; extra == "dev"
62
+ Requires-Dist: pydantic<3,>=2.14.1; extra == "dev"
63
+ Provides-Extra: docs
64
+ Requires-Dist: mkdocs<2,>=1.6.1; extra == "docs"
65
+ Requires-Dist: mkdocs-material<10,>=9.7.7; extra == "docs"
66
+ Requires-Dist: mkdocstrings[python]<2,>=1.0.8; extra == "docs"
67
+ Dynamic: license-file
68
+
69
+ # web-crawler
70
+
71
+ <p align="center">
72
+ <img src="https://img.shields.io/badge/Python-3.10%2B-3776AB" alt="Python 3.10+" />
73
+ <img src="https://img.shields.io/badge/lxml-adaptive%20selector-8CA1B6" alt="lxml 自适应选择器" />
74
+ <img src="https://img.shields.io/badge/TLS-impersonation-0F6B9E" alt="TLS 指纹隐身" />
75
+ <img src="https://github.com/xiaomaozjj666/web-crawler/actions/workflows/ci.yml/badge.svg" alt="CI" />
76
+ <img src="https://img.shields.io/badge/coverage-92%25%2F97%25%20(CI%20gate)-brightgreen" alt="覆盖率 CI 门禁:minimal-deps ≥92%、full-deps ≥97%(非实时数据)" />
77
+ <a href="https://xiaomaozjj666.github.io/web-crawler/"><img src="https://img.shields.io/badge/docs-online-brightgreen" alt="文档站" /></a>
78
+ <img src="https://img.shields.io/badge/license-MIT-blue" alt="MIT License" />
79
+ </p>
80
+
81
+ Scrapling 风格的隐身网页爬虫库:**自适应选择器**、**TLS 指纹隐身 HTTP**、**JS 渲染**与**回调式 Spider 框架**,另附一套应用层资源下载器与本地 Web UI。
82
+
83
+ 📖 **完整文档**(架构详解 / API 参考 / 逆向 Agent 手册):[https://xiaomaozjj666.github.io/web-crawler/](https://xiaomaozjj666.github.io/web-crawler/)
84
+
85
+ ## 分层架构
86
+
87
+ ```mermaid
88
+ flowchart TB
89
+ subgraph APP["应用层 app/"]
90
+ C1["资源下载器 CLI"]
91
+ C2["本地 Web UI<br/>SSE 实时推送"]
92
+ C3["MCP 服务 / CLI"]
93
+ C4["JS 逆向 Agent CLI"]
94
+ end
95
+ subgraph CORE["核心库 src/web_crawler/"]
96
+ F["Fetchers 层<br/>Fetcher · AsyncFetcher · DynamicFetcher<br/>StealthyFetcher · CamoufoxFetcher · ProxyPool"]
97
+ P["Parser 层<br/>Selector 自适应解析 · 元素指纹 · 重定位"]
98
+ S["Spider 层<br/>回调分发 · 优先级 · 暂停续跑"]
99
+ A["AI 层<br/>AIExtractor · AIScrapeAgent · ReverseAgent<br/>ImageCaptchaSolver"]
100
+ PT["pentest 子包<br/>授权安全研究 · 侦察工具集"]
101
+ end
102
+ APP --> F
103
+ F --> P
104
+ F --> S
105
+ A --> F
106
+ A --> P
107
+ PT -.->|"授权测试"| F
108
+ F --> R["统一 Response<br/>.css / .xpath / .json"]
109
+ ```
110
+
111
+ ## 界面预览
112
+
113
+ 本地 Web UI(`python -m web_crawler.app.ui`,默认 http://127.0.0.1:8765):网页采集(图片/JS/视频/文件下载、并发与断点续传)、JS 逆向 Agent、任务历史三个入口,SSE 实时推送进度与日志。
114
+
115
+ | 网页采集控制台 · 本地 Web UI |
116
+ | :---: |
117
+ | <img src="docs/screenshots/ui-console.png" alt="Web Crawler 控制台" width="820" /> |
118
+
119
+ ## 功能特性
120
+
121
+ - **自适应解析器** — `Selector` 基于 `lxml`,支持元素指纹计算与结构相似度重定位:站点改版导致选择器失效时,已保存的指纹会自动在新页面中重新找到对应元素(Scrapling 的标志性能力)。公开 `save` / `retrieve` / `relocate` API 便于显式管理指纹;内置 `find_by_regex`、`re` / `re_first`、`get_all_text`、`prettify`、完整 DOM 遍历(`parent` / `children` / `siblings` / `next` / `previous` / `path`),以及 `ResultList` 批量辅助方法(`css` / `xpath` / `get` / `getall` / `.first` / `.last`)。支持 Scrapling 风格 `::attr(name)` 伪元素直接取属性。
122
+ - **隐身 HTTP** — `Fetcher` 通过 `curl_cffi` 重放真实浏览器的 TLS/JA3 指纹与 HTTP/2 帧序,使请求在网络层与 Chrome 难以区分;支持 `impersonate="chrome131"` 等浏览器预设,并可通过 `ja3_fingerprint` 参数透传到 `curl_cffi` 的 `ja3` 参数做细粒度 TLS 扩展定制(`max_redirects` 默认 5,限制重定向跳数)。`curl_cffi` 缺失时自动降级到 `httpx`(带警告)。`AsyncFetcher` 提供纯异步 API;`download()` 提供流式下载(断点续传 + `max_bytes` 上限 + 取消回调,SSRF/DNS 钉扎/代理/会话池校验链全程生效)。
123
+ - **懒加载** — `import web_crawler` 不会强制加载 `playwright` / `curl_cffi`,重型子模块在首次访问时才解析(Scrapling 同款模式),仅需解析功能的用户无需安装浏览器依赖。
124
+ - **JS 渲染** — `DynamicFetcher` 驱动 Playwright/Chromium 渲染动态页面,支持按资源类型屏蔽、按选择器等待。
125
+ - **反爬处理** — `StealthyFetcher` 注入指纹补丁 JS、人类化鼠标/滚动轨迹,并对 Cloudflare 挑战做尽力而为的处理。
126
+ - **代理轮换** — `ProxyPool` 支持轮询/随机策略与按代理失败次数冷却。
127
+ - **会话池** — `SessionPool` 池化 `curl_cffi` 会话并让 TLS 指纹随会话轮换(round_robin / random),带 LIFO 复用、池上限与临时超额、空闲回收、连续失败淘汰与预热;`Fetcher(session_pool=...)` 一行接入,DNS 钉扎在池化会话上同样生效。
128
+ - **Spider 框架** — `Spider` / `Request` 提供回调分发、优先级调度、域名过滤、指纹去重(method+url+body,`DupeFilter` 可插拔)、失败重试(指数退避 + jitter 抖动,`retry_http_status` 响应级业务重试与 `fetcher.retries` 传输层重试两层独立)、可选 `robots.txt` 遵守(含 `Crawl-delay` 遵守与三源延迟取最保守者)、`per_domain_concurrency` 域级并发槽位与基于 JSON 的暂停/续跑;`CacheMiddleware` 提供 GET 响应磁盘缓存(TTL、离线回放、可调存储目录),`AutoThrottle` 基于 EMA 响应延迟做自适应限速(对齐 Scrapy 语义),运行统计可经 `stats_output` 导出 JSON / JSONL(多次运行聚合分析),`feed_output` 一行把抓取结果落盘(JSON 快照 / JSONL 追加 / CSV),`ValidationPipeline` 做 item 结构校验(pydantic 或零依赖 dict schema);`LinkExtractor`(allow/deny 正则、域白/黑名单、去重保序、URL 规范化)+ `CrawlSpider`(`rules` 规则引擎递归跟随遍历)+ `SitemapSpider`(sitemapindex 递归 + robots `Sitemap:` 行自动发现),对标 Scrapy 同名概念。
129
+ - **统一异常体系** — 以 `WebCrawlerError` 为根的异常族(`UnsafeTargetError` / `FetchError` / `ParseError` / `ConfigError` / `InvalidStateError` / `AIProviderError`):`except WebCrawlerError` 一次捕获库自身的错误,与环境错误精确区分;每个子类同时继承 `ValueError` / `RuntimeError`,既有捕获代码零破坏。
130
+ - **统一 `Response`** — 所有 fetcher 返回同一 `Response` 对象,内置 `.css()` / `.xpath()` / `.json()` / `urljoin()` 辅助方法。
131
+ - **AI 辅助抓取** — `AIExtractor` 把自然语言字段描述转换为校验过的 CSS 选择器(含自愈能力);`AIScrapeAgent` 编排抓取与抽取,遵守 `robots.txt`,对 429/503 退避(遵循 `Retry-After`),遇到卡死会返回"转人工处理"。
132
+ - **JS 逆向 Agent** — `ReverseAgent` 通过 **观察 → 思考 → 行动** 循环(`CamoufoxFetcher` + DeepSeek-V4.1-Flash)逆向目标站点的加密逻辑:注入 JS Hook(fetch / XHR / cookie / `crypto.subtle` / webpack / console)、捕获网络流量、拆分 webpack 包,再让 LLM 反混淆并在 Python 中重实现签名算法。内置 6 种真实浏览器交互动作(`click` / `type` / `scroll` / `press` / `hover` / `select_option`)、3 种多标签动作(`new_tab` / `switch_tab` / `close_tab`)与人类化输入轨迹;危险点击护栏与选择器注入拦截。同时通过 MCP 服务(`web-crawler-mcp`)、命令行(`web-crawler-reverse`)与 Web UI(SSE 实时推送 `/reverse/stream`)暴露。
133
+ - **图片验证码识别** — `ImageCaptchaSolver` 无需浏览器即可识别三类图片挑战:文本 OCR(4–8 位字母数字)、滑块缺口定位(Pillow + numpy 模板匹配)、点选坐标识别(视觉 LLM)。后端按 `ddddocr` → numpy 模板匹配 → LLM Vision 依次降级。`CaptchaManager` 在 `enable_image_captcha=True`(默认)时自动注入图片求解器,端到端处理 hCaptcha / reCAPTCHA v2 图片挑战与 GeeTest 拼图。
134
+ - **轻量渗透辅助** — `web_crawler.pentest` 子包提供纯 Python、无外部命令依赖的侦察工具:`PortScanner`(TCP connect + TOP-100 端口)、`DirBruter`(60+ 常见路径)、`SubdomainEnumerator`(80+ 子域前缀字典)、`VulnScanner`(SQL 注入 / XSS / 路径穿越规则检测,可选 LLM 分析)、`HeaderChecker`(8 项安全头检测 + A–F 评级),由 `PentestReport` 聚合。**仅限已获授权的安全测试**。
135
+ - **生产级 Agent 能力** — `ReverseAgent` 对齐 browser-use / Skyvern 等主流 Agent 框架,内置 DOM 焦点裁剪(`DomPruner`,约 80% token 削减)、断点续跑(`CheckpointManager`)、动作置信度评分(`ConfidenceScorer`)、动作护栏(`ActionGuard`)、Planner/Actor 双脑分离(`Planner`)、循环检测与上下文压缩(`LoopDetector` / `ContextCompressor`)、独立任务裁决(`TaskJudge`)、成功路径录制(`RunRecorder`)、事件总线与崩溃自愈(`watchdog`)、Pydantic 结构化校验(`SchemaValidator`)。全部能力可经 `ReverseAgentConfig` 独立开关。
136
+
137
+ ## 技术栈
138
+
139
+ - **语言**:Python 3.10+,全量类型标注(含 PEP 561 `py.typed`)
140
+ - **解析**:`lxml`、`cssselect`、`beautifulsoup4`
141
+ - **网络**:`httpx`(核心依赖)、`curl_cffi`(TLS 隐身,可选)、`playwright`(JS 渲染,可选)、`camoufox`(抗指纹 Firefox,可选)
142
+ - **AI / 逆向**:OpenAI 兼容 LLM 抽象层(默认 DeepSeek-V4.1-Flash,仅依赖 httpx)、`pydantic`(结构化校验,可选)、`pycryptodome`(AES 解密,可选)
143
+ - **验证码**:`ddddocr`、`numpy`、`Pillow`(可选)
144
+ - **存储**:SQLite(标准库 `sqlite3`,用于自适应指纹与任务记录)
145
+ - **Web UI**:标准库 `http.server`(`ThreadingHTTPServer`),SSE 实时推送
146
+ - **质量工具**:`pytest` / `pytest-asyncio` / `pytest-cov`、`ruff`、`mypy`
147
+ - **文档**:MkDocs + Material + mkdocstrings
148
+ - **CI**:GitHub Actions(`.github/workflows/ci.yml`)
149
+
150
+ ## 用途与边界
151
+
152
+ 本库是**面向开发者的隐身网页采集与安全研究工具集**,把两套能力放在同一个包里,方便做端到端的研究与自动化:
153
+
154
+ - **采集 / 逆向能力** —— 自适应解析、TLS 指纹隐身、JS 渲染、反爬对抗与 JS 逆向,面向你自己拥有或已获授权的目标做数据采集与前端逻辑研究。
155
+ - **安全研究能力** —— `web_crawler.pentest` 子包提供纯 Python 的侦察工具集(端口 / 路径 / 子域探测、常见漏洞规则检测、安全头评级),面向你自己拥有或已书面授权的资产做授权安全测试。
156
+
157
+ **默认姿态**:公开发布的版本默认开启安全防护——所有抓取入口拒绝私网 / 环回 / 云元数据目标,协议仅放行 `http` / `https`,并对主机名做 DNS 解析复查以防重绑定型 SSRF。这是一个**安全默认值**,不是能力上限。
158
+
159
+ **全解锁开关**:如果你在自己的可信环境(本机、内网、开发容器)需要访问内网服务或云元数据类目标,可显式开启个人 Power Mode(`WEB_CRAWLER_POWER_MODE=1` 或 `Fetcher(allow_private_hosts=True)`),此时仅放行 host 校验、`http/https` 协议白名单始终保留。详见下方 [Power Mode](#-power-mode个人全解锁默认关闭) 章节。
160
+
161
+ **红线**:本库仅供合法、已授权的用途。pentest 子包与任何抓取模块只可用于你拥有或已取得书面授权的系统;未经授权对他人系统发起扫描、爬取或探测均属违法,使用者自行承担全部责任。详见 [合规说明](#合规说明)。
162
+
163
+ ## 快速开始
164
+
165
+ ### 隐身 HTTP 抓取
166
+
167
+ ```python
168
+ from web_crawler import Fetcher
169
+
170
+ with Fetcher(impersonate="chrome131", timeout=30.0) as f:
171
+ resp = f.get("https://example.com")
172
+ print(resp.status, resp.css_first("h1").text)
173
+ ```
174
+
175
+ ### 自适应解析(站点改版也能定位元素)
176
+
177
+ ```python
178
+ from web_crawler import Selector, AdaptiveStorage
179
+
180
+ storage = AdaptiveStorage() # 默认存于 ~/.web_crawler/adaptive.sqlite3
181
+
182
+ # 第一次运行:保存元素指纹
183
+ page = Selector(html_v1, url="https://shop.example.com", adaptive=True, storage=storage)
184
+ title = page.css_first("#product-title", auto_save=True)
185
+
186
+ # 站点改版、id 消失后……
187
+ page2 = Selector(html_v2, url="https://shop.example.com", adaptive=True, storage=storage)
188
+ relocated = page2.css_first("#product-title", adaptive=True) # 按相似度自动重定位
189
+ ```
190
+
191
+ ### Spider 框架
192
+
193
+ ```python
194
+ from web_crawler import Spider, Request, Fetcher
195
+
196
+
197
+ class QuotesSpider(Spider):
198
+ start_urls = ["https://quotes.toscrape.com/"]
199
+ allowed_domains = ["quotes.toscrape.com"]
200
+
201
+ def parse(self, response):
202
+ for quote in response.css("div.quote"):
203
+ yield {"text": quote.css_first(".text").text, "author": quote.css_first(".author").text}
204
+ nxt = response.css_first("li.next > a")
205
+ if nxt:
206
+ yield Request(response.urljoin(nxt.attr("href")))
207
+
208
+
209
+ items = QuotesSpider(fetcher=Fetcher(impersonate="chrome131")).run()
210
+ # 中途暂停、之后续跑:
211
+ # spider.run(state_file="state.json")
212
+ # QuotesSpider(fetcher=Fetcher()).run(state_file="state.json", resume=True)
213
+ ```
214
+
215
+ Spider 还内置了爬虫框架级的通用机制,均可通过类属性开关:
216
+
217
+ ```python
218
+ class RobustSpider(Spider):
219
+ start_urls = ["https://example.com/"]
220
+ max_retries = 2 # 下载失败指数退避重试(默认 0 不重试)
221
+ per_domain_delay = {"example.com": 1.0} # 同域最小请求间隔(秒),可配多域
222
+ respect_robots = True # 回调产出的请求先过 robots.txt(默认关闭)
223
+ user_agent = "my-bot" # robots.txt 检查使用的 UA
224
+ ```
225
+
226
+ 去重默认按 `method + url + body` 的 SHA1 指纹判定(同 URL 不同分页参数不再互相误杀),需要磁盘持久化等自定义行为时可传入 `dupefilter=MyDupeFilter()`。
227
+
228
+ `stream()` 为持续流式调度:并发槽位空出即派发下一个请求,慢请求不会阻塞后续调度。下载流程可通过 `DownloaderMiddleware` 介入(`process_request` 返回 `Response` 可短路下载、抛 `IgnoreRequest` 可丢弃请求;`process_response` 可变换响应),回调产出的 item 经 `ItemPipeline` 链变换/过滤(抛 `DropItem` 或返回 `None` 丢弃):
229
+
230
+ ```python
231
+ from web_crawler import DownloaderMiddleware, ItemPipeline, IgnoreRequest, DropItem
232
+
233
+
234
+ class CacheMiddleware(DownloaderMiddleware):
235
+ def process_request(self, request, spider):
236
+ cached = my_cache.get(request.url)
237
+ return cached # 命中缓存时短路下载;None 则正常放行
238
+
239
+
240
+ class CleanPipeline(ItemPipeline):
241
+ def process_item(self, item, spider):
242
+ if not item.get("name"):
243
+ raise DropItem("empty name")
244
+ return {**item, "name": item["name"].strip()}
245
+
246
+
247
+ class MySpider(Spider):
248
+ middlewares = [CacheMiddleware] # 类或实例均可,按声明顺序执行
249
+ item_pipelines = [CleanPipeline]
250
+ ```
251
+
252
+ ### JS 渲染与反爬
253
+
254
+ ```python
255
+ from web_crawler import DynamicFetcher, StealthyFetcher
256
+
257
+ # 渲染 JS 重页面
258
+ with DynamicFetcher(headless=True, wait_selector="div.content") as f:
259
+ resp = f.fetch("https://spa.example.com")
260
+
261
+ # 隐身模式:人类化输入 + Cloudflare 感知
262
+ with StealthyFetcher(google_search=True, humanize=True) as f:
263
+ resp = f.fetch("https://protected.example.com")
264
+ ```
265
+
266
+ ### 代理轮换
267
+
268
+ ```python
269
+ from web_crawler import Fetcher, ProxyPool
270
+
271
+ pool = ProxyPool(
272
+ ["http://u:p@proxy1:8080", "http://proxy2:3128"],
273
+ strategy="round_robin",
274
+ max_failures=3,
275
+ cooldown=60,
276
+ )
277
+ with Fetcher(proxy=pool, impersonate="chrome131") as f:
278
+ resp = f.get("https://example.com")
279
+ ```
280
+
281
+ ### 会话池
282
+
283
+ ```python
284
+ from web_crawler import Fetcher, SessionPool
285
+
286
+ with SessionPool(max_size=8, strategy="round_robin") as pool:
287
+ with Fetcher(session_pool=pool, impersonate="chrome131") as f:
288
+ # 多个请求复用池化会话,TLS 指纹随会话轮换
289
+ for url in urls:
290
+ resp = f.get(url)
291
+ ```
292
+
293
+ ### 自适应限速
294
+
295
+ ```python
296
+ from web_crawler import Spider, Fetcher, AutoThrottle
297
+
298
+
299
+ class MySpider(Spider):
300
+ start_urls = ["https://example.com/"]
301
+
302
+ def parse(self, response):
303
+ yield {"title": response.css_first("h1").text}
304
+
305
+
306
+ # 站点变慢时自动放慢、变快时自动提速
307
+ throttle = AutoThrottle(target_concurrency=2.0, alpha=0.3)
308
+ items = MySpider(
309
+ fetcher=Fetcher(impersonate="chrome131"),
310
+ autothrottle=throttle,
311
+ ).run()
312
+ ```
313
+
314
+ ### 统一异常处理
315
+
316
+ ```python
317
+ from web_crawler import Fetcher, WebCrawlerError, UnsafeTargetError, FetchError
318
+
319
+ try:
320
+ with Fetcher() as f:
321
+ resp = f.get("https://example.com")
322
+ except UnsafeTargetError as e:
323
+ print(f"目标不安全: {e}") # 私网/环回/云元数据等
324
+ except FetchError as e:
325
+ print(f"抓取失败: {e}") # 重定向超限/JS 渲染失败等
326
+ except WebCrawlerError as e:
327
+ print(f"库错误: {e}") # 其他 web_crawler 自身错误
328
+ # 既有 except ValueError / except RuntimeError 仍原样工作(兼容式多继承)
329
+ ```
330
+
331
+ ### 图片验证码识别
332
+
333
+ ```python
334
+ from web_crawler import ImageCaptchaSolver
335
+
336
+ solver = ImageCaptchaSolver()
337
+
338
+ text = solver.solve_text(image_bytes) # 文本 OCR
339
+ gap_x = solver.solve_slider(bg_bytes, puzzle_bytes) # 滑块缺口 x 坐标
340
+ coords = solver.solve_click(image_bytes, prompt="按文字顺序点击") # 点选坐标
341
+ ```
342
+
343
+ ## 安装与运行
344
+
345
+ > **发行版名与导入名**:PyPI 上 `web-crawler` 与 `webcrawler` 均已被无关项目占用,本项目以 **`stealth-web-crawler`** 为发行版名(`pip` 安装),导入名保持 `web_crawler` 不变——与 `beautifulsoup4`/`bs4` 同款惯例。发行版发布后即可:
346
+
347
+ ```bash
348
+ pip install stealth-web-crawler # 核心(解析 + Spider 框架)
349
+ pip install "stealth-web-crawler[all]" # + curl_cffi TLS 隐身 + Playwright JS 渲染
350
+ ```
351
+
352
+ 发布前的开发安装(源码方式):
353
+
354
+ ```bash
355
+ python -m venv .venv
356
+ source .venv/bin/activate # Windows: .venv\Scripts\activate
357
+
358
+ # 核心 + 开发工具(解析、测试、lint、类型检查)
359
+ pip install -e ".[dev]"
360
+
361
+ # 可选能力(按需组合):
362
+ pip install -e ".[all]" # + curl_cffi TLS 隐身 + Playwright JS 渲染
363
+ pip install -e ".[camoufox]" # + Camoufox 抗指纹 Firefox
364
+ pip install -e ".[mcp]" # + MCP 服务 / CLI(隐含 camoufox)
365
+ pip install -e ".[captcha]" # + ddddocr 图片验证码识别(文本 OCR / 滑块定位)
366
+ pip install -e ".[crypto]" # + pycryptodome(AES-128 m3u8 分段解密)
367
+ pip install -e ".[visual]" # + Pillow(图片解码与预处理;供验证码滑块模板匹配使用)
368
+ playwright install chromium # 仅 DynamicFetcher / StealthyFetcher 需要
369
+ ```
370
+
371
+ 安装后自动注册以下命令(无需配置 PYTHONPATH):
372
+
373
+ | 命令 | 作用 |
374
+ | --- | --- |
375
+ | `web-crawler` | 资源下载器 CLI |
376
+ | `crawler-ui` | 本地 Web UI |
377
+ | `web-crawler-mcp` | MCP 服务(JSON-RPC over stdio) |
378
+ | `web-crawler-reverse` | JS 逆向 Agent 命令行 |
379
+
380
+ ### 直接运行(源码树,免 pip install)
381
+
382
+ ```bash
383
+ # Windows(cmd):把 src/ 加入模块路径后按模块运行
384
+ set PYTHONPATH=src
385
+ py -m web_crawler.app.crawler --url https://example.com --out ./out --workers 8
386
+ py -m web_crawler.app.crawler --url https://example.com --stealth --impersonate chrome131
387
+
388
+ # 本地 Web UI(默认 http://127.0.0.1:8765,--open 自动打开浏览器)
389
+ py -m web_crawler.app.ui --open
390
+
391
+ # 远程/容器绑定需显式放行:--allow-remote(控制面无鉴权,仅限可信网络)
392
+ py -m web_crawler.app.ui --host 0.0.0.0 --port 8765 --allow-remote
393
+
394
+ # Linux / macOS
395
+ PYTHONPATH=src python -m web_crawler.app.ui --open
396
+ ```
397
+
398
+ > 本仓库不附带任何一键启动脚本。个人如需解锁内网/云元数据目标,可在
399
+ > **自己的可信环境**里设置 `WEB_CRAWLER_POWER_MODE=1` 后运行
400
+ > (见下方 [Power Mode](#-power-mode个人全解锁默认关闭) 章节)。
401
+
402
+ ### Docker
403
+
404
+ ```bash
405
+ docker build -t web-crawler .
406
+ docker run -p 8765:8765 -e DEEPSEEK_API_KEY=<your-key> web-crawler
407
+ docker-compose up -d
408
+ ```
409
+
410
+ > 容器限制:镜像仅安装 Firefox(供 Camoufox / 逆向 Agent 使用),而
411
+ > `DynamicFetcher` 默认驱动 **Chromium**——容器内如需 JS 渲染,请自行执行
412
+ > `playwright install chromium`,或改用 `CamoufoxFetcher`。
413
+ > 镜像 CMD 已带 `--host 0.0.0.0 --port 8765 --allow-remote`;控制面无鉴权,
414
+ > 请勿将容器端口暴露到公网。
415
+
416
+ ### MCP 接入(Claude Desktop / Cursor 等)
417
+
418
+ ```bash
419
+ pip install -e ".[mcp]"
420
+ set DEEPSEEK_API_KEY=<your-key> # Windows: set DEEPSEEK_API_KEY=...
421
+ web-crawler-mcp # 通过 stdio 通信
422
+ ```
423
+
424
+ 在 AI 客户端的 MCP 配置中注册:
425
+
426
+ ```json
427
+ {
428
+ "mcpServers": {
429
+ "js-reverse": {
430
+ "command": "web-crawler-mcp",
431
+ "env": {"DEEPSEEK_API_KEY": "<your-key>"}
432
+ }
433
+ }
434
+ }
435
+ ```
436
+
437
+ ### JS 逆向 CLI
438
+
439
+ ```bash
440
+ web-crawler-reverse reverse https://example.com --target-params anti_content sign
441
+ web-crawler-reverse analyze script.js # 反混淆 JS 片段
442
+ web-crawler-reverse webpack bundle.js # 提取 webpack 模块
443
+ web-crawler-reverse reimplement algo.js --language python
444
+ web-crawler-reverse capture https://example.com --wait 8
445
+ web-crawler-reverse scripts https://example.com # 列出页面加载的 JS 脚本
446
+ web-crawler-reverse hooks https://example.com # 向页面注入 JS Hook
447
+ web-crawler-reverse deobfuscate script.js # AI 反混淆 JS 代码
448
+ web-crawler-reverse captcha https://example.com # 检测并处理页面验证码
449
+ web-crawler-reverse captcha-image --mode slider --bg bg.png --slider s.png
450
+ web-crawler-reverse pentest example.com --authorized # 轻量渗透侦察(仅限已授权目标)
451
+ web-crawler-reverse interactive # REPL,输入 tools 查看命令
452
+ web-crawler-reverse run --url https://example.com --task "提取签名参数" --headless
453
+ ```
454
+
455
+ ## 配置
456
+
457
+ 以下环境变量均可通过代码中 `api_key=` 关键字参数或项目根目录的 `.env` 文件 / 环境变量(如 `DEEPSEEK_API_KEY`)注入(`.env` 在进程启动时自动读取,不覆盖已存在的环境变量)。**所有值均为占位符,请替换为你自己的密钥。**
458
+
459
+ | 变量 | 用途 | 示例 |
460
+ | --- | --- | --- |
461
+ | `DEEPSEEK_API_KEY` | DeepSeek OpenAI 兼容接口密钥(AI 抽取 / 逆向 Agent 默认 provider) | `DEEPSEEK_API_KEY=<your-key>` |
462
+ | `LLM_API_KEY` | 自定义 OpenAI 兼容 provider 的密钥(可选) | `LLM_API_KEY=<your-key>` |
463
+ | `CRAWLER_DB_PATH` | 覆盖 Web UI 任务记录 SQLite 数据库路径 | `CRAWLER_DB_PATH=/path/to/crawler_data.db` |
464
+
465
+ ## 项目结构
466
+
467
+ ```
468
+ src/web_crawler/ # 核心库
469
+ _types.py # TextHandler / Attrs / ResultList
470
+ compat.py # 可选依赖探测(优雅降级)
471
+ response.py # 统一 Response(选择器辅助、meta、urljoin)
472
+ crawler.py # 同域异步爬虫(robots.txt 感知)
473
+ parser/ # Selector + 自适应引擎 + 指纹存储 + 截图瓦片 VLM
474
+ fetchers/ # Fetcher / AsyncFetcher / DynamicFetcher / StealthyFetcher / CamoufoxFetcher / ProxyPool
475
+ spider/ # Spider 框架(重试/robots/指纹去重/中间件管道/暂停续跑/流式并发)
476
+ ai/ # LLM 层 + AIExtractor + AIScrapeAgent + JS 逆向套件 + 生产级 Agent 能力
477
+ pentest/ # 轻量渗透辅助工具集(纯 Python,无外部命令依赖)
478
+ mcp/ # ReverseMCPServer(JSON-RPC over stdio)+ CLI
479
+ app/ # 应用层(随 web_crawler 包分发,不再污染顶层 app 命名空间)
480
+ crawler.py # 并发资源下载器(CLI / 主流程编排,续传、去重、sitemap、UI 驱动)
481
+ crawler_models.py # 共享数据类 Resource / ManifestRow
482
+ crawler_net.py # 网络/解析/工具层(限速、去重、URL 分类、HTML 解析)
483
+ crawler_report.py # 报告/格式层(清单、摘要、MD/HTML 报告、HTML 重写、智能抽取)
484
+ db.py # SQLite 持久化(任务 + 结果,线程安全)
485
+ ui.py # 本地 Web UI(SSE 实时推送)
486
+ static/index.html # UI 前端模板(运行时读取)
487
+ py.typed # PEP 561 类型标记
488
+ tests/ # pytest 测试套件
489
+ benchmarks.py # 解析器/fetcher 微基准 + 回归检测
490
+ docs/ + mkdocs.yml # MkDocs 文档站点
491
+ ```
492
+
493
+ ## 测试与质量
494
+
495
+ ```bash
496
+ ruff check . # 静态检查
497
+ mypy src/web_crawler # 类型检查
498
+ python -m pytest -m "not slow" # 运行测试(跳过慢速集成测试)
499
+ python -m pytest --cov=web_crawler # 带覆盖率
500
+ python benchmarks.py --check-regression # 性能回归检查(CI 模式)
501
+ ```
502
+
503
+ > 测试需要 dev 依赖(`pytest` / `pytest-asyncio` / `pytest-cov`)。仓库内 `.venv` 是
504
+ > 纯运行时环境、不含 pytest,请用已安装 dev 依赖的系统 Python 运行(`python -m pytest`
505
+ > 或直接 `pytest`,editable 安装后无需 `PYTHONPATH=src`)。
506
+
507
+ GitHub Actions 在每次 push 时运行 lint、类型检查、带覆盖率的测试、基准回归检查与文档构建(`--strict`);标记为 `@pytest.mark.slow` 的慢速测试(如 Camoufox 端到端套件)默认被排除。
508
+
509
+ 覆盖率徽章显示的是两个 CI 门禁值(`--cov-fail-under`:标准 test job 装 dev 依赖、门禁 92;`test-extras` job 装全量可选依赖、门禁 97),**非实时测量数据**——历史实测数字见 [CHANGELOG](CHANGELOG.md)。
510
+
511
+ ## 合规说明
512
+
513
+ - 逆向 Agent 仅模拟正常用户交互,不伪造登录凭证、不绕过付费墙。
514
+ - 图片验证码(OCR / 滑块 / 点选)通过 `ImageCaptchaSolver` 自动识别;当页面返回 401/403 或出现无法处理的挑战时,Agent 会停止并返回"转人工处理"。
515
+ - 用途边界、默认安全姿态与全解锁开关的详细说明见上方 [用途与边界](#用途与边界);`web_crawler.pentest` 仅用于**已获书面授权**的安全测试;未经授权对他人系统使用任何模块均属违法,使用者自行承担全部责任。
516
+
517
+ ## 🔓 Power Mode(个人全解锁,默认关闭)
518
+
519
+ **公开默认是安全版本**:所有抓取入口(Fetcher / AsyncFetcher / DynamicFetcher / StealthyFetcher / CamoufoxFetcher、app 下载器与重定向)都会拒绝私网 / 环回 / 链路本地目标(含云元数据 `169.254.169.254`、CGNAT、IPv6 ULA / 链路本地、`localhost` / `*.local` 等)。
520
+
521
+ **协议白名单**:仅允许 `http` / `https`——`file://`、`ftp://`、`gopher://`、`data:`、`javascript:` 等协议在入口与每一跳重定向处一律拒绝。
522
+
523
+ **DNS 解析复查(默认开启)**:对主机名先做一次 `getaddrinfo` 解析、逐地址核对拒绝段,防 DNS 重绑定型 SSRF;解析失败按保守策略拒绝。结果带 **60 秒 TTL 缓存**(解析失败负缓存 10 秒,有界 LRU),同一主机不会重复解析,性能开销可忽略。库层默认开启(`Fetcher(resolve_hosts=False)` 可关),app 层入口始终开启。
524
+
525
+ 如果你在**自己可信的环境**中需要访问内网服务或云元数据类目标,可开启个人 Power Mode——只放行 host 校验,`http/https` scheme 白名单始终保留:
526
+
527
+ ```bash
528
+ # Windows
529
+ set WEB_CRAWLER_POWER_MODE=1
530
+
531
+ # Linux / macOS
532
+ export WEB_CRAWLER_POWER_MODE=1
533
+ ```
534
+
535
+ 代码内等价方式:构造 fetcher 时传 `allow_private_hosts=True`(`resolve_hosts=True` 可单独开启 DNS 解析复查)。
536
+
537
+ > ⚠️ **风险提示**:Power Mode 会绕过 SSRF 防护,**只应在你自己的内网 / 开发环境使用**;公开部署、共享服务器、处理不可信网页时请保持默认关闭。测试套件使用的 `WEB_CRAWLER_ALLOW_PRIVATE_HOSTS=1` 为同义旧开关,二者等效。
538
+
539
+ ## 贡献与安全
540
+
541
+ - 参与贡献请阅读 [CONTRIBUTING.md](CONTRIBUTING.md)(开发环境、质量要求、提交规范)。
542
+ - 发现安全漏洞请按 [SECURITY.md](SECURITY.md) 私密披露,不要开公开 Issue。
543
+ - 社区行为准则见 [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md)。
544
+
545
+ ## 许可证
546
+
547
+ MIT,见 [LICENSE](LICENSE)。