crawlo 1.4.7__py3-none-any.whl → 1.4.8__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 crawlo might be problematic. Click here for more details.

Files changed (348) hide show
  1. crawlo/__init__.py +90 -90
  2. crawlo/__version__.py +1 -1
  3. crawlo/cli.py +75 -75
  4. crawlo/commands/__init__.py +14 -14
  5. crawlo/commands/check.py +594 -594
  6. crawlo/commands/genspider.py +186 -186
  7. crawlo/commands/help.py +140 -140
  8. crawlo/commands/list.py +155 -155
  9. crawlo/commands/run.py +379 -379
  10. crawlo/commands/startproject.py +460 -460
  11. crawlo/commands/stats.py +187 -187
  12. crawlo/commands/utils.py +196 -196
  13. crawlo/config.py +320 -320
  14. crawlo/config_validator.py +277 -277
  15. crawlo/core/__init__.py +52 -52
  16. crawlo/core/engine.py +451 -451
  17. crawlo/core/processor.py +47 -47
  18. crawlo/core/scheduler.py +290 -290
  19. crawlo/crawler.py +698 -698
  20. crawlo/data/__init__.py +5 -5
  21. crawlo/data/user_agents.py +194 -194
  22. crawlo/downloader/__init__.py +280 -280
  23. crawlo/downloader/aiohttp_downloader.py +233 -233
  24. crawlo/downloader/cffi_downloader.py +250 -250
  25. crawlo/downloader/httpx_downloader.py +265 -265
  26. crawlo/downloader/hybrid_downloader.py +212 -212
  27. crawlo/downloader/playwright_downloader.py +425 -425
  28. crawlo/downloader/selenium_downloader.py +486 -486
  29. crawlo/event.py +45 -45
  30. crawlo/exceptions.py +214 -214
  31. crawlo/extension/__init__.py +64 -64
  32. crawlo/extension/health_check.py +141 -141
  33. crawlo/extension/log_interval.py +94 -94
  34. crawlo/extension/log_stats.py +70 -70
  35. crawlo/extension/logging_extension.py +53 -53
  36. crawlo/extension/memory_monitor.py +104 -104
  37. crawlo/extension/performance_profiler.py +133 -133
  38. crawlo/extension/request_recorder.py +107 -107
  39. crawlo/factories/__init__.py +27 -27
  40. crawlo/factories/base.py +68 -68
  41. crawlo/factories/crawler.py +104 -104
  42. crawlo/factories/registry.py +84 -84
  43. crawlo/factories/utils.py +134 -134
  44. crawlo/filters/__init__.py +170 -170
  45. crawlo/filters/aioredis_filter.py +347 -347
  46. crawlo/filters/memory_filter.py +261 -261
  47. crawlo/framework.py +306 -306
  48. crawlo/initialization/__init__.py +44 -44
  49. crawlo/initialization/built_in.py +391 -391
  50. crawlo/initialization/context.py +141 -141
  51. crawlo/initialization/core.py +240 -240
  52. crawlo/initialization/phases.py +229 -229
  53. crawlo/initialization/registry.py +143 -143
  54. crawlo/initialization/utils.py +48 -48
  55. crawlo/interfaces.py +23 -23
  56. crawlo/items/__init__.py +23 -23
  57. crawlo/items/base.py +23 -23
  58. crawlo/items/fields.py +52 -52
  59. crawlo/items/items.py +104 -104
  60. crawlo/logging/__init__.py +42 -42
  61. crawlo/logging/config.py +280 -276
  62. crawlo/logging/factory.py +175 -175
  63. crawlo/logging/manager.py +104 -104
  64. crawlo/middleware/__init__.py +87 -87
  65. crawlo/middleware/default_header.py +132 -132
  66. crawlo/middleware/download_delay.py +104 -104
  67. crawlo/middleware/middleware_manager.py +142 -142
  68. crawlo/middleware/offsite.py +123 -123
  69. crawlo/middleware/proxy.py +209 -209
  70. crawlo/middleware/request_ignore.py +86 -86
  71. crawlo/middleware/response_code.py +150 -150
  72. crawlo/middleware/response_filter.py +136 -136
  73. crawlo/middleware/retry.py +124 -124
  74. crawlo/mode_manager.py +287 -287
  75. crawlo/network/__init__.py +21 -21
  76. crawlo/network/request.py +408 -376
  77. crawlo/network/response.py +598 -569
  78. crawlo/pipelines/__init__.py +52 -52
  79. crawlo/pipelines/base_pipeline.py +452 -452
  80. crawlo/pipelines/bloom_dedup_pipeline.py +145 -146
  81. crawlo/pipelines/console_pipeline.py +39 -39
  82. crawlo/pipelines/csv_pipeline.py +316 -316
  83. crawlo/pipelines/database_dedup_pipeline.py +196 -197
  84. crawlo/pipelines/json_pipeline.py +218 -218
  85. crawlo/pipelines/memory_dedup_pipeline.py +104 -105
  86. crawlo/pipelines/mongo_pipeline.py +140 -139
  87. crawlo/pipelines/mysql_pipeline.py +468 -469
  88. crawlo/pipelines/pipeline_manager.py +100 -100
  89. crawlo/pipelines/redis_dedup_pipeline.py +155 -155
  90. crawlo/project.py +347 -347
  91. crawlo/queue/__init__.py +9 -9
  92. crawlo/queue/pqueue.py +38 -38
  93. crawlo/queue/queue_manager.py +591 -591
  94. crawlo/queue/redis_priority_queue.py +518 -518
  95. crawlo/settings/__init__.py +7 -7
  96. crawlo/settings/default_settings.py +287 -284
  97. crawlo/settings/setting_manager.py +219 -219
  98. crawlo/spider/__init__.py +658 -657
  99. crawlo/stats_collector.py +81 -81
  100. crawlo/subscriber.py +129 -129
  101. crawlo/task_manager.py +138 -138
  102. crawlo/templates/crawlo.cfg.tmpl +10 -10
  103. crawlo/templates/project/__init__.py.tmpl +1 -1
  104. crawlo/templates/project/items.py.tmpl +13 -13
  105. crawlo/templates/project/middlewares.py.tmpl +38 -38
  106. crawlo/templates/project/pipelines.py.tmpl +35 -35
  107. crawlo/templates/project/settings.py.tmpl +113 -109
  108. crawlo/templates/project/settings_distributed.py.tmpl +160 -156
  109. crawlo/templates/project/settings_gentle.py.tmpl +174 -170
  110. crawlo/templates/project/settings_high_performance.py.tmpl +175 -171
  111. crawlo/templates/project/settings_minimal.py.tmpl +102 -98
  112. crawlo/templates/project/settings_simple.py.tmpl +172 -168
  113. crawlo/templates/project/spiders/__init__.py.tmpl +9 -9
  114. crawlo/templates/run.py.tmpl +23 -23
  115. crawlo/templates/spider/spider.py.tmpl +32 -32
  116. crawlo/templates/spiders_init.py.tmpl +4 -4
  117. crawlo/tools/__init__.py +86 -86
  118. crawlo/tools/date_tools.py +289 -289
  119. crawlo/tools/distributed_coordinator.py +384 -384
  120. crawlo/tools/scenario_adapter.py +262 -262
  121. crawlo/tools/text_cleaner.py +232 -232
  122. crawlo/utils/__init__.py +74 -50
  123. crawlo/utils/batch_processor.py +276 -276
  124. crawlo/utils/config_manager.py +442 -442
  125. crawlo/utils/controlled_spider_mixin.py +439 -439
  126. crawlo/utils/db_helper.py +250 -250
  127. crawlo/utils/encoding_helper.py +190 -0
  128. crawlo/utils/error_handler.py +410 -410
  129. crawlo/utils/fingerprint.py +121 -121
  130. crawlo/utils/func_tools.py +82 -82
  131. crawlo/utils/large_scale_helper.py +344 -344
  132. crawlo/utils/leak_detector.py +335 -335
  133. crawlo/utils/misc.py +81 -81
  134. crawlo/utils/mongo_connection_pool.py +157 -157
  135. crawlo/utils/mysql_connection_pool.py +197 -197
  136. crawlo/utils/performance_monitor.py +285 -285
  137. crawlo/utils/queue_helper.py +175 -175
  138. crawlo/utils/redis_checker.py +90 -90
  139. crawlo/utils/redis_connection_pool.py +578 -578
  140. crawlo/utils/redis_key_validator.py +198 -198
  141. crawlo/utils/request.py +278 -278
  142. crawlo/utils/request_serializer.py +225 -225
  143. crawlo/utils/resource_manager.py +337 -337
  144. crawlo/utils/response_helper.py +113 -0
  145. crawlo/utils/selector_helper.py +138 -137
  146. crawlo/utils/singleton.py +69 -69
  147. crawlo/utils/spider_loader.py +201 -201
  148. crawlo/utils/text_helper.py +94 -94
  149. {crawlo-1.4.7.dist-info → crawlo-1.4.8.dist-info}/METADATA +831 -689
  150. crawlo-1.4.8.dist-info/RECORD +347 -0
  151. examples/__init__.py +7 -7
  152. tests/__init__.py +7 -7
  153. tests/advanced_tools_example.py +217 -217
  154. tests/authenticated_proxy_example.py +110 -110
  155. tests/baidu_performance_test.py +108 -108
  156. tests/baidu_test.py +59 -59
  157. tests/bug_check_test.py +250 -250
  158. tests/cleaners_example.py +160 -160
  159. tests/comprehensive_framework_test.py +212 -212
  160. tests/comprehensive_test.py +81 -81
  161. tests/comprehensive_testing_summary.md +186 -186
  162. tests/config_validation_demo.py +142 -142
  163. tests/controlled_spider_example.py +205 -205
  164. tests/date_tools_example.py +180 -180
  165. tests/debug_configure.py +69 -69
  166. tests/debug_framework_logger.py +84 -84
  167. tests/debug_log_config.py +126 -126
  168. tests/debug_log_levels.py +63 -63
  169. tests/debug_pipelines.py +66 -66
  170. tests/detailed_log_test.py +233 -233
  171. tests/direct_selector_helper_test.py +96 -96
  172. tests/distributed_dedup_test.py +467 -467
  173. tests/distributed_test.py +66 -66
  174. tests/distributed_test_debug.py +76 -76
  175. tests/dynamic_loading_example.py +523 -523
  176. tests/dynamic_loading_test.py +104 -104
  177. tests/error_handling_example.py +171 -171
  178. tests/explain_mysql_update_behavior.py +76 -76
  179. tests/final_comprehensive_test.py +151 -151
  180. tests/final_log_test.py +260 -260
  181. tests/final_validation_test.py +182 -182
  182. tests/fix_log_test.py +142 -142
  183. tests/framework_performance_test.py +202 -202
  184. tests/log_buffering_test.py +111 -111
  185. tests/log_generation_timing_test.py +153 -153
  186. tests/monitor_redis_dedup.sh +72 -72
  187. tests/ofweek_scrapy/ofweek_scrapy/items.py +12 -12
  188. tests/ofweek_scrapy/ofweek_scrapy/middlewares.py +100 -100
  189. tests/ofweek_scrapy/ofweek_scrapy/pipelines.py +13 -13
  190. tests/ofweek_scrapy/ofweek_scrapy/settings.py +84 -84
  191. tests/ofweek_scrapy/ofweek_scrapy/spiders/__init__.py +4 -4
  192. tests/ofweek_scrapy/scrapy.cfg +11 -11
  193. tests/optimized_performance_test.py +211 -211
  194. tests/performance_comparison.py +244 -244
  195. tests/queue_blocking_test.py +113 -113
  196. tests/queue_test.py +89 -89
  197. tests/redis_key_validation_demo.py +130 -130
  198. tests/request_params_example.py +150 -150
  199. tests/response_improvements_example.py +144 -144
  200. tests/scrapy_comparison/ofweek_scrapy.py +138 -138
  201. tests/scrapy_comparison/scrapy_test.py +133 -133
  202. tests/simple_cli_test.py +54 -54
  203. tests/simple_command_test.py +119 -119
  204. tests/simple_crawlo_test.py +126 -126
  205. tests/simple_follow_test.py +38 -38
  206. tests/simple_log_test2.py +137 -137
  207. tests/simple_optimization_test.py +128 -128
  208. tests/simple_queue_type_test.py +41 -41
  209. tests/simple_response_selector_test.py +94 -94
  210. tests/simple_selector_helper_test.py +154 -154
  211. tests/simple_selector_test.py +207 -207
  212. tests/simple_spider_test.py +49 -49
  213. tests/simple_url_test.py +73 -73
  214. tests/simulate_mysql_update_test.py +139 -139
  215. tests/spider_log_timing_test.py +177 -177
  216. tests/test_advanced_tools.py +148 -148
  217. tests/test_all_commands.py +230 -230
  218. tests/test_all_pipeline_fingerprints.py +133 -133
  219. tests/test_all_redis_key_configs.py +145 -145
  220. tests/test_asyncmy_usage.py +56 -56
  221. tests/test_batch_processor.py +178 -178
  222. tests/test_cleaners.py +54 -54
  223. tests/test_cli_arguments.py +118 -118
  224. tests/test_component_factory.py +174 -174
  225. tests/test_config_consistency.py +80 -80
  226. tests/test_config_merge.py +152 -152
  227. tests/test_config_validator.py +182 -182
  228. tests/test_controlled_spider_mixin.py +79 -79
  229. tests/test_crawler_process_import.py +38 -38
  230. tests/test_crawler_process_spider_modules.py +47 -47
  231. tests/test_crawlo_proxy_integration.py +114 -114
  232. tests/test_date_tools.py +123 -123
  233. tests/test_dedup_fix.py +220 -220
  234. tests/test_dedup_pipeline_consistency.py +124 -124
  235. tests/test_default_header_middleware.py +313 -313
  236. tests/test_distributed.py +65 -65
  237. tests/test_double_crawlo_fix.py +204 -204
  238. tests/test_double_crawlo_fix_simple.py +124 -124
  239. tests/test_download_delay_middleware.py +221 -221
  240. tests/test_downloader_proxy_compatibility.py +272 -272
  241. tests/test_edge_cases.py +305 -305
  242. tests/test_encoding_core.py +56 -56
  243. tests/test_encoding_detection.py +126 -126
  244. tests/test_enhanced_error_handler.py +270 -270
  245. tests/test_enhanced_error_handler_comprehensive.py +245 -245
  246. tests/test_error_handler_compatibility.py +112 -112
  247. tests/test_factories.py +252 -252
  248. tests/test_factory_compatibility.py +196 -196
  249. tests/test_final_validation.py +153 -153
  250. tests/test_fingerprint_consistency.py +135 -135
  251. tests/test_fingerprint_simple.py +51 -51
  252. tests/test_get_component_logger.py +83 -83
  253. tests/test_hash_performance.py +99 -99
  254. tests/test_integration.py +169 -169
  255. tests/test_item_dedup_redis_key.py +122 -122
  256. tests/test_large_scale_helper.py +235 -235
  257. tests/test_logging_enhancements.py +374 -374
  258. tests/test_logging_final.py +184 -184
  259. tests/test_logging_integration.py +312 -312
  260. tests/test_logging_system.py +282 -282
  261. tests/test_middleware_debug.py +141 -141
  262. tests/test_mode_consistency.py +51 -51
  263. tests/test_multi_directory.py +67 -67
  264. tests/test_multiple_spider_modules.py +80 -80
  265. tests/test_mysql_pipeline_config.py +164 -164
  266. tests/test_mysql_pipeline_error.py +98 -98
  267. tests/test_mysql_pipeline_init_log.py +82 -82
  268. tests/test_mysql_pipeline_integration.py +132 -132
  269. tests/test_mysql_pipeline_refactor.py +143 -143
  270. tests/test_mysql_pipeline_refactor_simple.py +85 -85
  271. tests/test_mysql_pipeline_robustness.py +195 -195
  272. tests/test_mysql_pipeline_types.py +88 -88
  273. tests/test_mysql_update_columns.py +93 -93
  274. tests/test_offsite_middleware.py +244 -244
  275. tests/test_offsite_middleware_simple.py +203 -203
  276. tests/test_optimized_selector_naming.py +100 -100
  277. tests/test_parsel.py +29 -29
  278. tests/test_performance.py +327 -327
  279. tests/test_performance_monitor.py +115 -115
  280. tests/test_pipeline_fingerprint_consistency.py +86 -86
  281. tests/test_priority_behavior.py +211 -211
  282. tests/test_priority_consistency.py +151 -151
  283. tests/test_priority_consistency_fixed.py +249 -249
  284. tests/test_proxy_health_check.py +32 -32
  285. tests/test_proxy_middleware.py +217 -217
  286. tests/test_proxy_middleware_enhanced.py +212 -212
  287. tests/test_proxy_middleware_integration.py +142 -142
  288. tests/test_proxy_middleware_refactored.py +207 -207
  289. tests/test_proxy_only.py +83 -83
  290. tests/test_proxy_providers.py +56 -56
  291. tests/test_proxy_stats.py +19 -19
  292. tests/test_proxy_strategies.py +59 -59
  293. tests/test_proxy_with_downloader.py +152 -152
  294. tests/test_queue_empty_check.py +41 -41
  295. tests/test_queue_manager_double_crawlo.py +173 -173
  296. tests/test_queue_manager_redis_key.py +179 -179
  297. tests/test_queue_naming.py +154 -154
  298. tests/test_queue_type.py +106 -106
  299. tests/test_queue_type_redis_config_consistency.py +130 -130
  300. tests/test_random_headers_default.py +322 -322
  301. tests/test_random_headers_necessity.py +308 -308
  302. tests/test_random_user_agent.py +72 -72
  303. tests/test_redis_config.py +28 -28
  304. tests/test_redis_connection_pool.py +294 -294
  305. tests/test_redis_key_naming.py +181 -181
  306. tests/test_redis_key_validator.py +123 -123
  307. tests/test_redis_queue.py +224 -224
  308. tests/test_redis_queue_name_fix.py +175 -175
  309. tests/test_redis_queue_type_fallback.py +129 -129
  310. tests/test_request_ignore_middleware.py +182 -182
  311. tests/test_request_params.py +111 -111
  312. tests/test_request_serialization.py +70 -70
  313. tests/test_response_code_middleware.py +349 -349
  314. tests/test_response_filter_middleware.py +427 -427
  315. tests/test_response_follow.py +104 -104
  316. tests/test_response_improvements.py +152 -152
  317. tests/test_response_selector_methods.py +92 -92
  318. tests/test_response_url_methods.py +70 -70
  319. tests/test_response_urljoin.py +86 -86
  320. tests/test_retry_middleware.py +333 -333
  321. tests/test_retry_middleware_realistic.py +273 -273
  322. tests/test_scheduler.py +252 -252
  323. tests/test_scheduler_config_update.py +133 -133
  324. tests/test_scrapy_style_encoding.py +112 -112
  325. tests/test_selector_helper.py +100 -100
  326. tests/test_selector_optimizations.py +146 -146
  327. tests/test_simple_response.py +61 -61
  328. tests/test_spider_loader.py +49 -49
  329. tests/test_spider_loader_comprehensive.py +69 -69
  330. tests/test_spider_modules.py +84 -84
  331. tests/test_spiders/test_spider.py +9 -9
  332. tests/test_telecom_spider_redis_key.py +205 -205
  333. tests/test_template_content.py +87 -87
  334. tests/test_template_redis_key.py +134 -134
  335. tests/test_tools.py +159 -159
  336. tests/test_user_agent_randomness.py +176 -176
  337. tests/test_user_agents.py +96 -96
  338. tests/untested_features_report.md +138 -138
  339. tests/verify_debug.py +51 -51
  340. tests/verify_distributed.py +117 -117
  341. tests/verify_log_fix.py +111 -111
  342. tests/verify_mysql_warnings.py +109 -109
  343. crawlo/utils/log.py +0 -80
  344. crawlo/utils/url_utils.py +0 -40
  345. crawlo-1.4.7.dist-info/RECORD +0 -347
  346. {crawlo-1.4.7.dist-info → crawlo-1.4.8.dist-info}/WHEEL +0 -0
  347. {crawlo-1.4.7.dist-info → crawlo-1.4.8.dist-info}/entry_points.txt +0 -0
  348. {crawlo-1.4.7.dist-info → crawlo-1.4.8.dist-info}/top_level.txt +0 -0
