-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbatch_processing.py
More file actions
346 lines (271 loc) · 11.6 KB
/
Copy pathbatch_processing.py
File metadata and controls
346 lines (271 loc) · 11.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
#!/usr/bin/env python3
"""
Batch Processing Example - PyTubeSearch
This example demonstrates how to process multiple queries efficiently:
- Running multiple searches in sequence
- Aggregating results from different queries
- Handling errors in batch operations
- Performance optimization for bulk operations
Usage:
python batch_processing.py
python batch_processing.py "python,javascript,rust,go"
"""
import sys
import time
from concurrent.futures import ThreadPoolExecutor, as_completed
from pytubesearch import PyTubeSearch
def sequential_batch_processing(queries: list):
"""Process multiple queries sequentially."""
print(f"🔄 Sequential batch processing for {len(queries)} queries")
print("-" * 50)
all_results = {}
total_start_time = time.time()
with PyTubeSearch() as client:
for i, query in enumerate(queries, 1):
print(f"📝 Processing query {i}/{len(queries)}: {query}")
try:
start_time = time.time()
results = client.search(query, limit=5)
end_time = time.time()
all_results[query] = {
"items": results.items,
"count": len(results.items),
"time": end_time - start_time,
"success": True,
}
print(f" ✅ Found {len(results.items)} items in {end_time - start_time:.2f}s")
# Show first result
if results.items:
first_item = results.items[0]
print(f" 📹 Top result: {first_item.title[:50]}...")
except Exception as e:
print(f" ❌ Failed: {e}")
all_results[query] = {
"items": [],
"count": 0,
"time": 0,
"success": False,
"error": str(e),
}
print()
total_end_time = time.time()
total_time = total_end_time - total_start_time
# Summary
print("📊 SEQUENTIAL PROCESSING SUMMARY:")
print(f" Total queries: {len(queries)}")
print(f" Successful: {sum(1 for r in all_results.values() if r['success'])}")
print(f" Failed: {sum(1 for r in all_results.values() if not r['success'])}")
print(f" Total time: {total_time:.2f}s")
print(f" Average per query: {total_time/len(queries):.2f}s")
total_items = sum(r["count"] for r in all_results.values())
print(f" Total items found: {total_items}")
return all_results
def parallel_batch_processing(queries: list, max_workers: int = 3):
"""Process multiple queries in parallel using threads."""
print(f"⚡ Parallel batch processing for {len(queries)} queries (max {max_workers} workers)")
print("-" * 50)
def search_query(query):
"""Search function for thread execution."""
try:
with PyTubeSearch() as client:
start_time = time.time()
results = client.search(query, limit=5)
end_time = time.time()
return {
"query": query,
"items": results.items,
"count": len(results.items),
"time": end_time - start_time,
"success": True,
}
except Exception as e:
return {
"query": query,
"items": [],
"count": 0,
"time": 0,
"success": False,
"error": str(e),
}
all_results = {}
total_start_time = time.time()
# Use ThreadPoolExecutor for parallel processing
with ThreadPoolExecutor(max_workers=max_workers) as executor:
# Submit all queries
future_to_query = {executor.submit(search_query, query): query for query in queries}
# Process completed futures
for future in as_completed(future_to_query):
query = future_to_query[future]
try:
result = future.result()
all_results[query] = result
if result["success"]:
print(f"✅ {query}: {result['count']} items in {result['time']:.2f}s")
if result["items"]:
first_item = result["items"][0]
print(f" 📹 Top: {first_item.title[:50]}...")
else:
print(f"❌ {query}: {result.get('error', 'Unknown error')}")
except Exception as e:
print(f"❌ {query}: Thread execution failed: {e}")
all_results[query] = {
"items": [],
"count": 0,
"time": 0,
"success": False,
"error": str(e),
}
total_end_time = time.time()
total_time = total_end_time - total_start_time
# Summary
print(f"\n📊 PARALLEL PROCESSING SUMMARY:")
print(f" Total queries: {len(queries)}")
print(f" Successful: {sum(1 for r in all_results.values() if r['success'])}")
print(f" Failed: {sum(1 for r in all_results.values() if not r['success'])}")
print(f" Total time: {total_time:.2f}s")
print(f" Average per query: {total_time/len(queries):.2f}s")
total_items = sum(r["count"] for r in all_results.values())
print(f" Total items found: {total_items}")
return all_results
def aggregate_batch_results(results_dict: dict):
"""Aggregate and analyze results from batch processing."""
print("📈 BATCH RESULTS AGGREGATION")
print("-" * 50)
# Collect all items
all_items = []
successful_queries = []
for query, result in results_dict.items():
if result["success"]:
all_items.extend(result["items"])
successful_queries.append(query)
if not all_items:
print("No items to aggregate")
return
print(f"📊 AGGREGATED STATISTICS:")
print(f" Total items across all queries: {len(all_items)}")
print(f" Successful queries: {len(successful_queries)}")
print()
# Content type analysis
content_types = {}
for item in all_items:
content_types[item.type] = content_types.get(item.type, 0) + 1
print("📋 CONTENT TYPE BREAKDOWN:")
for content_type, count in sorted(content_types.items()):
percentage = (count / len(all_items)) * 100
print(f" {content_type.capitalize()}s: {count} ({percentage:.1f}%)")
print()
# Channel analysis
channels = {}
for item in all_items:
if item.channel_title:
channels[item.channel_title] = channels.get(item.channel_title, 0) + 1
if channels:
print("📺 TOP CHANNELS (across all queries):")
top_channels = sorted(channels.items(), key=lambda x: x[1], reverse=True)[:10]
for channel, count in top_channels:
print(f" {channel}: {count} videos")
print()
# Live content analysis
live_items = [item for item in all_items if item.is_live]
print(f"🔴 LIVE CONTENT: {len(live_items)} items ({len(live_items)/len(all_items)*100:.1f}%)")
# Query-specific analysis
print("\n🔍 PER-QUERY BREAKDOWN:")
for query in successful_queries:
result = results_dict[query]
query_items = result["items"]
if query_items:
videos = sum(1 for item in query_items if item.type == "video")
channels = sum(1 for item in query_items if item.type == "channel")
live = sum(1 for item in query_items if item.is_live)
print(f" {query}:")
print(
f" Total: {len(query_items)}, Videos: {videos}, Channels: {channels}, Live: {live}"
)
def compare_processing_methods(queries: list):
"""Compare sequential vs parallel processing performance."""
print("⚖️ PROCESSING METHOD COMPARISON")
print("-" * 50)
print("🔄 Running sequential processing...")
seq_start = time.time()
seq_results = sequential_batch_processing(queries)
seq_time = time.time() - seq_start
print("\n" + "=" * 60 + "\n")
print("⚡ Running parallel processing...")
par_start = time.time()
par_results = parallel_batch_processing(queries, max_workers=3)
par_time = time.time() - par_start
print("\n" + "=" * 60 + "\n")
# Comparison
print("📊 PERFORMANCE COMPARISON:")
print(f" Sequential time: {seq_time:.2f}s")
print(f" Parallel time: {par_time:.2f}s")
if par_time > 0:
speedup = seq_time / par_time
print(f" Speedup: {speedup:.2f}x")
print(
f" Time saved: {seq_time - par_time:.2f}s ({(seq_time - par_time)/seq_time*100:.1f}%)"
)
# Results comparison
seq_total = sum(r["count"] for r in seq_results.values() if r["success"])
par_total = sum(r["count"] for r in par_results.values() if r["success"])
print(f" Sequential total items: {seq_total}")
print(f" Parallel total items: {par_total}")
print(f" Results consistency: {'✅ SAME' if seq_total == par_total else '⚠️ DIFFERENT'}")
def batch_error_handling_example(queries: list):
"""Demonstrate error handling in batch processing."""
print("🛡️ BATCH ERROR HANDLING EXAMPLE")
print("-" * 50)
# Add some intentionally problematic queries
test_queries = queries + ["", "a" * 1000, "invalid@#$%query"]
results = {}
error_count = 0
with PyTubeSearch() as client:
for i, query in enumerate(test_queries, 1):
print(
f"📝 Query {i}/{len(test_queries)}: {query[:30]}{'...' if len(query) > 30 else ''}"
)
try:
# Add timeout and retry logic
results[query] = client.search(query, limit=3)
print(f" ✅ Success: {len(results[query].items)} items")
except Exception as e:
error_count += 1
error_type = type(e).__name__
print(f" ❌ {error_type}: {str(e)[:50]}...")
# Log error but continue processing
results[query] = None
print(f"\n📊 ERROR HANDLING SUMMARY:")
print(f" Total queries: {len(test_queries)}")
print(f" Successful: {len(test_queries) - error_count}")
print(f" Failed: {error_count}")
print(f" Success rate: {(len(test_queries) - error_count)/len(test_queries)*100:.1f}%")
def main():
"""Main function to run batch processing examples."""
print("📦 PyTubeSearch - Batch Processing Examples")
print("=" * 60)
# Get queries from command line or use defaults
if len(sys.argv) > 1:
queries = [q.strip() for q in sys.argv[1].split(",")]
else:
queries = ["python programming", "machine learning", "web development", "data science"]
print(f"🎯 Processing queries: {', '.join(queries)}")
print("\n" + "=" * 60 + "\n")
# Run sequential processing
seq_results = sequential_batch_processing(queries)
print("\n" + "=" * 60 + "\n")
# Aggregate results
aggregate_batch_results(seq_results)
print("\n" + "=" * 60 + "\n")
# Compare processing methods (if we have multiple queries)
if len(queries) > 1:
compare_processing_methods(queries[:3]) # Limit to 3 for comparison
print("\n" + "=" * 60 + "\n")
# Error handling example
batch_error_handling_example(queries[:2]) # Use subset for error demo
print("\n✅ All batch processing examples completed!")
print("\n💡 Next steps:")
print(" - Try error_handling.py for comprehensive error handling")
print(" - Try data_export.py for saving batch results")
print(" - Try async_usage.py for async processing patterns")
if __name__ == "__main__":
main()