pytrends-modern 0.2.2__tar.gz → 0.2.3__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.
Files changed (33) hide show
  1. {pytrends_modern-0.2.2/pytrends_modern.egg-info → pytrends_modern-0.2.3}/PKG-INFO +31 -1
  2. {pytrends_modern-0.2.2 → pytrends_modern-0.2.3}/README.md +30 -0
  3. {pytrends_modern-0.2.2 → pytrends_modern-0.2.3}/examples/example_browser_mode.py +3 -3
  4. {pytrends_modern-0.2.2 → pytrends_modern-0.2.3}/pyproject.toml +1 -1
  5. {pytrends_modern-0.2.2 → pytrends_modern-0.2.3}/pytrends_modern/__init__.py +1 -1
  6. {pytrends_modern-0.2.2 → pytrends_modern-0.2.3}/pytrends_modern/browser_config_camoufox.py +17 -1
  7. {pytrends_modern-0.2.2 → pytrends_modern-0.2.3}/pytrends_modern/request.py +48 -3
  8. {pytrends_modern-0.2.2 → pytrends_modern-0.2.3}/pytrends_modern/request_async.py +24 -3
  9. {pytrends_modern-0.2.2 → pytrends_modern-0.2.3/pytrends_modern.egg-info}/PKG-INFO +31 -1
  10. {pytrends_modern-0.2.2 → pytrends_modern-0.2.3}/LICENSE +0 -0
  11. {pytrends_modern-0.2.2 → pytrends_modern-0.2.3}/MANIFEST.in +0 -0
  12. {pytrends_modern-0.2.2 → pytrends_modern-0.2.3}/examples/advanced_usage.py +0 -0
  13. {pytrends_modern-0.2.2 → pytrends_modern-0.2.3}/examples/basic_usage.py +0 -0
  14. {pytrends_modern-0.2.2 → pytrends_modern-0.2.3}/examples/example_docker_usage.py +0 -0
  15. {pytrends_modern-0.2.2 → pytrends_modern-0.2.3}/examples/test_async_integration.py +0 -0
  16. {pytrends_modern-0.2.2 → pytrends_modern-0.2.3}/pytrends_modern/browser_config.py +0 -0
  17. {pytrends_modern-0.2.2 → pytrends_modern-0.2.3}/pytrends_modern/camoufox_setup.py +0 -0
  18. {pytrends_modern-0.2.2 → pytrends_modern-0.2.3}/pytrends_modern/cli.py +0 -0
  19. {pytrends_modern-0.2.2 → pytrends_modern-0.2.3}/pytrends_modern/config.py +0 -0
  20. {pytrends_modern-0.2.2 → pytrends_modern-0.2.3}/pytrends_modern/exceptions.py +0 -0
  21. {pytrends_modern-0.2.2 → pytrends_modern-0.2.3}/pytrends_modern/proxy_extension.py +0 -0
  22. {pytrends_modern-0.2.2 → pytrends_modern-0.2.3}/pytrends_modern/py.typed +0 -0
  23. {pytrends_modern-0.2.2 → pytrends_modern-0.2.3}/pytrends_modern/rss.py +0 -0
  24. {pytrends_modern-0.2.2 → pytrends_modern-0.2.3}/pytrends_modern/scraper.py +0 -0
  25. {pytrends_modern-0.2.2 → pytrends_modern-0.2.3}/pytrends_modern/utils.py +0 -0
  26. {pytrends_modern-0.2.2 → pytrends_modern-0.2.3}/pytrends_modern.egg-info/SOURCES.txt +0 -0
  27. {pytrends_modern-0.2.2 → pytrends_modern-0.2.3}/pytrends_modern.egg-info/dependency_links.txt +0 -0
  28. {pytrends_modern-0.2.2 → pytrends_modern-0.2.3}/pytrends_modern.egg-info/entry_points.txt +0 -0
  29. {pytrends_modern-0.2.2 → pytrends_modern-0.2.3}/pytrends_modern.egg-info/requires.txt +0 -0
  30. {pytrends_modern-0.2.2 → pytrends_modern-0.2.3}/pytrends_modern.egg-info/top_level.txt +0 -0
  31. {pytrends_modern-0.2.2 → pytrends_modern-0.2.3}/setup.cfg +0 -0
  32. {pytrends_modern-0.2.2 → pytrends_modern-0.2.3}/tests/conftest.py +0 -0
  33. {pytrends_modern-0.2.2 → pytrends_modern-0.2.3}/tests/test_basic.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pytrends-modern
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Summary: Modern Google Trends API - Combining the best of pytrends, with RSS feeds, Selenium scraping, DrissionPage browser automation, and enhanced features
5
5
  Author: pytrends-modern contributors
