crawlo 1.3.2__py3-none-any.whl → 1.3.4__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.
- crawlo/__init__.py +24 -0
- crawlo/__version__.py +1 -1
- crawlo/commands/run.py +58 -32
- crawlo/core/__init__.py +44 -0
- crawlo/core/engine.py +119 -45
- crawlo/core/scheduler.py +4 -3
- crawlo/crawler.py +603 -1133
- crawlo/downloader/aiohttp_downloader.py +4 -2
- crawlo/extension/__init__.py +1 -1
- crawlo/extension/logging_extension.py +23 -7
- crawlo/factories/__init__.py +28 -0
- crawlo/factories/base.py +69 -0
- crawlo/factories/crawler.py +104 -0
- crawlo/factories/registry.py +85 -0
- crawlo/filters/aioredis_filter.py +25 -2
- crawlo/framework.py +292 -0
- crawlo/initialization/__init__.py +40 -0
- crawlo/initialization/built_in.py +426 -0
- crawlo/initialization/context.py +142 -0
- crawlo/initialization/core.py +194 -0
- crawlo/initialization/phases.py +149 -0
- crawlo/initialization/registry.py +146 -0
- crawlo/items/base.py +2 -1
- crawlo/logging/__init__.py +38 -0
- crawlo/logging/config.py +97 -0
- crawlo/logging/factory.py +129 -0
- crawlo/logging/manager.py +112 -0
- crawlo/middleware/middleware_manager.py +1 -1
- crawlo/middleware/offsite.py +1 -1
- crawlo/mode_manager.py +26 -1
- crawlo/pipelines/pipeline_manager.py +2 -1
- crawlo/project.py +76 -46
- crawlo/queue/pqueue.py +11 -5
- crawlo/queue/queue_manager.py +143 -19
- crawlo/queue/redis_priority_queue.py +69 -49
- crawlo/settings/default_settings.py +110 -14
- crawlo/settings/setting_manager.py +29 -13
- crawlo/spider/__init__.py +34 -16
- crawlo/stats_collector.py +17 -3
- crawlo/task_manager.py +112 -3
- crawlo/templates/project/settings.py.tmpl +103 -202
- crawlo/templates/project/settings_distributed.py.tmpl +122 -135
- crawlo/templates/project/settings_gentle.py.tmpl +149 -43
- crawlo/templates/project/settings_high_performance.py.tmpl +127 -90
- crawlo/templates/project/settings_minimal.py.tmpl +46 -15
- crawlo/templates/project/settings_simple.py.tmpl +138 -75
- crawlo/templates/project/spiders/__init__.py.tmpl +5 -1
- crawlo/templates/run.py.tmpl +10 -14
- crawlo/templates/spiders_init.py.tmpl +10 -0
- crawlo/tools/network_diagnostic.py +365 -0
- crawlo/utils/class_loader.py +26 -0
- crawlo/utils/error_handler.py +76 -35
- crawlo/utils/log.py +41 -144
- crawlo/utils/redis_connection_pool.py +43 -6
- crawlo/utils/request_serializer.py +8 -1
- {crawlo-1.3.2.dist-info → crawlo-1.3.4.dist-info}/METADATA +120 -14
- {crawlo-1.3.2.dist-info → crawlo-1.3.4.dist-info}/RECORD +104 -45
- tests/authenticated_proxy_example.py +2 -2
- tests/baidu_performance_test.py +109 -0
- tests/baidu_test.py +60 -0
- tests/comprehensive_framework_test.py +213 -0
- tests/comprehensive_test.py +82 -0
- tests/comprehensive_testing_summary.md +187 -0
- tests/debug_configure.py +70 -0
- tests/debug_framework_logger.py +85 -0
- tests/debug_log_levels.py +64 -0
- tests/distributed_test.py +67 -0
- tests/distributed_test_debug.py +77 -0
- tests/final_command_test_report.md +0 -0
- tests/final_comprehensive_test.py +152 -0
- tests/final_validation_test.py +183 -0
- tests/framework_performance_test.py +203 -0
- tests/optimized_performance_test.py +212 -0
- tests/performance_comparison.py +246 -0
- tests/queue_blocking_test.py +114 -0
- tests/queue_test.py +90 -0
- tests/scrapy_comparison/ofweek_scrapy.py +139 -0
- tests/scrapy_comparison/scrapy_test.py +134 -0
- tests/simple_command_test.py +120 -0
- tests/simple_crawlo_test.py +128 -0
- tests/simple_log_test.py +58 -0
- tests/simple_optimization_test.py +129 -0
- tests/simple_spider_test.py +50 -0
- tests/simple_test.py +48 -0
- tests/test_all_commands.py +231 -0
- tests/test_batch_processor.py +179 -0
- tests/test_component_factory.py +175 -0
- tests/test_controlled_spider_mixin.py +80 -0
- tests/test_enhanced_error_handler_comprehensive.py +246 -0
- tests/test_factories.py +253 -0
- tests/test_framework_logger.py +67 -0
- tests/test_framework_startup.py +65 -0
- tests/test_large_scale_config.py +113 -0
- tests/test_large_scale_helper.py +236 -0
- tests/test_mode_change.py +73 -0
- tests/test_mode_consistency.py +1 -1
- tests/test_performance_monitor.py +116 -0
- tests/test_queue_empty_check.py +42 -0
- tests/untested_features_report.md +139 -0
- tests/verify_debug.py +52 -0
- tests/verify_log_fix.py +112 -0
- tests/DOUBLE_CRAWLO_PREFIX_FIX_REPORT.md +0 -82
- {crawlo-1.3.2.dist-info → crawlo-1.3.4.dist-info}/WHEEL +0 -0
- {crawlo-1.3.2.dist-info → crawlo-1.3.4.dist-info}/entry_points.txt +0 -0
- {crawlo-1.3.2.dist-info → crawlo-1.3.4.dist-info}/top_level.txt +0 -0
tests/test_factories.py
ADDED
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
工厂模式测试
|
|
5
|
+
测试 ComponentRegistry, ComponentFactory, DefaultComponentFactory, CrawlerComponentFactory
|
|
6
|
+
"""
|
|
7
|
+
import sys
|
|
8
|
+
import os
|
|
9
|
+
import unittest
|
|
10
|
+
from unittest.mock import Mock, patch
|
|
11
|
+
|
|
12
|
+
# 添加项目根目录到 Python 路径
|
|
13
|
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
|
|
14
|
+
|
|
15
|
+
from crawlo.factories import (
|
|
16
|
+
ComponentRegistry,
|
|
17
|
+
ComponentFactory,
|
|
18
|
+
ComponentSpec,
|
|
19
|
+
CrawlerComponentFactory,
|
|
20
|
+
get_component_registry
|
|
21
|
+
)
|
|
22
|
+
from crawlo.factories.base import DefaultComponentFactory
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class TestComponent:
|
|
26
|
+
"""测试组件类"""
|
|
27
|
+
def __init__(self, name="test_component", value=42):
|
|
28
|
+
self.name = name
|
|
29
|
+
self.value = value
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class TestFactories(unittest.TestCase):
|
|
33
|
+
"""工厂模式测试类"""
|
|
34
|
+
|
|
35
|
+
def setUp(self):
|
|
36
|
+
"""测试前准备"""
|
|
37
|
+
self.registry = ComponentRegistry()
|
|
38
|
+
|
|
39
|
+
def test_component_spec_creation(self):
|
|
40
|
+
"""测试组件规范创建"""
|
|
41
|
+
def factory_func(**kwargs):
|
|
42
|
+
return TestComponent(**kwargs)
|
|
43
|
+
|
|
44
|
+
spec = ComponentSpec(
|
|
45
|
+
name="test_component",
|
|
46
|
+
component_type=TestComponent,
|
|
47
|
+
factory_func=factory_func,
|
|
48
|
+
dependencies=[],
|
|
49
|
+
singleton=False
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
self.assertEqual(spec.name, "test_component")
|
|
53
|
+
self.assertEqual(spec.component_type, TestComponent)
|
|
54
|
+
self.assertEqual(spec.dependencies, [])
|
|
55
|
+
self.assertFalse(spec.singleton)
|
|
56
|
+
|
|
57
|
+
def test_default_component_factory(self):
|
|
58
|
+
"""测试默认组件工厂"""
|
|
59
|
+
factory = DefaultComponentFactory()
|
|
60
|
+
|
|
61
|
+
def factory_func(**kwargs):
|
|
62
|
+
return TestComponent(**kwargs)
|
|
63
|
+
|
|
64
|
+
spec = ComponentSpec(
|
|
65
|
+
name="test_component",
|
|
66
|
+
component_type=TestComponent,
|
|
67
|
+
factory_func=factory_func
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
# 测试创建组件
|
|
71
|
+
component = factory.create(spec, name="created_component", value=100)
|
|
72
|
+
self.assertIsInstance(component, TestComponent)
|
|
73
|
+
self.assertEqual(component.name, "created_component")
|
|
74
|
+
self.assertEqual(component.value, 100)
|
|
75
|
+
|
|
76
|
+
# 测试单例模式
|
|
77
|
+
spec_singleton = ComponentSpec(
|
|
78
|
+
name="singleton_component",
|
|
79
|
+
component_type=TestComponent,
|
|
80
|
+
factory_func=factory_func,
|
|
81
|
+
singleton=True
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
component1 = factory.create(spec_singleton, name="singleton_1", value=200)
|
|
85
|
+
component2 = factory.create(spec_singleton, name="singleton_2", value=300)
|
|
86
|
+
|
|
87
|
+
# 单例应该返回相同的实例
|
|
88
|
+
self.assertIs(component1, component2)
|
|
89
|
+
self.assertEqual(component1.value, 200) # 应该是第一次创建时的值
|
|
90
|
+
|
|
91
|
+
def test_component_registry_registration(self):
|
|
92
|
+
"""测试组件注册表注册功能"""
|
|
93
|
+
def factory_func(**kwargs):
|
|
94
|
+
return TestComponent(**kwargs)
|
|
95
|
+
|
|
96
|
+
spec = ComponentSpec(
|
|
97
|
+
name="registered_component",
|
|
98
|
+
component_type=TestComponent,
|
|
99
|
+
factory_func=factory_func
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
# 注册组件规范
|
|
103
|
+
self.registry.register(spec)
|
|
104
|
+
|
|
105
|
+
# 验证注册
|
|
106
|
+
retrieved_spec = self.registry.get_spec("registered_component")
|
|
107
|
+
self.assertEqual(retrieved_spec, spec)
|
|
108
|
+
|
|
109
|
+
# 测试列出组件
|
|
110
|
+
components = self.registry.list_components()
|
|
111
|
+
self.assertIn("registered_component", components)
|
|
112
|
+
|
|
113
|
+
def test_component_registry_creation(self):
|
|
114
|
+
"""测试组件注册表创建功能"""
|
|
115
|
+
def factory_func(**kwargs):
|
|
116
|
+
return TestComponent(**kwargs)
|
|
117
|
+
|
|
118
|
+
spec = ComponentSpec(
|
|
119
|
+
name="creatable_component",
|
|
120
|
+
component_type=TestComponent,
|
|
121
|
+
factory_func=factory_func
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
# 注册组件规范
|
|
125
|
+
self.registry.register(spec)
|
|
126
|
+
|
|
127
|
+
# 创建组件时应该出现错误,因为没有传递name参数
|
|
128
|
+
with self.assertRaises(TypeError):
|
|
129
|
+
component = self.registry.create("creatable_component", name="created", value=500)
|
|
130
|
+
|
|
131
|
+
def test_global_component_registry(self):
|
|
132
|
+
"""测试全局组件注册表"""
|
|
133
|
+
registry = get_component_registry()
|
|
134
|
+
self.assertIsInstance(registry, ComponentRegistry)
|
|
135
|
+
|
|
136
|
+
# 测试注册表是否包含预注册的组件
|
|
137
|
+
components = registry.list_components()
|
|
138
|
+
# 应该至少包含crawler组件
|
|
139
|
+
self.assertGreater(len(components), 0)
|
|
140
|
+
|
|
141
|
+
def test_crawler_component_factory_supports(self):
|
|
142
|
+
"""测试Crawler组件工厂支持检查"""
|
|
143
|
+
factory = CrawlerComponentFactory()
|
|
144
|
+
|
|
145
|
+
# 测试支持检查(CrawlerComponentFactory只支持特定类型)
|
|
146
|
+
class Engine:
|
|
147
|
+
pass
|
|
148
|
+
|
|
149
|
+
class MockEngine:
|
|
150
|
+
pass
|
|
151
|
+
|
|
152
|
+
# Engine应该被支持
|
|
153
|
+
self.assertTrue(factory.supports(Engine))
|
|
154
|
+
|
|
155
|
+
# MockEngine不应该被支持
|
|
156
|
+
self.assertFalse(factory.supports(MockEngine))
|
|
157
|
+
|
|
158
|
+
def test_crawler_component_factory_create_without_crawler(self):
|
|
159
|
+
"""测试Crawler组件工厂创建时缺少crawler依赖"""
|
|
160
|
+
factory = CrawlerComponentFactory()
|
|
161
|
+
|
|
162
|
+
# 测试创建功能(需要crawler依赖)
|
|
163
|
+
def mock_engine_factory(crawler=None, **kwargs):
|
|
164
|
+
if crawler is None:
|
|
165
|
+
raise ValueError("需要crawler实例")
|
|
166
|
+
return "mock_engine"
|
|
167
|
+
|
|
168
|
+
spec = ComponentSpec(
|
|
169
|
+
name="mock_engine",
|
|
170
|
+
component_type=type('Engine', (), {}), # 使用Engine类型
|
|
171
|
+
factory_func=mock_engine_factory,
|
|
172
|
+
dependencies=['crawler']
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
# 测试缺少依赖时的错误处理
|
|
176
|
+
with self.assertRaises(ValueError) as context:
|
|
177
|
+
factory.create(spec)
|
|
178
|
+
self.assertIn("Crawler instance required for component", str(context.exception))
|
|
179
|
+
|
|
180
|
+
def test_crawler_component_factory_create_with_crawler(self):
|
|
181
|
+
"""测试Crawler组件工厂带crawler依赖的创建"""
|
|
182
|
+
factory = CrawlerComponentFactory()
|
|
183
|
+
|
|
184
|
+
def mock_engine_factory(crawler=None, **kwargs):
|
|
185
|
+
if crawler is None:
|
|
186
|
+
raise ValueError("需要crawler实例")
|
|
187
|
+
return "mock_engine"
|
|
188
|
+
|
|
189
|
+
spec = ComponentSpec(
|
|
190
|
+
name="mock_engine",
|
|
191
|
+
component_type=type('MockEngine', (), {}),
|
|
192
|
+
factory_func=mock_engine_factory,
|
|
193
|
+
dependencies=['crawler']
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
# 测试带依赖的创建
|
|
197
|
+
result = factory.create(spec, crawler="mock_crawler")
|
|
198
|
+
self.assertEqual(result, "mock_engine")
|
|
199
|
+
|
|
200
|
+
def test_component_registry_clear(self):
|
|
201
|
+
"""测试组件注册表清空功能"""
|
|
202
|
+
def factory_func(**kwargs):
|
|
203
|
+
return TestComponent(**kwargs)
|
|
204
|
+
|
|
205
|
+
spec = ComponentSpec(
|
|
206
|
+
name="clearable_component",
|
|
207
|
+
component_type=TestComponent,
|
|
208
|
+
factory_func=factory_func
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
# 注册组件
|
|
212
|
+
self.registry.register(spec)
|
|
213
|
+
self.assertIsNotNone(self.registry.get_spec("clearable_component"))
|
|
214
|
+
|
|
215
|
+
# 清空注册表
|
|
216
|
+
self.registry.clear()
|
|
217
|
+
|
|
218
|
+
# 验证已清空
|
|
219
|
+
self.assertIsNone(self.registry.get_spec("clearable_component"))
|
|
220
|
+
self.assertEqual(len(self.registry.list_components()), 0)
|
|
221
|
+
|
|
222
|
+
def test_default_component_factory_clear_singletons(self):
|
|
223
|
+
"""测试默认组件工厂清空单例功能"""
|
|
224
|
+
factory = DefaultComponentFactory()
|
|
225
|
+
|
|
226
|
+
def factory_func(**kwargs):
|
|
227
|
+
return TestComponent(**kwargs)
|
|
228
|
+
|
|
229
|
+
spec_singleton = ComponentSpec(
|
|
230
|
+
name="clear_singleton_component",
|
|
231
|
+
component_type=TestComponent,
|
|
232
|
+
factory_func=factory_func,
|
|
233
|
+
singleton=True
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
# 创建单例组件
|
|
237
|
+
component1 = factory.create(spec_singleton, name="singleton_1", value=200)
|
|
238
|
+
component2 = factory.create(spec_singleton, name="singleton_2", value=300)
|
|
239
|
+
|
|
240
|
+
# 验证是单例
|
|
241
|
+
self.assertIs(component1, component2)
|
|
242
|
+
|
|
243
|
+
# 清空单例
|
|
244
|
+
factory.clear_singletons()
|
|
245
|
+
|
|
246
|
+
# 再次创建应该得到新实例
|
|
247
|
+
component3 = factory.create(spec_singleton, name="singleton_3", value=400)
|
|
248
|
+
self.assertIsNot(component1, component3)
|
|
249
|
+
self.assertEqual(component3.value, 400)
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
if __name__ == '__main__':
|
|
253
|
+
unittest.main()
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: UTF-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
测试框架日志系统
|
|
5
|
+
"""
|
|
6
|
+
import sys
|
|
7
|
+
import os
|
|
8
|
+
sys.path.insert(0, '/')
|
|
9
|
+
|
|
10
|
+
from crawlo.core.framework_initializer import initialize_framework, get_framework_initializer
|
|
11
|
+
from crawlo.utils.log import get_logger, LoggerManager
|
|
12
|
+
|
|
13
|
+
def test_framework_logger():
|
|
14
|
+
print("=== 测试框架日志系统 ===")
|
|
15
|
+
|
|
16
|
+
# 1. 初始化框架,传入基本配置
|
|
17
|
+
print("1. 初始化框架...")
|
|
18
|
+
custom_settings = {
|
|
19
|
+
'LOG_LEVEL': 'INFO',
|
|
20
|
+
'LOG_FILE': 'test_framework.log',
|
|
21
|
+
'PROJECT_NAME': 'test_framework',
|
|
22
|
+
'RUN_MODE': 'standalone'
|
|
23
|
+
}
|
|
24
|
+
settings = initialize_framework(custom_settings)
|
|
25
|
+
print(f" LOG_LEVEL: {settings.get('LOG_LEVEL')}")
|
|
26
|
+
print(f" LOG_FILE: {settings.get('LOG_FILE')}")
|
|
27
|
+
|
|
28
|
+
# 2. 获取框架初始化管理器
|
|
29
|
+
init_manager = get_framework_initializer()
|
|
30
|
+
print(f" 框架是否就绪: {init_manager.is_ready}")
|
|
31
|
+
print(f" 初始化阶段: {init_manager.phase}")
|
|
32
|
+
|
|
33
|
+
# 3. 测试框架logger
|
|
34
|
+
framework_logger = init_manager.logger
|
|
35
|
+
if framework_logger:
|
|
36
|
+
print(f" 框架logger名称: {framework_logger.name}")
|
|
37
|
+
print(f" 框架logger级别: {framework_logger.level}")
|
|
38
|
+
print(f" 框架logger处理器数量: {len(framework_logger.handlers)}")
|
|
39
|
+
|
|
40
|
+
for i, handler in enumerate(framework_logger.handlers):
|
|
41
|
+
handler_type = type(handler).__name__
|
|
42
|
+
print(f" 处理器{i}: {handler_type}, 级别: {handler.level}")
|
|
43
|
+
else:
|
|
44
|
+
print(" 框架logger为None!")
|
|
45
|
+
framework_logger = get_logger('crawlo.framework')
|
|
46
|
+
print(f" 手动创建的框架logger: {framework_logger.name}")
|
|
47
|
+
|
|
48
|
+
# 4. 测试日志输出
|
|
49
|
+
print("2. 测试日志输出...")
|
|
50
|
+
framework_logger.info("Crawlo框架初始化完成")
|
|
51
|
+
framework_logger.info("Crawlo Framework Started 1.3.3")
|
|
52
|
+
framework_logger.info("使用单机模式 - 简单快速,适合开发和中小规模爬取")
|
|
53
|
+
framework_logger.info("Run Mode: standalone")
|
|
54
|
+
framework_logger.info("Starting running test_spider")
|
|
55
|
+
|
|
56
|
+
# 5. 测试其他logger
|
|
57
|
+
print("3. 测试其他组件logger...")
|
|
58
|
+
queue_logger = get_logger('QueueManager')
|
|
59
|
+
queue_logger.info("Queue initialized successfully Type: memory")
|
|
60
|
+
|
|
61
|
+
scheduler_logger = get_logger('Scheduler')
|
|
62
|
+
scheduler_logger.info("enabled filters: crawlo.filters.memory_filter.MemoryFilter")
|
|
63
|
+
|
|
64
|
+
print("=== 测试完成 ===")
|
|
65
|
+
|
|
66
|
+
if __name__ == "__main__":
|
|
67
|
+
test_framework_logger()
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
测试框架启动信息是否正确输出到日志文件
|
|
5
|
+
"""
|
|
6
|
+
import sys
|
|
7
|
+
import os
|
|
8
|
+
sys.path.insert(0, '/')
|
|
9
|
+
|
|
10
|
+
def test_framework_startup():
|
|
11
|
+
print("=== 测试框架启动信息输出 ===")
|
|
12
|
+
|
|
13
|
+
# 删除旧的日志文件
|
|
14
|
+
test_log_file = '/Users/oscar/projects/Crawlo/test_startup.log'
|
|
15
|
+
if os.path.exists(test_log_file):
|
|
16
|
+
os.remove(test_log_file)
|
|
17
|
+
|
|
18
|
+
# 准备测试设置
|
|
19
|
+
test_settings = {
|
|
20
|
+
'PROJECT_NAME': 'test_startup',
|
|
21
|
+
'LOG_LEVEL': 'INFO',
|
|
22
|
+
'LOG_FILE': test_log_file,
|
|
23
|
+
'RUN_MODE': 'standalone'
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
# 初始化框架
|
|
27
|
+
from crawlo.core.framework_initializer import initialize_framework
|
|
28
|
+
settings = initialize_framework(test_settings)
|
|
29
|
+
|
|
30
|
+
print(f"设置初始化完成: {settings.get('PROJECT_NAME')}")
|
|
31
|
+
|
|
32
|
+
# 检查日志文件是否包含框架启动信息
|
|
33
|
+
if os.path.exists(test_log_file):
|
|
34
|
+
with open(test_log_file, 'r', encoding='utf-8') as f:
|
|
35
|
+
content = f.read()
|
|
36
|
+
print(f"日志文件内容长度: {len(content)} 字符")
|
|
37
|
+
|
|
38
|
+
# 检查关键的框架启动信息
|
|
39
|
+
if "Crawlo框架初始化完成" in content:
|
|
40
|
+
print("✅ 发现: Crawlo框架初始化完成")
|
|
41
|
+
else:
|
|
42
|
+
print("❌ 未找到: Crawlo框架初始化完成")
|
|
43
|
+
|
|
44
|
+
if "Crawlo Framework Started" in content:
|
|
45
|
+
print("✅ 发现: Crawlo Framework Started")
|
|
46
|
+
else:
|
|
47
|
+
print("❌ 未找到: Crawlo Framework Started")
|
|
48
|
+
|
|
49
|
+
if "使用单机模式" in content:
|
|
50
|
+
print("✅ 发现: 使用单机模式")
|
|
51
|
+
else:
|
|
52
|
+
print("❌ 未找到: 使用单机模式")
|
|
53
|
+
|
|
54
|
+
print("\n前50行日志内容:")
|
|
55
|
+
lines = content.split('\n')[:50]
|
|
56
|
+
for i, line in enumerate(lines, 1):
|
|
57
|
+
if any(keyword in line for keyword in ["框架", "Framework", "Started"]):
|
|
58
|
+
print(f"{i:3d}: {line}")
|
|
59
|
+
else:
|
|
60
|
+
print("❌ 日志文件未创建")
|
|
61
|
+
|
|
62
|
+
print("=== 测试完成 ===")
|
|
63
|
+
|
|
64
|
+
if __name__ == "__main__":
|
|
65
|
+
test_framework_startup()
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
大规模配置工具测试
|
|
5
|
+
测试 LargeScaleConfig, apply_large_scale_config
|
|
6
|
+
"""
|
|
7
|
+
import sys
|
|
8
|
+
import os
|
|
9
|
+
import unittest
|
|
10
|
+
from unittest.mock import Mock, patch, MagicMock
|
|
11
|
+
|
|
12
|
+
# 添加项目根目录到 Python 路径
|
|
13
|
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
|
|
14
|
+
|
|
15
|
+
from crawlo.utils.large_scale_config import LargeScaleConfig, apply_large_scale_config
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class TestLargeScaleConfig(unittest.TestCase):
|
|
19
|
+
"""大规模配置工具测试"""
|
|
20
|
+
|
|
21
|
+
def test_conservative_config(self):
|
|
22
|
+
"""测试保守配置"""
|
|
23
|
+
config = LargeScaleConfig.conservative_config(concurrency=8)
|
|
24
|
+
|
|
25
|
+
# 验证基本配置
|
|
26
|
+
self.assertEqual(config['CONCURRENCY'], 8)
|
|
27
|
+
self.assertEqual(config['SCHEDULER_MAX_QUEUE_SIZE'], 80) # 8 * 10
|
|
28
|
+
self.assertEqual(config['DOWNLOAD_DELAY'], 0.2)
|
|
29
|
+
self.assertEqual(config['MAX_RUNNING_SPIDERS'], 1)
|
|
30
|
+
|
|
31
|
+
# 验证连接池配置
|
|
32
|
+
self.assertEqual(config['CONNECTION_POOL_LIMIT'], 16) # 8 * 2
|
|
33
|
+
|
|
34
|
+
# 验证重试配置
|
|
35
|
+
self.assertEqual(config['MAX_RETRY_TIMES'], 2)
|
|
36
|
+
|
|
37
|
+
def test_balanced_config(self):
|
|
38
|
+
"""测试平衡配置"""
|
|
39
|
+
config = LargeScaleConfig.balanced_config(concurrency=16)
|
|
40
|
+
|
|
41
|
+
# 验证基本配置
|
|
42
|
+
self.assertEqual(config['CONCURRENCY'], 16)
|
|
43
|
+
self.assertEqual(config['SCHEDULER_MAX_QUEUE_SIZE'], 240) # 16 * 15
|
|
44
|
+
self.assertEqual(config['DOWNLOAD_DELAY'], 0.1)
|
|
45
|
+
self.assertEqual(config['MAX_RUNNING_SPIDERS'], 2)
|
|
46
|
+
|
|
47
|
+
# 验证连接池配置
|
|
48
|
+
self.assertEqual(config['CONNECTION_POOL_LIMIT'], 48) # 16 * 3
|
|
49
|
+
|
|
50
|
+
# 验证重试配置
|
|
51
|
+
self.assertEqual(config['MAX_RETRY_TIMES'], 3)
|
|
52
|
+
|
|
53
|
+
def test_aggressive_config(self):
|
|
54
|
+
"""测试激进配置"""
|
|
55
|
+
config = LargeScaleConfig.aggressive_config(concurrency=32)
|
|
56
|
+
|
|
57
|
+
# 验证基本配置
|
|
58
|
+
self.assertEqual(config['CONCURRENCY'], 32)
|
|
59
|
+
self.assertEqual(config['SCHEDULER_MAX_QUEUE_SIZE'], 640) # 32 * 20
|
|
60
|
+
self.assertEqual(config['DOWNLOAD_DELAY'], 0.05)
|
|
61
|
+
self.assertEqual(config['MAX_RUNNING_SPIDERS'], 3)
|
|
62
|
+
|
|
63
|
+
# 验证连接池配置
|
|
64
|
+
self.assertEqual(config['CONNECTION_POOL_LIMIT'], 128) # 32 * 4
|
|
65
|
+
|
|
66
|
+
# 验证重试配置
|
|
67
|
+
self.assertEqual(config['MAX_RETRY_TIMES'], 5)
|
|
68
|
+
|
|
69
|
+
def test_memory_optimized_config(self):
|
|
70
|
+
"""测试内存优化配置"""
|
|
71
|
+
config = LargeScaleConfig.memory_optimized_config(concurrency=12)
|
|
72
|
+
|
|
73
|
+
# 验证基本配置
|
|
74
|
+
self.assertEqual(config['CONCURRENCY'], 12)
|
|
75
|
+
self.assertEqual(config['SCHEDULER_MAX_QUEUE_SIZE'], 60) # 12 * 5
|
|
76
|
+
self.assertEqual(config['DOWNLOAD_DELAY'], 0.1)
|
|
77
|
+
self.assertEqual(config['MAX_RUNNING_SPIDERS'], 1)
|
|
78
|
+
|
|
79
|
+
# 验证连接池配置
|
|
80
|
+
self.assertEqual(config['CONNECTION_POOL_LIMIT'], 12) # 12 * 1
|
|
81
|
+
|
|
82
|
+
# 验证内存限制配置
|
|
83
|
+
self.assertEqual(config['DOWNLOAD_MAXSIZE'], 2 * 1024 * 1024) # 2MB
|
|
84
|
+
self.assertEqual(config['DOWNLOAD_WARN_SIZE'], 512 * 1024) # 512KB
|
|
85
|
+
|
|
86
|
+
# 验证重试配置
|
|
87
|
+
self.assertEqual(config['MAX_RETRY_TIMES'], 2)
|
|
88
|
+
|
|
89
|
+
def test_apply_large_scale_config(self):
|
|
90
|
+
"""测试应用大规模配置"""
|
|
91
|
+
settings_dict = {}
|
|
92
|
+
|
|
93
|
+
# 应用平衡配置
|
|
94
|
+
apply_large_scale_config(settings_dict, "balanced", 16)
|
|
95
|
+
|
|
96
|
+
# 验证配置已应用
|
|
97
|
+
self.assertEqual(settings_dict['CONCURRENCY'], 16)
|
|
98
|
+
self.assertEqual(settings_dict['SCHEDULER_MAX_QUEUE_SIZE'], 240)
|
|
99
|
+
self.assertEqual(settings_dict['DOWNLOAD_DELAY'], 0.1)
|
|
100
|
+
|
|
101
|
+
def test_apply_large_scale_config_invalid_type(self):
|
|
102
|
+
"""测试应用无效的大规模配置类型"""
|
|
103
|
+
settings_dict = {}
|
|
104
|
+
|
|
105
|
+
# 应用无效配置类型应该抛出异常
|
|
106
|
+
with self.assertRaises(ValueError) as context:
|
|
107
|
+
apply_large_scale_config(settings_dict, "invalid_type", 16)
|
|
108
|
+
|
|
109
|
+
self.assertIn("不支持的配置类型", str(context.exception))
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
if __name__ == '__main__':
|
|
113
|
+
unittest.main()
|