py-youtube-search 0.2.2__tar.gz → 0.2.4__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: py-youtube-search
3
- Version: 0.2.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, asynchronous Python library to search YouTube videos programmatically without an API key.
29
- It scrapes search results using `aiohttp` and `re`, making it fast, robust, and perfect for high-performance applications.
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**: Fully asynchronous using `aiohttp` for non-blocking execution.
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. Basic Async Search
48
- Initialize the client once and run multiple searches.
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. Advanced Search with Filters
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
- The `.search()` method returns a list of dictionaries:
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 requests)
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,210 @@
1
+ # py-youtube-search
2
+
3
+ A lightweight Python library to search YouTube videos programmatically without an API key.
4
+ It scrapes search results using regex, making it fast, robust, and perfect for both synchronous and asynchronous applications.
5
+
6
+ ## Features
7
+
8
+ - **Async & Sync Support**: Choose between fully asynchronous (`aiohttp`) or synchronous (`requests`) implementations.
9
+ - **Reusable Client**: Create a single instance and run multiple searches with different configurations.
10
+ - **No API Key Required**: Search YouTube directly without setting up Google Cloud projects.
11
+ - **Advanced Filtering**: Built-in support for duration (Medium 3-20m, Long >20m) and upload date filters.
12
+ - **Rich Data Extraction**: Extracts Video ID, Title, Duration, and View Count using optimized regex.
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ pip install py-youtube-search
18
+ ```
19
+
20
+ ## Quick Start
21
+
22
+ ### 1. Async Search (Recommended for Concurrent Operations)
23
+ Perfect for FastAPI, async applications, or when running multiple searches concurrently.
24
+
25
+ ```python
26
+ import asyncio
27
+ from py_youtube_search import YouTubeSearch
28
+
29
+ async def main():
30
+ # 1. Initialize the async client (reusable)
31
+ yt = YouTubeSearch()
32
+
33
+ # 2. Run a search
34
+ videos = await yt.search("Python async tutorials", limit=5)
35
+
36
+ for v in videos:
37
+ print(f"Title: {v['title']}")
38
+ print(f"Duration: {v['duration']}")
39
+ print(f"Views: {v['views']}")
40
+ print(f"Link: https://www.youtube.com/watch?v={v['id']}\n")
41
+
42
+ if __name__ == "__main__":
43
+ asyncio.run(main())
44
+ ```
45
+
46
+ ### 2. Sync Search (Simple Scripts & Notebooks)
47
+ Perfect for simple scripts, Jupyter notebooks, or synchronous applications.
48
+
49
+ ```python
50
+ from py_youtube_search import YouTubeSearchSync
51
+
52
+ def main():
53
+ # 1. Initialize the sync client (reusable)
54
+ yt = YouTubeSearchSync()
55
+
56
+ # 2. Run a search
57
+ videos = yt.search("Python async tutorials", limit=5)
58
+
59
+ for v in videos:
60
+ print(f"Title: {v['title']}")
61
+ print(f"Duration: {v['duration']}")
62
+ print(f"Views: {v['views']}")
63
+ print(f"Link: https://www.youtube.com/watch?v={v['id']}\n")
64
+
65
+ if __name__ == "__main__":
66
+ main()
67
+ ```
68
+
69
+ ### 3. Advanced Search with Filters (Async)
70
+ Search for specific content, like long-form videos (>20m) uploaded this week.
71
+
72
+ ```python
73
+ import asyncio
74
+ from py_youtube_search import YouTubeSearch, Filters
75
+
76
+ async def main():
77
+ yt = YouTubeSearch()
78
+
79
+ # Search 1: Long videos about LangGraph
80
+ print("Searching for LangGraph...")
81
+ videos = await yt.search("LangGraph", sp=Filters.long_this_week, limit=3)
82
+
83
+ for v in videos:
84
+ print(f"🎥 {v['title']} | ⏱ {v['duration']} | 👁 {v['views']}")
85
+
86
+ # Search 2: Reusing the same client for a different query
87
+ print("\nSearching for Python...")
88
+ videos_py = await yt.search("Python 3.12", sp=Filters.medium_today, limit=3)
89
+
90
+ for v in videos_py:
91
+ print(f"🐍 {v['title']}")
92
+
93
+ if __name__ == "__main__":
94
+ asyncio.run(main())
95
+ ```
96
+
97
+ ### 4. Advanced Search with Filters (Sync)
98
+
99
+ ```python
100
+ from py_youtube_search import YouTubeSearchSync, Filters
101
+
102
+ def main():
103
+ yt = YouTubeSearchSync()
104
+
105
+ # Search 1: Long videos about LangGraph
106
+ print("Searching for LangGraph...")
107
+ videos = yt.search("LangGraph", sp=Filters.long_this_week, limit=3)
108
+
109
+ for v in videos:
110
+ print(f"🎥 {v['title']} | ⏱ {v['duration']} | 👁 {v['views']}")
111
+
112
+ # Search 2: Reusing the same client for a different query
113
+ print("\nSearching for Python...")
114
+ videos_py = yt.search("Python 3.12", sp=Filters.medium_today, limit=3)
115
+
116
+ for v in videos_py:
117
+ print(f"🐍 {v['title']}")
118
+
119
+ if __name__ == "__main__":
120
+ main()
121
+ ```
122
+
123
+ ## Available Filters
124
+
125
+ Pass these constants into the `sp` parameter of the `search()` method.
126
+
127
+ ### Duration: Medium (3 - 20 Minutes)
128
+ | Filter Attribute | Description |
129
+ | :--- | :--- |
130
+ | `Filters.medium_today` | Uploaded **Today** |
131
+ | `Filters.medium_this_week` | Uploaded **This Week** |
132
+ | `Filters.medium_this_month` | Uploaded **This Month** |
133
+ | `Filters.medium_this_year` | Uploaded **This Year** |
134
+
135
+ ### Duration: Long (Over 20 Minutes)
136
+ | Filter Attribute | Description |
137
+ | :--- | :--- |
138
+ | `Filters.long_today` | Uploaded **Today** |
139
+ | `Filters.long_this_week` | Uploaded **This Week** |
140
+ | `Filters.long_this_month` | Uploaded **This Month** |
141
+ | `Filters.long_this_year` | Uploaded **This Year** |
142
+
143
+ ## Data Structure
144
+
145
+ Both `.search()` methods return a list of dictionaries:
146
+
147
+ ```json
148
+ [
149
+ {
150
+ "id": "lDoYisPfcck",
151
+ "title": "Hack the planet! LangGraph AI HackBot Dev & Q/A",
152
+ "duration": "1:05:23",
153
+ "views": "1.2K views",
154
+ "url_suffix": "/watch?v=lDoYisPfcck"
155
+ }
156
+ ]
157
+ ```
158
+
159
+ ## API Reference
160
+
161
+ ### `YouTubeSearch` (Async)
162
+
163
+ ```python
164
+ async def search(query: str, sp: str = None, limit: int = 15) -> list
165
+ ```
166
+
167
+ **Parameters:**
168
+ - `query` (str): The search query
169
+ - `sp` (str, optional): Filter string from `Filters` class
170
+ - `limit` (int, optional): Maximum number of results (default: 15)
171
+
172
+ **Returns:** List of video dictionaries
173
+
174
+ ### `YouTubeSearchSync` (Sync)
175
+
176
+ ```python
177
+ def search(query: str, sp: str = None, limit: int = 15) -> list
178
+ ```
179
+
180
+ **Parameters:**
181
+ - `query` (str): The search query
182
+ - `sp` (str, optional): Filter string from `Filters` class
183
+ - `limit` (int, optional): Maximum number of results (default: 15)
184
+
185
+ **Returns:** List of video dictionaries
186
+
187
+ ## When to Use Async vs Sync?
188
+
189
+ ### Use `YouTubeSearch` (Async) when:
190
+ - Building FastAPI, aiohttp, or other async web applications
191
+ - Running multiple searches concurrently
192
+ - Integrating with async frameworks or event loops
193
+
194
+ ### Use `YouTubeSearchSync` (Sync) when:
195
+ - Writing simple scripts or automation tools
196
+ - Working in Jupyter notebooks or interactive environments
197
+ - Building synchronous applications (Flask, Django views, etc.)
198
+
199
+ ## Dependencies
200
+ - `aiohttp>=3.8.0` (for async version)
201
+ - `requests>=2.25.0` (for sync version)
202
+
203
+ ## License
204
+ MIT License. See LICENSE file for details.
205
+
206
+ ## Contributing
207
+ Contributions are welcome! Please feel free to submit a Pull Request.
208
+
209
+ ## Issues
210
+ If you encounter any problems, please file an issue on GitHub.
@@ -0,0 +1,176 @@
1
+ import re
2
+ import aiohttp
3
+ import asyncio
4
+ import requests
5
+
6
+
7
+ # Filter constants accessible to the user
8
+ class Filters:
9
+ # Duration: 3 - 20 Minutes (Medium)
10
+ medium_today = "EgYIAhABGAU="
11
+ medium_this_week = "EgQIAxGF"
12
+ medium_this_month = "EgYIBBABGAU="
13
+ medium_this_year = "EgYIBRABGAU="
14
+
15
+ # Duration: Over 20 Minutes (Long)
16
+ long_today = "EgYIAhABGAI="
17
+ long_this_week = "EgQIAxAB"
18
+ long_this_month = "EgYIBBABGAI="
19
+ long_this_year = "EgYIBRABGAI="
20
+
21
+
22
+ class YouTubeSearch:
23
+ def __init__(self):
24
+ """
25
+ Initialize the YouTubeSearch client.
26
+ The client is stateless; parameters are passed to the search method.
27
+ """
28
+ self.base_url = "https://www.youtube.com/results"
29
+ self.headers = {
30
+ "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"
31
+ }
32
+
33
+ async def _fetch_source(self, query: str, sp: str = None):
34
+ params = {"search_query": query.replace(" ", "+")}
35
+ if sp:
36
+ params["sp"] = sp
37
+
38
+ async with aiohttp.ClientSession() as session:
39
+ async with session.get(self.base_url, params=params, headers=self.headers) as response:
40
+ return await response.text()
41
+
42
+ async def search(self, query: str, sp: str = None, limit: int = 15):
43
+ """
44
+ Asynchronously fetches and parses search results for the given query.
45
+
46
+ Args:
47
+ query (str): The search query.
48
+ sp (str, optional): The filter string (use Filters class). Defaults to None.
49
+ limit (int, optional): Max number of results. Defaults to 15.
50
+
51
+ Returns:
52
+ list: A list of dictionaries containing video details.
53
+ """
54
+ source = await self._fetch_source(query, sp)
55
+
56
+ # Regex to capture distinct JSON fields for ID, Title, Duration, and Views.
57
+ pattern = (
58
+ r'\"videoRenderer\":\{'
59
+ r'.+?\"videoId\":\"(?P<id>\S{11})\"'
60
+ r'.+?\"title\":\{\"runs\":\[\{\"text\":\"(?P<title>.+?)\"\}\]'
61
+ r'.+?\"lengthText\":\{.*?\"simpleText\":\"(?P<duration>.+?)\"\}'
62
+ r'.+?\"viewCountText\":\{\"simpleText\":\"(?P<views>.+?)\"\}'
63
+ )
64
+
65
+ matches = re.finditer(pattern, source)
66
+
67
+ results = []
68
+ for match in matches:
69
+ if len(results) >= limit:
70
+ break
71
+
72
+ data = match.groupdict()
73
+ results.append({
74
+ "id": data["id"],
75
+ "title": data["title"],
76
+ "duration": data["duration"],
77
+ "views": data["views"],
78
+ "url_suffix": f"/watch?v={data['id']}"
79
+ })
80
+
81
+ return results
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
+
145
+ # --- Usage Example (Async) ---
146
+ # import asyncio
147
+ # async def main():
148
+ # yt = YouTubeSearch()
149
+ #
150
+ # # Search 1: Long videos about LangGraph
151
+ # print("Searching LangGraph...")
152
+ # videos1 = await yt.search("LangGraph", sp=Filters.long_this_week, limit=5)
153
+ # print(f"Found {len(videos1)} videos")
154
+ #
155
+ # # Search 2: Short Python tutorials (reusing the same instance)
156
+ # print("Searching Python...")
157
+ # videos2 = await yt.search("Python", sp=Filters.medium_today, limit=3)
158
+ # print(f"Found {len(videos2)} videos")
159
+ #
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.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, asynchronous Python library to search YouTube videos programmatically without an API key.
29
- It scrapes search results using `aiohttp` and `re`, making it fast, robust, and perfect for high-performance applications.
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**: Fully asynchronous using `aiohttp` for non-blocking execution.
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. Basic Async Search
48
- Initialize the client once and run multiple searches.
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. Advanced Search with Filters
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
- The `.search()` method returns a list of dictionaries:
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 requests)
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,2 @@
1
+ aiohttp>=3.8.0
2
+ requests>=2.25.0
@@ -5,10 +5,11 @@ with open("README.md", "r", encoding="utf-8") as fh:
5
5
 
6
6
  setup(
7
7
  name="py-youtube-search",
8
- version="0.2.2",
8
+ version="0.2.4",
9
9
  author="VishvaRam",
10
10
  install_requires=[
11
11
  "aiohttp>=3.8.0",
12
+ "requests>=2.25.0",
12
13
  ],
13
14
  author_email="murthyvishva@gmail.com",
14
15
  description="A lightweight, regex-based YouTube search library without API keys.",
@@ -1,114 +0,0 @@
1
- # py-youtube-search
2
-
3
- A lightweight, asynchronous Python library to search YouTube videos programmatically without an API key.
4
- It scrapes search results using `aiohttp` and `re`, making it fast, robust, and perfect for high-performance applications.
5
-
6
- ## Features
7
-
8
- - **Async Support**: Fully asynchronous using `aiohttp` for non-blocking execution.
9
- - **Reusable Client**: Create a single instance and run multiple searches with different configurations.
10
- - **No API Key Required**: Search YouTube directly without setting up Google Cloud projects.
11
- - **Advanced Filtering**: Built-in support for duration (Medium 3-20m, Long >20m) and upload date filters.
12
- - **Rich Data Extraction**: Extracts Video ID, Title, Duration, and View Count using optimized regex.
13
-
14
- ## Installation
15
-
16
- ```bash
17
- pip install py-youtube-search
18
- ```
19
-
20
- ## Quick Start
21
-
22
- ### 1. Basic Async Search
23
- Initialize the client once and run multiple searches.
24
-
25
- ```python
26
- import asyncio
27
- from py_youtube_search import YouTubeSearch
28
-
29
- async def main():
30
- # 1. Initialize the client (reusable)
31
- yt = YouTubeSearch()
32
-
33
- # 2. Run a search
34
- videos = await yt.search("Python async tutorials", limit=5)
35
-
36
- for v in videos:
37
- print(f"Title: {v['title']}")
38
- print(f"Duration: {v['duration']}")
39
- print(f"Views: {v['views']}")
40
- print(f"Link: https://www.youtube.com/watch?v={v['id']}\n")
41
-
42
- if __name__ == "__main__":
43
- asyncio.run(main())
44
- ```
45
-
46
- ### 2. Advanced Search with Filters
47
- Search for specific content, like long-form videos (>20m) uploaded this week.
48
-
49
- ```python
50
- import asyncio
51
- from py_youtube_search import YouTubeSearch, Filters
52
-
53
- async def main():
54
- yt = YouTubeSearch()
55
-
56
- # Search 1: Long videos about LangGraph
57
- print("Searching for LangGraph...")
58
- videos = await yt.search("LangGraph", sp=Filters.long_this_week, limit=3)
59
-
60
- for v in videos:
61
- print(f"🎥 {v['title']} | ⏱ {v['duration']} | 👁 {v['views']}")
62
-
63
- # Search 2: Reusing the same client for a different query
64
- print("\nSearching for Python...")
65
- videos_py = await yt.search("Python 3.12", sp=Filters.medium_today, limit=3)
66
-
67
- for v in videos_py:
68
- print(f"🐍 {v['title']}")
69
-
70
- if __name__ == "__main__":
71
- asyncio.run(main())
72
- ```
73
-
74
- ## Available Filters
75
-
76
- Pass these constants into the `sp` parameter of the `search()` method.
77
-
78
- ### Duration: Medium (3 - 20 Minutes)
79
- | Filter Attribute | Description |
80
- | :--- | :--- |
81
- | `Filters.medium_today` | Uploaded **Today** |
82
- | `Filters.medium_this_week` | Uploaded **This Week** |
83
- | `Filters.medium_this_month` | Uploaded **This Month** |
84
- | `Filters.medium_this_year` | Uploaded **This Year** |
85
-
86
- ### Duration: Long (Over 20 Minutes)
87
- | Filter Attribute | Description |
88
- | :--- | :--- |
89
- | `Filters.long_today` | Uploaded **Today** |
90
- | `Filters.long_this_week` | Uploaded **This Week** |
91
- | `Filters.long_this_month` | Uploaded **This Month** |
92
- | `Filters.long_this_year` | Uploaded **This Year** |
93
-
94
- ## Data Structure
95
-
96
- The `.search()` method returns a list of dictionaries:
97
-
98
- ```json
99
- [
100
- {
101
- "id": "lDoYisPfcck",
102
- "title": "Hack the planet! LangGraph AI HackBot Dev & Q/A",
103
- "duration": "1:05:23",
104
- "views": "1.2K views",
105
- "url_suffix": "/watch?v=lDoYisPfcck"
106
- }
107
- ]
108
- ```
109
-
110
- ## Dependencies
111
- - `aiohttp` (for async requests)
112
-
113
- ## License
114
- MIT License. See LICENSE file for details.
@@ -1,96 +0,0 @@
1
- import re
2
- import aiohttp
3
- import asyncio
4
-
5
- # Filter constants accessible to the user
6
- class Filters:
7
- # Duration: 3 - 20 Minutes (Medium)
8
- medium_today = "EgYIAhABGAU="
9
- medium_this_week = "EgQIAxGF"
10
- medium_this_month = "EgYIBBABGAU="
11
- medium_this_year = "EgYIBRABGAU="
12
-
13
- # Duration: Over 20 Minutes (Long)
14
- long_today = "EgYIAhABGAI="
15
- long_this_week = "EgQIAxAB"
16
- long_this_month = "EgYIBBABGAI="
17
- long_this_year = "EgYIBRABGAI="
18
-
19
-
20
- class YouTubeSearch:
21
- def __init__(self):
22
- """
23
- Initialize the YouTubeSearch client.
24
- The client is stateless; parameters are passed to the search method.
25
- """
26
- self.base_url = "https://www.youtube.com/results"
27
- self.headers = {
28
- "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"
29
- }
30
-
31
- async def _fetch_source(self, keywords: str, sp: str = None):
32
- params = {"search_query": keywords.replace(" ", "+")}
33
- if sp:
34
- params["sp"] = sp
35
-
36
- async with aiohttp.ClientSession() as session:
37
- async with session.get(self.base_url, params=params, headers=self.headers) as response:
38
- return await response.text()
39
-
40
- async def search(self, keywords: str, sp: str = None, limit: int = 15):
41
- """
42
- Asynchronously fetches and parses search results for the given keywords.
43
-
44
- Args:
45
- keywords (str): The search query.
46
- sp (str, optional): The filter string (use Filters class). Defaults to None.
47
- limit (int, optional): Max number of results. Defaults to 15.
48
-
49
- Returns:
50
- list: A list of dictionaries containing video details.
51
- """
52
- source = await self._fetch_source(keywords, sp)
53
-
54
- # Regex to capture distinct JSON fields for ID, Title, Duration, and Views.
55
- pattern = (
56
- r'\"videoRenderer\":\{'
57
- r'.+?\"videoId\":\"(?P<id>\S{11})\"'
58
- r'.+?\"title\":\{\"runs\":\[\{\"text\":\"(?P<title>.+?)\"\}\]'
59
- r'.+?\"lengthText\":\{.*?\"simpleText\":\"(?P<duration>.+?)\"\}'
60
- r'.+?\"viewCountText\":\{\"simpleText\":\"(?P<views>.+?)\"\}'
61
- )
62
-
63
- matches = re.finditer(pattern, source)
64
-
65
- results = []
66
- for match in matches:
67
- if len(results) >= limit:
68
- break
69
-
70
- data = match.groupdict()
71
- results.append({
72
- "id": data["id"],
73
- "title": data["title"],
74
- "duration": data["duration"],
75
- "views": data["views"],
76
- "url_suffix": f"/watch?v={data['id']}"
77
- })
78
-
79
- return results
80
-
81
- # --- Usage Example (Async) ---
82
- # import asyncio
83
- # async def main():
84
- # yt = YouTubeSearch()
85
- #
86
- # # Search 1: Long videos about LangGraph
87
- # print("Searching LangGraph...")
88
- # videos1 = await yt.search("LangGraph", sp=Filters.long_this_week, limit=5)
89
- # print(f"Found {len(videos1)} videos")
90
- #
91
- # # Search 2: Short Python tutorials (reusing the same instance)
92
- # print("Searching Python...")
93
- # videos2 = await yt.search("Python", sp=Filters.medium_today, limit=3)
94
- # print(f"Found {len(videos2)} videos")
95
- #
96
- # asyncio.run(main())
@@ -1 +0,0 @@
1
- aiohttp>=3.8.0