@@ -1,54 +1,54 @@
1
- from typing import Any
2
- from crawlo.exceptions import NotConfigured
3
- from crawlo.logging import get_logger
4
-
5
- # 获取logger实例
6
- _logger = get_logger(__name__)
7
-
8
-
9
- class CustomLoggerExtension:
10
- """
11
- 日志系统初始化扩展
12
- 遵循与 ExtensionManager 一致的接口规范:使用 create_instance
13
- """
14
-
15
- def __init__(self, settings: Any):
16
- self.settings = settings
17
- # 使用新的日志系统,但要简化配置传递
18
- try:
19
- from crawlo.logging import configure_logging
20
- # 直接传递settings对象,让日志系统内部处理
21
- configure_logging(settings)
22
- except Exception as e:
23
- # 如果日志系统配置失败,不应该阻止扩展加载
24
- # 使用基本日志输出错误信息
25
- import logging
26
- logging.getLogger(__name__).warning(f"Failed to configure logging system: {e}")
27
- # 不抛出异常,让扩展继续加载
28
-
29
- @classmethod
30
- def create_instance(cls, crawler: Any, *args: Any, **kwargs: Any) -> 'CustomLoggerExtension':
31
- """
32
- 工厂方法:兼容 ExtensionManager 的创建方式
33
- 被 ExtensionManager 调用
34
- """
35
- # 可以通过 settings 控制是否启用
36
- log_file = crawler.settings.get('LOG_FILE')
37
- log_enable_custom = crawler.settings.get('LOG_ENABLE_CUSTOM', False)
38
-
39
- # 只有当没有配置日志文件且未启用自定义日志时才禁用
40
- if not log_file and not log_enable_custom:
41
- raise NotConfigured("CustomLoggerExtension: LOG_FILE not set and LOG_ENABLE_CUSTOM=False")
42
-
43
- return cls(crawler.settings)
44
-
45
- def spider_opened(self, spider: Any) -> None:
46
- try:
47
- _logger.info(
48
- f"CustomLoggerExtension: Logging initialized. "
49
- f"LOG_FILE={self.settings.get('LOG_FILE')}, "
50
- f"LOG_LEVEL={self.settings.get('LOG_LEVEL')}"
51
- )
52
- except Exception as e:
53
- # 即使日志初始化信息无法打印,也不应该影响程序运行
1
+ from typing import Any
2
+ from crawlo.exceptions import NotConfigured
3
+ from crawlo.logging import get_logger
4
+
5
+ # 获取logger实例
6
+ _logger = get_logger(__name__)
7
+
8
+
9
+ class CustomLoggerExtension:
10
+ """
11
+ 日志系统初始化扩展
12
+ 遵循与 ExtensionManager 一致的接口规范:使用 create_instance
13
+ """
14
+
15
+ def __init__(self, settings: Any):
16
+ self.settings = settings
17
+ # 使用新的日志系统,但要简化配置传递
18
+ try:
19
+ from crawlo.logging import configure_logging
20
+ # 直接传递settings对象,让日志系统内部处理
21
+ configure_logging(settings)
22
+ except Exception as e:
23
+ # 如果日志系统配置失败,不应该阻止扩展加载
24
+ # 使用基本日志输出错误信息
25
+ import logging
26
+ logging.getLogger(__name__).warning(f"Failed to configure logging system: {e}")
27
+ # 不抛出异常,让扩展继续加载
28
+
29
+ @classmethod
30
+ def create_instance(cls, crawler: Any, *args: Any, **kwargs: Any) -> 'CustomLoggerExtension':
31
+ """
32
+ 工厂方法:兼容 ExtensionManager 的创建方式
33
+ 被 ExtensionManager 调用
34
+ """
35
+ # 可以通过 settings 控制是否启用
36
+ log_file = crawler.settings.get('LOG_FILE')
37
+ log_enable_custom = crawler.settings.get('LOG_ENABLE_CUSTOM', False)
38
+
39
+ # 只有当没有配置日志文件且未启用自定义日志时才禁用
40
+ if not log_file and not log_enable_custom:
41
+ raise NotConfigured("CustomLoggerExtension: LOG_FILE not set and LOG_ENABLE_CUSTOM=False")
42
+
43
+ return cls(crawler.settings)
44
+
45
+ def spider_opened(self, spider: Any) -> None:
46
+ try:
47
+ _logger.info(
48
+ f"CustomLoggerExtension: Logging initialized. "
49
+ f"LOG_FILE={self.settings.get('LOG_FILE')}, "
50
+ f"LOG_LEVEL={self.settings.get('LOG_LEVEL')}"
51
+ )
52
+ except Exception as e:
53
+ # 即使日志初始化信息无法打印,也不应该影响程序运行
54
54
  pass