6
6
  License: MIT
@@ -139,6 +139,36 @@ df = pytrends.interest_over_time()
139
139
  print(df.head())
140
140
  ```
141
141
 
142
+ **Avoiding 429 Rate Limits:**
143
+
144
+ If you're getting 429 errors even with browser mode, use these anti-rate-limit features:
145
+
146
+ ```python
147
+ import random
148
+ from pytrends_modern import TrendReq, BrowserConfig
149
+
150
+ # Add delays + rotate OS fingerprint
151
+ os_choice = random.choice(['windows', 'macos', 'linux'])
152
+ config = BrowserConfig(
153
+ headless=False,
154
+ min_delay=3.0, # Min delay between requests (seconds)
155
+ max_delay=7.0, # Max delay between requests
156
+ persistent_context=True, # Keep Google login
157
+ os=os_choice, # Rotate OS fingerprint
158
+ humanize=True
159
+ )
160
+
161
+ pytrends = TrendReq(browser_config=config)
162
+ # Delays are automatically added before each request
163
+ ```
164
+
165
+ **Anti-Rate-Limit Options:**
166
+ - `min_delay` / `max_delay` - Random delay between requests (default: 2-5s)
167
+ - `os` - Rotate between 'windows', 'macos', 'linux' for different fingerprints
168
+ - `persistent_context=False` - Fresh profile each time (no cookies)
169
+ - `proxy_server` - Use proxy to rotate IPs
170
+ - `humanize=True` - Human-like cursor movements (enabled by default)
171
+
142
172
  **Browser Mode Limitations:**
143
173
  - ⚠️ Only 1 keyword at a time (no comparisons)
144
174
  - ⚠️ Only 'today 1-m' timeframe
@@ -89,6 +89,36 @@ df = pytrends.interest_over_time()
89
89
  print(df.head())
90
90
  ```
91
91
 
92
+ **Avoiding 429 Rate Limits:**
93
+
94
+ If you're getting 429 errors even with browser mode, use these anti-rate-limit features:
95
+
96
+ ```python
97
+ import random
98
+ from pytrends_modern import TrendReq, BrowserConfig
99
+
100
+ # Add delays + rotate OS fingerprint
101
+ os_choice = random.choice(['windows', 'macos', 'linux'])
102
+ config = BrowserConfig(
103
+ headless=False,
104
+ min_delay=3.0, # Min delay between requests (seconds)
105
+ max_delay=7.0, # Max delay between requests
106
+ persistent_context=True, # Keep Google login
107
+ os=os_choice, # Rotate OS fingerprint
108
+ humanize=True
109
+ )
110
+
111
+ pytrends = TrendReq(browser_config=config)
112
+ # Delays are automatically added before each request
113
+ ```
114
+
115
+ **Anti-Rate-Limit Options:**
116
+ - `min_delay` / `max_delay` - Random delay between requests (default: 2-5s)
117
+ - `os` - Rotate between 'windows', 'macos', 'linux' for different fingerprints
118
+ - `persistent_context=False` - Fresh profile each time (no cookies)
119
+ - `proxy_server` - Use proxy to rotate IPs
120
+ - `humanize=True` - Human-like cursor movements (enabled by default)
121
+
92
122
  **Browser Mode Limitations:**
93
123
  - ⚠️ Only 1 keyword at a time (no comparisons)
