py-youtube-search 0.2.3__py3-none-any.whl → 0.2.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.
- py_youtube_search/__init__.py +80 -0
- {py_youtube_search-0.2.3.dist-info → py_youtube_search-0.2.4.dist-info}/METADATA +107 -10
- py_youtube_search-0.2.4.dist-info/RECORD +5 -0
- py_youtube_search-0.2.3.dist-info/RECORD +0 -5
- {py_youtube_search-0.2.3.dist-info → py_youtube_search-0.2.4.dist-info}/WHEEL +0 -0
- {py_youtube_search-0.2.3.dist-info → py_youtube_search-0.2.4.dist-info}/top_level.txt +0 -0
py_youtube_search/__init__.py
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import re
|
|
2
2
|
import aiohttp
|
|
3
3
|
import asyncio
|
|
4
|
+
import requests
|
|
5
|
+
|
|
4
6
|
|
|
5
7
|
# Filter constants accessible to the user
|
|
6
8
|
class Filters:
|
|
@@ -78,6 +80,68 @@ class YouTubeSearch:
|
|
|
78
80
|
|
|
79
81
|
return results
|
|
80
82
|
|
|
83
|
+
|
|
84
|
+
class YouTubeSearchSync:
|
|
85
|
+
def __init__(self):
|
|
86
|
+
"""
|
|
87
|
+
Initialize the YouTubeSearchSync client.
|
|
88
|
+
The client is stateless; parameters are passed to the search method.
|
|
89
|
+
"""
|
|
90
|
+
self.base_url = "https://www.youtube.com/results"
|
|
91
|
+
self.headers = {
|
|
92
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
def _fetch_source(self, query: str, sp: str = None):
|
|
96
|
+
params = {"search_query": query.replace(" ", "+")}
|
|
97
|
+
if sp:
|
|
98
|
+
params["sp"] = sp
|
|
99
|
+
|
|
100
|
+
response = requests.get(self.base_url, params=params, headers=self.headers)
|
|
101
|
+
return response.text
|
|
102
|
+
|
|
103
|
+
def search(self, query: str, sp: str = None, limit: int = 15):
|
|
104
|
+
"""
|
|
105
|
+
Synchronously fetches and parses search results for the given query.
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
query (str): The search query.
|
|
109
|
+
sp (str, optional): The filter string (use Filters class). Defaults to None.
|
|
110
|
+
limit (int, optional): Max number of results. Defaults to 15.
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
list: A list of dictionaries containing video details.
|
|
114
|
+
"""
|
|
115
|
+
source = self._fetch_source(query, sp)
|
|
116
|
+
|
|
117
|
+
# Regex to capture distinct JSON fields for ID, Title, Duration, and Views.
|
|
118
|
+
pattern = (
|
|
119
|
+
r'\"videoRenderer\":\{'
|
|
120
|
+
r'.+?\"videoId\":\"(?P<id>\S{11})\"'
|
|
121
|
+
r'.+?\"title\":\{\"runs\":\[\{\"text\":\"(?P<title>.+?)\"\}\]'
|
|
122
|
+
r'.+?\"lengthText\":\{.*?\"simpleText\":\"(?P<duration>.+?)\"\}'
|
|
123
|
+
r'.+?\"viewCountText\":\{\"simpleText\":\"(?P<views>.+?)\"\}'
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
matches = re.finditer(pattern, source)
|
|
127
|
+
|
|
128
|
+
results = []
|
|
129
|
+
for match in matches:
|
|
130
|
+
if len(results) >= limit:
|
|
131
|
+
break
|
|
132
|
+
|
|
133
|
+
data = match.groupdict()
|
|
134
|
+
results.append({
|
|
135
|
+
"id": data["id"],
|
|
136
|
+
"title": data["title"],
|
|
137
|
+
"duration": data["duration"],
|
|
138
|
+
"views": data["views"],
|
|
139
|
+
"url_suffix": f"/watch?v={data['id']}"
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
return results
|
|
143
|
+
|
|
144
|
+
|
|
81
145
|
# --- Usage Example (Async) ---
|
|
82
146
|
# import asyncio
|
|
83
147
|
# async def main():
|
|
@@ -94,3 +158,19 @@ class YouTubeSearch:
|
|
|
94
158
|
# print(f"Found {len(videos2)} videos")
|
|
95
159
|
#
|
|
96
160
|
# asyncio.run(main())
|
|
161
|
+
|
|
162
|
+
# --- Usage Example (Sync) ---
|
|
163
|
+
# def main():
|
|
164
|
+
# yt = YouTubeSearchSync()
|
|
165
|
+
#
|
|
166
|
+
# # Search 1: Long videos about LangGraph
|
|
167
|
+
# print("Searching LangGraph...")
|
|
168
|
+
# videos1 = yt.search("LangGraph", sp=Filters.long_this_week, limit=5)
|
|
169
|
+
# print(f"Found {len(videos1)} videos")
|
|
170
|
+
#
|
|
171
|
+
# # Search 2: Short Python tutorials (reusing the same instance)
|
|
172
|
+
# print("Searching Python...")
|
|
173
|
+
# videos2 = yt.search("Python", sp=Filters.medium_today, limit=3)
|
|
174
|
+
# print(f"Found {len(videos2)} videos")
|
|
175
|
+
#
|
|
176
|
+
# main()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: py-youtube-search
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4
|
|
4
4
|
Summary: A lightweight, regex-based YouTube search library without API keys.
|
|
5
5
|
Home-page: https://github.com/VishvaRam/py-youtube-search
|
|
6
6
|
Author: VishvaRam
|
|
@@ -12,6 +12,7 @@ Classifier: Operating System :: OS Independent
|
|
|
12
12
|
Requires-Python: >=3.6
|
|
13
13
|
Description-Content-Type: text/markdown
|
|
14
14
|
Requires-Dist: aiohttp>=3.8.0
|
|
15
|
+
Requires-Dist: requests>=2.25.0
|
|
15
16
|
Dynamic: author
|
|
16
17
|
Dynamic: author-email
|
|
17
18
|
Dynamic: classifier
|
|
@@ -25,12 +26,12 @@ Dynamic: summary
|
|
|
25
26
|
|
|
26
27
|
# py-youtube-search
|
|
27
28
|
|
|
28
|
-
A lightweight
|
|
29
|
-
It scrapes search results using
|
|
29
|
+
A lightweight Python library to search YouTube videos programmatically without an API key.
|
|
30
|
+
It scrapes search results using regex, making it fast, robust, and perfect for both synchronous and asynchronous applications.
|
|
30
31
|
|
|
31
32
|
## Features
|
|
32
33
|
|
|
33
|
-
- **Async Support**:
|
|
34
|
+
- **Async & Sync Support**: Choose between fully asynchronous (`aiohttp`) or synchronous (`requests`) implementations.
|
|
34
35
|
- **Reusable Client**: Create a single instance and run multiple searches with different configurations.
|
|
35
36
|
- **No API Key Required**: Search YouTube directly without setting up Google Cloud projects.
|
|
36
37
|
- **Advanced Filtering**: Built-in support for duration (Medium 3-20m, Long >20m) and upload date filters.
|
|
@@ -44,15 +45,15 @@ pip install py-youtube-search
|
|
|
44
45
|
|
|
45
46
|
## Quick Start
|
|
46
47
|
|
|
47
|
-
### 1.
|
|
48
|
-
|
|
48
|
+
### 1. Async Search (Recommended for Concurrent Operations)
|
|
49
|
+
Perfect for FastAPI, async applications, or when running multiple searches concurrently.
|
|
49
50
|
|
|
50
51
|
```python
|
|
51
52
|
import asyncio
|
|
52
53
|
from py_youtube_search import YouTubeSearch
|
|
53
54
|
|
|
54
55
|
async def main():
|
|
55
|
-
# 1. Initialize the client (reusable)
|
|
56
|
+
# 1. Initialize the async client (reusable)
|
|
56
57
|
yt = YouTubeSearch()
|
|
57
58
|
|
|
58
59
|
# 2. Run a search
|
|
@@ -68,7 +69,30 @@ if __name__ == "__main__":
|
|
|
68
69
|
asyncio.run(main())
|
|
69
70
|
```
|
|
70
71
|
|
|
71
|
-
### 2.
|
|
72
|
+
### 2. Sync Search (Simple Scripts & Notebooks)
|
|
73
|
+
Perfect for simple scripts, Jupyter notebooks, or synchronous applications.
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
from py_youtube_search import YouTubeSearchSync
|
|
77
|
+
|
|
78
|
+
def main():
|
|
79
|
+
# 1. Initialize the sync client (reusable)
|
|
80
|
+
yt = YouTubeSearchSync()
|
|
81
|
+
|
|
82
|
+
# 2. Run a search
|
|
83
|
+
videos = yt.search("Python async tutorials", limit=5)
|
|
84
|
+
|
|
85
|
+
for v in videos:
|
|
86
|
+
print(f"Title: {v['title']}")
|
|
87
|
+
print(f"Duration: {v['duration']}")
|
|
88
|
+
print(f"Views: {v['views']}")
|
|
89
|
+
print(f"Link: https://www.youtube.com/watch?v={v['id']}\n")
|
|
90
|
+
|
|
91
|
+
if __name__ == "__main__":
|
|
92
|
+
main()
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### 3. Advanced Search with Filters (Async)
|
|
72
96
|
Search for specific content, like long-form videos (>20m) uploaded this week.
|
|
73
97
|
|
|
74
98
|
```python
|
|
@@ -96,6 +120,32 @@ if __name__ == "__main__":
|
|
|
96
120
|
asyncio.run(main())
|
|
97
121
|
```
|
|
98
122
|
|
|
123
|
+
### 4. Advanced Search with Filters (Sync)
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
from py_youtube_search import YouTubeSearchSync, Filters
|
|
127
|
+
|
|
128
|
+
def main():
|
|
129
|
+
yt = YouTubeSearchSync()
|
|
130
|
+
|
|
131
|
+
# Search 1: Long videos about LangGraph
|
|
132
|
+
print("Searching for LangGraph...")
|
|
133
|
+
videos = yt.search("LangGraph", sp=Filters.long_this_week, limit=3)
|
|
134
|
+
|
|
135
|
+
for v in videos:
|
|
136
|
+
print(f"🎥 {v['title']} | ⏱ {v['duration']} | 👁 {v['views']}")
|
|
137
|
+
|
|
138
|
+
# Search 2: Reusing the same client for a different query
|
|
139
|
+
print("\nSearching for Python...")
|
|
140
|
+
videos_py = yt.search("Python 3.12", sp=Filters.medium_today, limit=3)
|
|
141
|
+
|
|
142
|
+
for v in videos_py:
|
|
143
|
+
print(f"🐍 {v['title']}")
|
|
144
|
+
|
|
145
|
+
if __name__ == "__main__":
|
|
146
|
+
main()
|
|
147
|
+
```
|
|
148
|
+
|
|
99
149
|
## Available Filters
|
|
100
150
|
|
|
101
151
|
Pass these constants into the `sp` parameter of the `search()` method.
|
|
@@ -118,7 +168,7 @@ Pass these constants into the `sp` parameter of the `search()` method.
|
|
|
118
168
|
|
|
119
169
|
## Data Structure
|
|
120
170
|
|
|
121
|
-
|
|
171
|
+
Both `.search()` methods return a list of dictionaries:
|
|
122
172
|
|
|
123
173
|
```json
|
|
124
174
|
[
|
|
@@ -132,8 +182,55 @@ The `.search()` method returns a list of dictionaries:
|
|
|
132
182
|
]
|
|
133
183
|
```
|
|
134
184
|
|
|
185
|
+
## API Reference
|
|
186
|
+
|
|
187
|
+
### `YouTubeSearch` (Async)
|
|
188
|
+
|
|
189
|
+
```python
|
|
190
|
+
async def search(query: str, sp: str = None, limit: int = 15) -> list
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
**Parameters:**
|
|
194
|
+
- `query` (str): The search query
|
|
195
|
+
- `sp` (str, optional): Filter string from `Filters` class
|
|
196
|
+
- `limit` (int, optional): Maximum number of results (default: 15)
|
|
197
|
+
|
|
198
|
+
**Returns:** List of video dictionaries
|
|
199
|
+
|
|
200
|
+
### `YouTubeSearchSync` (Sync)
|
|
201
|
+
|
|
202
|
+
```python
|
|
203
|
+
def search(query: str, sp: str = None, limit: int = 15) -> list
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
**Parameters:**
|
|
207
|
+
- `query` (str): The search query
|
|
208
|
+
- `sp` (str, optional): Filter string from `Filters` class
|
|
209
|
+
- `limit` (int, optional): Maximum number of results (default: 15)
|
|
210
|
+
|
|
211
|
+
**Returns:** List of video dictionaries
|
|
212
|
+
|
|
213
|
+
## When to Use Async vs Sync?
|
|
214
|
+
|
|
215
|
+
### Use `YouTubeSearch` (Async) when:
|
|
216
|
+
- Building FastAPI, aiohttp, or other async web applications
|
|
217
|
+
- Running multiple searches concurrently
|
|
218
|
+
- Integrating with async frameworks or event loops
|
|
219
|
+
|
|
220
|
+
### Use `YouTubeSearchSync` (Sync) when:
|
|
221
|
+
- Writing simple scripts or automation tools
|
|
222
|
+
- Working in Jupyter notebooks or interactive environments
|
|
223
|
+
- Building synchronous applications (Flask, Django views, etc.)
|
|
224
|
+
|
|
135
225
|
## Dependencies
|
|
136
|
-
- `aiohttp` (for async
|
|
226
|
+
- `aiohttp>=3.8.0` (for async version)
|
|
227
|
+
- `requests>=2.25.0` (for sync version)
|
|
137
228
|
|
|
138
229
|
## License
|
|
139
230
|
MIT License. See LICENSE file for details.
|
|
231
|
+
|
|
232
|
+
## Contributing
|
|
233
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
234
|
+
|
|
235
|
+
## Issues
|
|
236
|
+
If you encounter any problems, please file an issue on GitHub.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
py_youtube_search/__init__.py,sha256=dUemIKQyFx1Gpc_-4iZyKDvYXMWJYXtK8a8p1CZEpGA,5999
|
|
2
|
+
py_youtube_search-0.2.4.dist-info/METADATA,sha256=pqGPEGid5FP5c0Pjv42N8TzM7wX84HOfpN7l4opcg3Q,6805
|
|
3
|
+
py_youtube_search-0.2.4.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
4
|
+
py_youtube_search-0.2.4.dist-info/top_level.txt,sha256=4EMqIznKjzwgAcB_78SKaMIzZXpIxWQ4SRLeakY3fzQ,18
|
|
5
|
+
py_youtube_search-0.2.4.dist-info/RECORD,,
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
py_youtube_search/__init__.py,sha256=izTjs9UNT9lxj_atAEOPGa4a2kn-_URj7SkGMfDwNj4,3292
|
|
2
|
-
py_youtube_search-0.2.3.dist-info/METADATA,sha256=pSiIS7-NPmQ5X7E0jfHyV8K4pDiHi8ZgXQrQa3f3iRE,4038
|
|
3
|
-
py_youtube_search-0.2.3.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
4
|
-
py_youtube_search-0.2.3.dist-info/top_level.txt,sha256=4EMqIznKjzwgAcB_78SKaMIzZXpIxWQ4SRLeakY3fzQ,18
|
|
5
|
-
py_youtube_search-0.2.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|