@@ -1,105 +1,105 @@
1
- #!/usr/bin/python
2
- # -*- coding:UTF-8 -*-
3
- import asyncio
4
- import psutil
5
- from typing import Any, Optional
6
-
7
- from crawlo.logging import get_logger
8
- from crawlo.utils.error_handler import ErrorHandler
9
- from crawlo.event import CrawlerEvent
10
-
11
-
12
- class MemoryMonitorExtension:
13
- """
14
- 内存监控扩展
15
- 定期监控爬虫进程的内存使用情况,并在超出阈值时发出警告
16
- """
17
-
18
- def __init__(self, crawler: Any):
19
- self.task: Optional[asyncio.Task] = None
20
- self.process = psutil.Process()
21
- self.settings = crawler.settings
22
- self.logger = get_logger(self.__class__.__name__)
23
- self.error_handler = ErrorHandler(self.__class__.__name__, crawler.settings.get('LOG_LEVEL'))
24
-
25
- # 获取配置参数
26
- self.interval = self.settings.get_int('MEMORY_MONITOR_INTERVAL', 60) # 默认60秒检查一次
27
- self.warning_threshold = self.settings.get_float('MEMORY_WARNING_THRESHOLD', 80.0) # 默认80%警告阈值
28
- self.critical_threshold = self.settings.get_float('MEMORY_CRITICAL_THRESHOLD', 90.0) # 默认90%严重阈值
29
-
30
- @classmethod
31
- def create_instance(cls, crawler: Any) -> 'MemoryMonitorExtension':
32
- # 只有当配置启用时才创建实例
33
- if not crawler.settings.get_bool('MEMORY_MONITOR_ENABLED', False):
34
- from crawlo.exceptions import NotConfigured
35
- raise NotConfigured("MemoryMonitorExtension: MEMORY_MONITOR_ENABLED is False")
36
-
37
- o = cls(crawler)
38
- crawler.subscriber.subscribe(o.spider_opened, event=CrawlerEvent.SPIDER_OPENED)
39
- crawler.subscriber.subscribe(o.spider_closed, event=CrawlerEvent.SPIDER_CLOSED)
40
- return o
41
-
42
- async def spider_opened(self) -> None:
43
- """爬虫启动时开始监控"""
44
- try:
45
- self.task = asyncio.create_task(self._monitor_loop())
46
- self.logger.info(
47
- f"Memory monitor started. Interval: {self.interval}s, "
48
- f"Warning threshold: {self.warning_threshold}%, Critical threshold: {self.critical_threshold}%"
49
- )
50
- except Exception as e:
51
- self.error_handler.handle_error(
52
- e,
53
- context="启动内存监控失败",
54
- raise_error=False
55
- )
56
-
57
- async def spider_closed(self) -> None:
58
- """爬虫关闭时停止监控"""
59
- try:
60
- if self.task:
61
- self.task.cancel()
62
- try:
63
- await self.task
64
- except asyncio.CancelledError:
65
- pass
66
- self.task = None
67
- self.logger.info("Memory monitor stopped.")
68
- except Exception as e:
69
- self.error_handler.handle_error(
70
- e,
71
- context="停止内存监控失败",
72
- raise_error=False
73
- )
74
-
75
- async def _monitor_loop(self) -> None:
76
- """内存监控循环"""
77
- while True:
78
- try:
79
- # 获取内存使用信息
80
- memory_info = self.process.memory_info()
81
- memory_percent = self.process.memory_percent()
82
-
83
- # 记录内存使用情况
84
- self.logger.debug(
85
- f"Memory usage: {memory_percent:.2f}% "
86
- f"(RSS: {memory_info.rss / 1024 / 1024:.2f} MB, "
87
- f"VMS: {memory_info.vms / 1024 / 1024:.2f} MB)"
88
- )
89
-
90
- # 检查是否超过阈值
91
- if memory_percent >= self.critical_threshold:
92
- self.logger.critical(
93
- f"Memory usage critical: {memory_percent:.2f}% "
94
- f"(RSS: {memory_info.rss / 1024 / 1024:.2f} MB)"
95
- )
96
- elif memory_percent >= self.warning_threshold:
97
- self.logger.warning(
98
- f"Memory usage high: {memory_percent:.2f}% "
99
- f"(RSS: {memory_info.rss / 1024 / 1024:.2f} MB)"
100
- )
101
-
102
- await asyncio.sleep(self.interval)
103
- except Exception as e:
104
- self.logger.error(f"Error in memory monitoring: {e}")
1
+ #!/usr/bin/python
2
+ # -*- coding:UTF-8 -*-
3
+ import asyncio
4
+ import psutil
5
+ from typing import Any, Optional
6
+
7
+ from crawlo.logging import get_logger
8
+ from crawlo.utils.error_handler import ErrorHandler
9
+ from crawlo.event import CrawlerEvent
10
+
11
+
12
+ class MemoryMonitorExtension:
13
+ """
14
+ 内存监控扩展
15
+ 定期监控爬虫进程的内存使用情况,并在超出阈值时发出警告
16
+ """
17
+
18
+ def __init__(self, crawler: Any):
19
+ self.task: Optional[asyncio.Task] = None
20
+ self.process = psutil.Process()
21
+ self.settings = crawler.settings
22
+ self.logger = get_logger(self.__class__.__name__)
23
+ self.error_handler = ErrorHandler(self.__class__.__name__, crawler.settings.get('LOG_LEVEL'))
24
+
25
+ # 获取配置参数
26
+ self.interval = self.settings.get_int('MEMORY_MONITOR_INTERVAL', 60) # 默认60秒检查一次
27
+ self.warning_threshold = self.settings.get_float('MEMORY_WARNING_THRESHOLD', 80.0) # 默认80%警告阈值
28
+ self.critical_threshold = self.settings.get_float('MEMORY_CRITICAL_THRESHOLD', 90.0) # 默认90%严重阈值
29
+
30
+ @classmethod
31
+ def create_instance(cls, crawler: Any) -> 'MemoryMonitorExtension':
32
+ # 只有当配置启用时才创建实例
33
+ if not crawler.settings.get_bool('MEMORY_MONITOR_ENABLED', False):
34
+ from crawlo.exceptions import NotConfigured
35
+ raise NotConfigured("MemoryMonitorExtension: MEMORY_MONITOR_ENABLED is False")
36
+
37
+ o = cls(crawler)
38
+ crawler.subscriber.subscribe(o.spider_opened, event=CrawlerEvent.SPIDER_OPENED)
39
+ crawler.subscriber.subscribe(o.spider_closed, event=CrawlerEvent.SPIDER_CLOSED)
40
+ return o
41
+
42
+ async def spider_opened(self) -> None:
43
+ """爬虫启动时开始监控"""
44
+ try:
45
+ self.task = asyncio.create_task(self._monitor_loop())
46
+ self.logger.info(
47
+ f"Memory monitor started. Interval: {self.interval}s, "
48
+ f"Warning threshold: {self.warning_threshold}%, Critical threshold: {self.critical_threshold}%"
49
+ )
50
+ except Exception as e:
51
+ self.error_handler.handle_error(
52
+ e,
53
+ context="启动内存监控失败",
54
+ raise_error=False
55
+ )
56
+
57
+ async def spider_closed(self) -> None:
58
+ """爬虫关闭时停止监控"""
59
+ try:
60
+ if self.task:
61
+ self.task.cancel()
62
+ try:
63
+ await self.task
64
+ except asyncio.CancelledError:
65
+ pass
66
+ self.task = None
67
+ self.logger.info("Memory monitor stopped.")
68
+ except Exception as e:
69
+ self.error_handler.handle_error(
70
+ e,
71
+ context="停止内存监控失败",
72
+ raise_error=False
73
+ )
74
+
75
+ async def _monitor_loop(self) -> None:
76
+ """内存监控循环"""
77
+ while True:
78
+ try:
79
+ # 获取内存使用信息
80
+ memory_info = self.process.memory_info()
81
+ memory_percent = self.process.memory_percent()
82
+
83
+ # 记录内存使用情况
84
+ self.logger.debug(
85
+ f"Memory usage: {memory_percent:.2f}% "
86
+ f"(RSS: {memory_info.rss / 1024 / 1024:.2f} MB, "
87
+ f"VMS: {memory_info.vms / 1024 / 1024:.2f} MB)"
88
+ )
89
+
90
+ # 检查是否超过阈值
91
+ if memory_percent >= self.critical_threshold:
92
+ self.logger.critical(
93
+ f"Memory usage critical: {memory_percent:.2f}% "
94
+ f"(RSS: {memory_info.rss / 1024 / 1024:.2f} MB)"
95
+ )
96
+ elif memory_percent >= self.warning_threshold:
97
+ self.logger.warning(
98
+ f"Memory usage high: {memory_percent:.2f}% "
99
+ f"(RSS: {memory_info.rss / 1024 / 1024:.2f} MB)"
100
+ )
101
+
102
+ await asyncio.sleep(self.interval)
103
+ except Exception as e:
104
+ self.logger.error(f"Error in memory monitoring: {e}")
105
105
  await asyncio.sleep(self.interval)