94
124
  - ⚠️ Only 'today 1-m' timeframe
@@ -35,15 +35,15 @@ from pytrends_modern import TrendReq, BrowserConfig
35
35
  config = BrowserConfig(
36
36
  headless=False, # Set to True for headless mode
37
37
  humanize=True, # Human-like cursor movements
38
- os='linux', # or 'windows', 'macos'
39
- geoip=True # Auto-detect location from IP
38
+ os='windows', # or 'windows', 'macos'
39
+ geoip=False # Auto-detect location from IP
40
40
  )
41
41
 
42
42
  # Initialize with browser mode
43
43
  pytrends = TrendReq(browser_config=config)
44
44
 
45
45
  # Test with a keyword
46
- keyword = "Artificial Intelligence"
46
+ keyword = "Brainrot"
47
47
  print(f"\n🔍 Fetching data for: {keyword}")
48
48
  print("⏳ Browser will open briefly...")
49
49
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "pytrends-modern"
7
- version = "0.2.2"
7
+ version = "0.2.3"
8
8
  description = "Modern Google Trends API - Combining the best of pytrends, with RSS feeds, Selenium scraping, DrissionPage browser automation, and enhanced features"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -2,7 +2,7 @@
2
2
  pytrends-modern: Modern Google Trends API
3
3
  """
4
4
 
5
- __version__ = "0.2.2"
5
+ __version__ = "0.2.3"
6
6
  __author__ = "pytrends-modern contributors"
7
7
  __license__ = "MIT"
8
8
 
@@ -1,6 +1,6 @@
1
1
  """Browser configuration for Camoufox automation"""
2
2
 
3
- from typing import Optional, Union
3
+ from typing import Optional, Union, Dict, Any
4
4
  import os as os_module
5
5
 
6
6
 
@@ -29,6 +29,12 @@ class BrowserConfig:
29
29
  humanize: Enable human-like cursor movement (default: True)
30
30
  os: Operating system for fingerprint ('windows', 'macos', 'linux')
31
31
  geoip: Auto-detect geolocation from proxy IP (default: True if proxy)
32
+ rotate_fingerprint: Generate random fingerprint for each session (default: True)
33
+ Set to False to use persistent fingerprint
34
+ min_delay: Minimum delay between requests in seconds (default: 2)
35
+ max_delay: Maximum delay between requests in seconds (default: 5)
36
+ persistent_context: Keep browser profile between sessions (default: True)
37
+ Set to False to use fresh profile each time (helps avoid 429)
32
38
 
33
39
  Example:
34
40
  >>> from pytrends_modern import TrendReq, BrowserConfig
@@ -57,6 +63,11 @@ class BrowserConfig:
57
63
  humanize: bool = True,
58
64
  os: str = 'linux',
59
65
  geoip: bool = True,
66
+ rotate_fingerprint: bool = True,
67
+ min_delay: float = 2.0,
68
+ max_delay: float = 5.0,
69
+ persistent_context: bool = True,
70
+ custom_config: Optional[Dict[str, Any]] = None,
60
71
  ):
61
72
  self.headless = headless
62
73
  self.proxy_server = proxy_server
@@ -66,4 +77,9 @@ class BrowserConfig:
66
77
  self.humanize = humanize
67
78
  self.os = os
68
79
  self.geoip = geoip if proxy_server else False
80
+ self.rotate_fingerprint = rotate_fingerprint
81
+ self.min_delay = min_delay
82
+ self.max_delay = max_delay
83
+ self.persistent_context = persistent_context
84
+ self.custom_config = custom_config or {}
69
85
 
@@ -170,6 +170,44 @@ class TrendReq:
170
170
  """Get a random user agent"""
171
171
  return random.choice(USER_AGENTS) if self.rotate_user_agent else USER_AGENTS[0]
172
172
 
173
+ def _browserforge_to_camoufox(self, fingerprint) -> Dict:
174
+ """Convert BrowserForge fingerprint to Camoufox config"""
175
+ config = {}
176
+
177
+ # Navigator properties
178
+ if hasattr(fingerprint, 'navigator'):
179
+ nav = fingerprint.navigator
180
+ if hasattr(nav, 'userAgent'):
181
+ config['navigator.userAgent'] = nav.userAgent
182
+ if hasattr(nav, 'language'):
183
+ config['navigator.language'] = nav.language
184
+ if hasattr(nav, 'hardwareConcurrency'):
185
+ config['navigator.hardwareConcurrency'] = nav.hardwareConcurrency
186
+ if hasattr(nav, 'maxTouchPoints'):
187
+ config['navigator.maxTouchPoints'] = nav.maxTouchPoints
188
+
189
+ # Screen properties
190
+ if hasattr(fingerprint, 'screen'):
191
+ scr = fingerprint.screen
192
+ if hasattr(scr, 'width'):
193
+ config['screen.width'] = scr.width
194
+ if hasattr(scr, 'height'):
195
+ config['screen.height'] = scr.height
196
+ if hasattr(scr, 'availWidth'):
197
+ config['screen.availWidth'] = scr.availWidth
198
+ if hasattr(scr, 'availHeight'):
199
+ config['screen.availHeight'] = scr.availHeight
200
+
201
+ return config
202
+
203
+ def _add_request_delay(self) -> None:
204
+ """Add random delay between requests to avoid rate limiting"""
205
+ if hasattr(self.browser_config, 'min_delay') and hasattr(self.browser_config, 'max_delay'):
206
+ import time
207
+ import random
208
+ delay = random.uniform(self.browser_config.min_delay, self.browser_config.max_delay)
209
+ time.sleep(delay)
210
+
173
211
  def _init_camoufox(self) -> None:
174
212
  """Initialize Camoufox browser with persistent context"""
175
213
  try:
@@ -212,15 +250,19 @@ class TrendReq:
212
250
 
213
251
  # Initialize Camoufox with persistent context
214
252
  try:
253
+ # Note: Camoufox automatically generates BrowserForge fingerprints
254
+ # based on the 'os' parameter. No need to manually pass fingerprints.
255
+
215
256
  # Camoufox() returns a context manager, we need to use __enter__() to get the context
216
257
  camoufox_manager = Camoufox(
217
- persistent_context=True,
218
- user_data_dir=user_data_dir,
258
+ persistent_context=self.browser_config.persistent_context,
259
+ user_data_dir=user_data_dir if self.browser_config.persistent_context else None,
219
260
  headless=self.browser_config.headless,
220
261
  humanize=self.browser_config.humanize if hasattr(self.browser_config, 'humanize') else True,
221
262
  os=self.browser_config.os if hasattr(self.browser_config, 'os') else 'linux',
222
263
  geoip=self.browser_config.geoip if hasattr(self.browser_config, 'geoip') else True,
223
- proxy=proxy_config
264
+ proxy=proxy_config,
265
+ config=self.browser_config.custom_config if self.browser_config.custom_config else None
224
266
  )
225
267
 
226
268
  # Enter the context manager to get the browser context
@@ -303,6 +345,9 @@ class TrendReq:
303
345
  if not self.browser_page:
304
346
  raise exceptions.BrowserError("Browser not initialized")
305
347
 
348
+ # Add random delay before request (anti-rate-limiting)
349
+ self._add_request_delay()
350
+
306
351
  # Clear cache
307
352
  self.browser_responses_cache.clear()
308
353
 
@@ -56,6 +56,20 @@ class AsyncTrendReq:
56
56
  """Async context manager exit"""
57
57
  await self._close_browser()
58
58
 
59
+ def _browserforge_to_camoufox(self, fingerprint) -> Dict:
60
+ """Convert BrowserForge fingerprint to Camoufox config"""
61
+ # Reuse the sync version's implementation
62
+ from pytrends_modern.request import TrendReq
63
+ return TrendReq._browserforge_to_camoufox(self, fingerprint)
64
+
65
+ async def _add_request_delay(self) -> None:
66
+ """Add random delay between requests to avoid rate limiting (async)"""
67
+ if hasattr(self.browser_config, 'min_delay') and hasattr(self.browser_config, 'max_delay'):
68
+ import asyncio
69
+ import random
70
+ delay = random.uniform(self.browser_config.min_delay, self.browser_config.max_delay)
71
+ await asyncio.sleep(delay)
72
+
59
73
  async def _init_camoufox(self) -> None:
60
74
  """Initialize Camoufox browser with persistent context (async)"""
61
75
  try:
@@ -98,15 +112,19 @@ class AsyncTrendReq:
98
112
 
99
113
  # Initialize AsyncCamoufox with persistent context
100
114
  try:
115
+ # Note: Camoufox automatically generates BrowserForge fingerprints
116
+ # based on the 'os' parameter. No need to manually pass fingerprints.
117
+
101
118
  # AsyncCamoufox() returns a context manager
102
119
  camoufox_manager = AsyncCamoufox(
103
- persistent_context=True,
104
- user_data_dir=user_data_dir,
120
+ persistent_context=self.browser_config.persistent_context,
121
+ user_data_dir=user_data_dir if self.browser_config.persistent_context else None,
105
122
  headless=self.browser_config.headless,
106
123
  humanize=self.browser_config.humanize if hasattr(self.browser_config, 'humanize') else True,
107
124
  os=self.browser_config.os if hasattr(self.browser_config, 'os') else 'linux',
108
125
  geoip=self.browser_config.geoip if hasattr(self.browser_config, 'geoip') else True,
109
- proxy=proxy_config
126
+ proxy=proxy_config,
127
+ config=self.browser_config.custom_config if self.browser_config.custom_config else None
110
128
  )
111
129
 
112
130
  # Enter the context manager to get the browser context
@@ -189,6 +207,9 @@ class AsyncTrendReq:
189
207
  if not self.browser_page:
190
208
  raise exceptions.BrowserError("Browser not initialized")
191
209
 
210
+ # Add random delay before request (anti-rate-limiting)
211
+ await self._add_request_delay()
212
+
192
213
  # Clear cache
193
214
  self.browser_responses_cache.clear()
194
215
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pytrends-modern
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Summary: Modern Google Trends API - Combining the best of pytrends, with RSS feeds, Selenium scraping, DrissionPage browser automation, and enhanced features
5
5
  Author: pytrends-modern contributors
6
6
  License: MIT
@@ -139,6 +139,36 @@ df = pytrends.interest_over_time()
139
139
  print(df.head())
140
140
  ```
141
141
 
142
+ **Avoiding 429 Rate Limits:**
143
+
144
+ If you're getting 429 errors even with browser mode, use these anti-rate-limit features:
145
+
146
+ ```python
147
+ import random
148
+ from pytrends_modern import TrendReq, BrowserConfig
149
+
150
+ # Add delays + rotate OS fingerprint
151
+ os_choice = random.choice(['windows', 'macos', 'linux'])
152
+ config = BrowserConfig(
153
+ headless=False,
154
+ min_delay=3.0, # Min delay between requests (seconds)
155
+ max_delay=7.0, # Max delay between requests
156
+ persistent_context=True, # Keep Google login
157
+ os=os_choice, # Rotate OS fingerprint
158
+ humanize=True
159
+ )
160
+
161
+ pytrends = TrendReq(browser_config=config)
162
+ # Delays are automatically added before each request
163
+ ```
164
+
165
+ **Anti-Rate-Limit Options:**
166
+ - `min_delay` / `max_delay` - Random delay between requests (default: 2-5s)
167
+ - `os` - Rotate between 'windows', 'macos', 'linux' for different fingerprints
168
+ - `persistent_context=False` - Fresh profile each time (no cookies)
169
+ - `proxy_server` - Use proxy to rotate IPs
170
+ - `humanize=True` - Human-like cursor movements (enabled by default)
171
+
142
172
  **Browser Mode Limitations:**
143
173
  - ⚠️ Only 1 keyword at a time (no comparisons)
144
174
  - ⚠️ Only 'today 1-m' timeframe
File without changes