@@ -1,134 +1,134 @@
1
- #!/usr/bin/python
2
- # -*- coding:UTF-8 -*-
3
- import io
4
- import os
5
- import pstats
6
- import asyncio
7
- import cProfile
8
- from typing import Any, Optional
9
-
10
- from crawlo.logging import get_logger
11
- from crawlo.utils.error_handler import ErrorHandler
12
- from crawlo.event import CrawlerEvent
13
-
14
-
15
- class PerformanceProfilerExtension:
16
- """
17
- 性能分析扩展
18
- 在爬虫运行期间进行性能分析,帮助优化爬虫性能
19
- """
20
-
21
- def __init__(self, crawler: Any):
22
- self.settings = crawler.settings
23
- self.logger = get_logger(self.__class__.__name__)
24
- self.error_handler = ErrorHandler(self.__class__.__name__, crawler.settings.get('LOG_LEVEL'))
25
-
26
- # 获取配置参数
27
- self.enabled = self.settings.get_bool('PERFORMANCE_PROFILER_ENABLED', False)
28
- self.output_dir = self.settings.get('PERFORMANCE_PROFILER_OUTPUT_DIR', 'profiling')
29
- self.interval = self.settings.get_int('PERFORMANCE_PROFILER_INTERVAL', 300) # 默认5分钟
30
-
31
- self.profiler: Optional[cProfile.Profile] = None
32
- self.task: Optional[asyncio.Task] = None
33
-
34
- # 创建输出目录
35
- if self.enabled:
36
- os.makedirs(self.output_dir, exist_ok=True)
37
-
38
- @classmethod
39
- def create_instance(cls, crawler: Any) -> 'PerformanceProfilerExtension':
40
- # 只有当配置启用时才创建实例
41
- if not crawler.settings.get_bool('PERFORMANCE_PROFILER_ENABLED', False):
42
- from crawlo.exceptions import NotConfigured
43
- raise NotConfigured("PerformanceProfilerExtension: PERFORMANCE_PROFILER_ENABLED is False")
44
-
45
- o = cls(crawler)
46
- if o.enabled:
47
- crawler.subscriber.subscribe(o.spider_opened, event=CrawlerEvent.SPIDER_OPENED)
48
- crawler.subscriber.subscribe(o.spider_closed, event=CrawlerEvent.SPIDER_CLOSED)
49
- return o
50
-
51
- async def spider_opened(self) -> None:
52
- """爬虫启动时开始性能分析"""
53
- if not self.enabled:
54
- return
55
-
56
- try:
57
- self.profiler = cProfile.Profile()
58
- self.profiler.enable()
59
-
60
- # 启动定期保存分析结果的任务
61
- self.task = asyncio.create_task(self._periodic_save())
62
-
63
- self.logger.info("Performance profiler started.")
64
- except Exception as e:
65
- self.error_handler.handle_error(
66
- e,
67
- context="启动性能分析器失败",
68
- raise_error=False
69
- )
70
-
71
- async def spider_closed(self) -> None:
72
- """爬虫关闭时停止性能分析并保存结果"""
73
- if not self.enabled or not self.profiler:
74
- return
75
-
76
- try:
77
- # 停止定期保存任务
78
- if self.task:
79
- self.task.cancel()
80
- try:
81
- await self.task
82
- except asyncio.CancelledError:
83
- pass
84
-
85
- # 停止分析器并保存最终结果
86
- self.profiler.disable()
87
-
88
- # 保存分析结果
89
- await self._save_profile("final")
90
- self.logger.info("Performance profiler stopped and results saved.")
91
- except Exception as e:
92
- self.error_handler.handle_error(
93
- e,
94
- context="停止性能分析器失败",
95
- raise_error=False
96
- )
97
-
98
- async def _periodic_save(self) -> None:
99
- """定期保存分析结果"""
100
- counter = 1
101
- while True:
102
- try:
103
- await asyncio.sleep(self.interval)
104
- if self.profiler:
105
- # 临时禁用分析器以保存结果
106
- self.profiler.disable()
107
- await self._save_profile(f"periodic_{counter}")
108
- counter += 1
109
- # 重新启用分析器
110
- self.profiler.enable()
111
- except asyncio.CancelledError:
112
- break
113
- except Exception as e:
114
- self.logger.error(f"Error in periodic profiling save: {e}")
115
-
116
- async def _save_profile(self, name: str) -> None:
117
- """保存分析结果到文件"""
118
- try:
119
- # 创建内存中的字符串流
120
- s = io.StringIO()
121
- ps = pstats.Stats(self.profiler, stream=s)
122
-
123
- # 排序并打印统计信息
124
- ps.sort_stats('cumulative')
125
- ps.print_stats()
126
-
127
- # 保存到文件
128
- filename = os.path.join(self.output_dir, f'profile_{name}.txt')
129
- with open(filename, 'w', encoding='utf-8') as f:
130
- f.write(s.getvalue())
131
-
132
- self.logger.info(f"Performance profile saved to {filename}")
133
- except Exception as e:
1
+ #!/usr/bin/python
2
+ # -*- coding:UTF-8 -*-
3
+ import io
4
+ import os
5
+ import pstats
6
+ import asyncio
7
+ import cProfile
8
+ from typing import Any, Optional
9
+
10
+ from crawlo.logging import get_logger
11
+ from crawlo.utils.error_handler import ErrorHandler
12
+ from crawlo.event import CrawlerEvent
13
+
14
+
15
+ class PerformanceProfilerExtension:
16
+ """
17
+ 性能分析扩展
18
+ 在爬虫运行期间进行性能分析,帮助优化爬虫性能
19
+ """
20
+
21
+ def __init__(self, crawler: Any):
22
+ self.settings = crawler.settings
23
+ self.logger = get_logger(self.__class__.__name__)
24
+ self.error_handler = ErrorHandler(self.__class__.__name__, crawler.settings.get('LOG_LEVEL'))
25
+
26
+ # 获取配置参数
27
+ self.enabled = self.settings.get_bool('PERFORMANCE_PROFILER_ENABLED', False)
28
+ self.output_dir = self.settings.get('PERFORMANCE_PROFILER_OUTPUT_DIR', 'profiling')
29
+ self.interval = self.settings.get_int('PERFORMANCE_PROFILER_INTERVAL', 300) # 默认5分钟
30
+
31
+ self.profiler: Optional[cProfile.Profile] = None
32
+ self.task: Optional[asyncio.Task] = None
33
+
34
+ # 创建输出目录
35
+ if self.enabled:
36
+ os.makedirs(self.output_dir, exist_ok=True)
37
+
38
+ @classmethod
39
+ def create_instance(cls, crawler: Any) -> 'PerformanceProfilerExtension':
40
+ # 只有当配置启用时才创建实例
41
+ if not crawler.settings.get_bool('PERFORMANCE_PROFILER_ENABLED', False):
42
+ from crawlo.exceptions import NotConfigured
43
+ raise NotConfigured("PerformanceProfilerExtension: PERFORMANCE_PROFILER_ENABLED is False")
44
+
45
+ o = cls(crawler)
46
+ if o.enabled:
47
+ crawler.subscriber.subscribe(o.spider_opened, event=CrawlerEvent.SPIDER_OPENED)
48
+ crawler.subscriber.subscribe(o.spider_closed, event=CrawlerEvent.SPIDER_CLOSED)
49
+ return o
50
+
51
+ async def spider_opened(self) -> None:
52
+ """爬虫启动时开始性能分析"""
53
+ if not self.enabled:
54
+ return
55
+
56
+ try:
57
+ self.profiler = cProfile.Profile()
58
+ self.profiler.enable()
59
+
60
+ # 启动定期保存分析结果的任务
61
+ self.task = asyncio.create_task(self._periodic_save())
62
+
63
+ self.logger.info("Performance profiler started.")
64
+ except Exception as e:
65
+ self.error_handler.handle_error(
66
+ e,
67
+ context="启动性能分析器失败",
68
+ raise_error=False
69
+ )
70
+
71
+ async def spider_closed(self) -> None:
72
+ """爬虫关闭时停止性能分析并保存结果"""
73
+ if not self.enabled or not self.profiler:
74
+ return
75
+
76
+ try:
77
+ # 停止定期保存任务
78
+ if self.task:
79
+ self.task.cancel()
80
+ try:
81
+ await self.task
82
+ except asyncio.CancelledError:
83
+ pass
84
+
85
+ # 停止分析器并保存最终结果
86
+ self.profiler.disable()
87
+
88
+ # 保存分析结果
89
+ await self._save_profile("final")
90
+ self.logger.info("Performance profiler stopped and results saved.")
91
+ except Exception as e:
92
+ self.error_handler.handle_error(
93
+ e,
94
+ context="停止性能分析器失败",
95
+ raise_error=False
96
+ )
97
+
98
+ async def _periodic_save(self) -> None:
99
+ """定期保存分析结果"""
100
+ counter = 1
101
+ while True:
102
+ try:
103
+ await asyncio.sleep(self.interval)
104
+ if self.profiler:
105
+ # 临时禁用分析器以保存结果
106
+ self.profiler.disable()
107
+ await self._save_profile(f"periodic_{counter}")
108
+ counter += 1
109
+ # 重新启用分析器
110
+ self.profiler.enable()
111
+ except asyncio.CancelledError:
112
+ break
113
+ except Exception as e:
114
+ self.logger.error(f"Error in periodic profiling save: {e}")
115
+
116
+ async def _save_profile(self, name: str) -> None:
117
+ """保存分析结果到文件"""
118
+ try:
119
+ # 创建内存中的字符串流
120
+ s = io.StringIO()
121
+ ps = pstats.Stats(self.profiler, stream=s)
122
+
123
+ # 排序并打印统计信息
124
+ ps.sort_stats('cumulative')
125
+ ps.print_stats()
126
+
127
+ # 保存到文件
128
+ filename = os.path.join(self.output_dir, f'profile_{name}.txt')
129
+ with open(filename, 'w', encoding='utf-8') as f:
130
+ f.write(s.getvalue())
131
+
132
+ self.logger.info(f"Performance profile saved to {filename}")
133
+ except Exception as e:
134
134
  self.logger.error(f"Error saving performance profile: {e}")