pytrends-modern 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.
- {pytrends_modern-0.2.2/pytrends_modern.egg-info → pytrends_modern-0.2.4}/PKG-INFO +31 -1
- {pytrends_modern-0.2.2 → pytrends_modern-0.2.4}/README.md +30 -0
- {pytrends_modern-0.2.2 → pytrends_modern-0.2.4}/examples/example_browser_mode.py +3 -3
- pytrends_modern-0.2.4/examples/test_timeframes.py +55 -0
- {pytrends_modern-0.2.2 → pytrends_modern-0.2.4}/pyproject.toml +1 -1
- {pytrends_modern-0.2.2 → pytrends_modern-0.2.4}/pytrends_modern/__init__.py +1 -1
- {pytrends_modern-0.2.2 → pytrends_modern-0.2.4}/pytrends_modern/browser_config_camoufox.py +22 -2
- {pytrends_modern-0.2.2 → pytrends_modern-0.2.4}/pytrends_modern/request.py +61 -6
- {pytrends_modern-0.2.2 → pytrends_modern-0.2.4}/pytrends_modern/request_async.py +36 -4
- {pytrends_modern-0.2.2 → pytrends_modern-0.2.4/pytrends_modern.egg-info}/PKG-INFO +31 -1
- {pytrends_modern-0.2.2 → pytrends_modern-0.2.4}/pytrends_modern.egg-info/SOURCES.txt +1 -0
- {pytrends_modern-0.2.2 → pytrends_modern-0.2.4}/LICENSE +0 -0
- {pytrends_modern-0.2.2 → pytrends_modern-0.2.4}/MANIFEST.in +0 -0
- {pytrends_modern-0.2.2 → pytrends_modern-0.2.4}/examples/advanced_usage.py +0 -0
- {pytrends_modern-0.2.2 → pytrends_modern-0.2.4}/examples/basic_usage.py +0 -0
- {pytrends_modern-0.2.2 → pytrends_modern-0.2.4}/examples/example_docker_usage.py +0 -0
- {pytrends_modern-0.2.2 → pytrends_modern-0.2.4}/examples/test_async_integration.py +0 -0
- {pytrends_modern-0.2.2 → pytrends_modern-0.2.4}/pytrends_modern/browser_config.py +0 -0
- {pytrends_modern-0.2.2 → pytrends_modern-0.2.4}/pytrends_modern/camoufox_setup.py +0 -0
- {pytrends_modern-0.2.2 → pytrends_modern-0.2.4}/pytrends_modern/cli.py +0 -0
- {pytrends_modern-0.2.2 → pytrends_modern-0.2.4}/pytrends_modern/config.py +0 -0
- {pytrends_modern-0.2.2 → pytrends_modern-0.2.4}/pytrends_modern/exceptions.py +0 -0
- {pytrends_modern-0.2.2 → pytrends_modern-0.2.4}/pytrends_modern/proxy_extension.py +0 -0
- {pytrends_modern-0.2.2 → pytrends_modern-0.2.4}/pytrends_modern/py.typed +0 -0
- {pytrends_modern-0.2.2 → pytrends_modern-0.2.4}/pytrends_modern/rss.py +0 -0
- {pytrends_modern-0.2.2 → pytrends_modern-0.2.4}/pytrends_modern/scraper.py +0 -0
- {pytrends_modern-0.2.2 → pytrends_modern-0.2.4}/pytrends_modern/utils.py +0 -0
- {pytrends_modern-0.2.2 → pytrends_modern-0.2.4}/pytrends_modern.egg-info/dependency_links.txt +0 -0
- {pytrends_modern-0.2.2 → pytrends_modern-0.2.4}/pytrends_modern.egg-info/entry_points.txt +0 -0
- {pytrends_modern-0.2.2 → pytrends_modern-0.2.4}/pytrends_modern.egg-info/requires.txt +0 -0
- {pytrends_modern-0.2.2 → pytrends_modern-0.2.4}/pytrends_modern.egg-info/top_level.txt +0 -0
- {pytrends_modern-0.2.2 → pytrends_modern-0.2.4}/setup.cfg +0 -0
- {pytrends_modern-0.2.2 → pytrends_modern-0.2.4}/tests/conftest.py +0 -0
- {pytrends_modern-0.2.2 → pytrends_modern-0.2.4}/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.
|
|
3
|
+
Version: 0.2.4
|
|
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='
|
|
39
|
-
geoip=
|
|
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 = "
|
|
46
|
+
keyword = "Brainrot"
|
|
47
47
|
print(f"\n🔍 Fetching data for: {keyword}")
|
|
48
48
|
print("⏳ Browser will open briefly...")
|
|
49
49
|
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Example: Using different timeframes with browser mode
|
|
4
|
+
|
|
5
|
+
Test both 'today 1-m' (default) and 'today 12-m' (past 12 months)
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from pytrends_modern import TrendReq, BrowserConfig
|
|
9
|
+
|
|
10
|
+
keyword = "Python"
|
|
11
|
+
|
|
12
|
+
print("=" * 70)
|
|
13
|
+
print("Testing Different Timeframes")
|
|
14
|
+
print("=" * 70)
|
|
15
|
+
|
|
16
|
+
# Test 1: Default timeframe (today 1-m)
|
|
17
|
+
print("\n📊 Test 1: Default timeframe (past month)")
|
|
18
|
+
config1 = BrowserConfig(
|
|
19
|
+
headless=False,
|
|
20
|
+
min_delay=2.0,
|
|
21
|
+
max_delay=4.0,
|
|
22
|
+
)
|
|
23
|
+
print(f"Timeframe: {config1.timeframe}")
|
|
24
|
+
|
|
25
|
+
pytrends1 = TrendReq(browser_config=config1)
|
|
26
|
+
pytrends1.kw_list = [keyword]
|
|
27
|
+
df1 = pytrends1.interest_over_time()
|
|
28
|
+
print(f"✓ Got {len(df1)} data points")
|
|
29
|
+
print(df1.head())
|
|
30
|
+
print(f"Date range: {df1.index.min()} to {df1.index.max()}")
|
|
31
|
+
|
|
32
|
+
# Close first browser before opening second
|
|
33
|
+
pytrends1._close_browser()
|
|
34
|
+
del pytrends1
|
|
35
|
+
|
|
36
|
+
# Test 2: Past 12 months
|
|
37
|
+
print("\n📊 Test 2: Past 12 months")
|
|
38
|
+
config2 = BrowserConfig(
|
|
39
|
+
headless=False,
|
|
40
|
+
min_delay=2.0,
|
|
41
|
+
max_delay=4.0,
|
|
42
|
+
timeframe='today 12-m'
|
|
43
|
+
)
|
|
44
|
+
print(f"Timeframe: {config2.timeframe}")
|
|
45
|
+
|
|
46
|
+
pytrends2 = TrendReq(browser_config=config2)
|
|
47
|
+
pytrends2.kw_list = [keyword]
|
|
48
|
+
df2 = pytrends2.interest_over_time()
|
|
49
|
+
print(f"✓ Got {len(df2)} data points")
|
|
50
|
+
print(df2.head())
|
|
51
|
+
print(f"Date range: {df2.index.min()} to {df2.index.max()}")
|
|
52
|
+
|
|
53
|
+
print("\n✅ Both timeframes working!")
|
|
54
|
+
print(f"\nPast month: {len(df1)} points")
|
|
55
|
+
print(f"Past 12 months: {len(df2)} points")
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pytrends-modern"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.4"
|
|
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"
|
|
@@ -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
|
|
|
@@ -12,7 +12,6 @@ class BrowserConfig:
|
|
|
12
12
|
|
|
13
13
|
⚠️ LIMITATIONS:
|
|
14
14
|
- Only 1 keyword supported (no comparison)
|
|
15
|
-
- Only 'today 1-m' timeframe supported
|
|
16
15
|
- Only WORLDWIDE geo supported (no geo filtering)
|
|
17
16
|
- Requires Google account login (first run)
|
|
18
17
|
|
|
@@ -29,6 +28,15 @@ class BrowserConfig:
|
|
|
29
28
|
humanize: Enable human-like cursor movement (default: True)
|
|
30
29
|
os: Operating system for fingerprint ('windows', 'macos', 'linux')
|
|
31
30
|
geoip: Auto-detect geolocation from proxy IP (default: True if proxy)
|
|
31
|
+
rotate_fingerprint: Generate random fingerprint for each session (default: True)
|
|
32
|
+
Set to False to use persistent fingerprint
|
|
33
|
+
min_delay: Minimum delay between requests in seconds (default: 2)
|
|
34
|
+
max_delay: Maximum delay between requests in seconds (default: 5)
|
|
35
|
+
persistent_context: Keep browser profile between sessions (default: True)
|
|
36
|
+
Set to False to use fresh profile each time (helps avoid 429)
|
|
37
|
+
timeframe: Time range for trends data (default: 'today 1-m')
|
|
38
|
+
- 'today 1-m': Past month
|
|
39
|
+
- 'today 12-m': Past 12 months
|
|
32
40
|
|
|
33
41
|
Example:
|
|
34
42
|
>>> from pytrends_modern import TrendReq, BrowserConfig
|
|
@@ -57,6 +65,12 @@ class BrowserConfig:
|
|
|
57
65
|
humanize: bool = True,
|
|
58
66
|
os: str = 'linux',
|
|
59
67
|
geoip: bool = True,
|
|
68
|
+
rotate_fingerprint: bool = True,
|
|
69
|
+
min_delay: float = 2.0,
|
|
70
|
+
max_delay: float = 5.0,
|
|
71
|
+
persistent_context: bool = True,
|
|
72
|
+
custom_config: Optional[Dict[str, Any]] = None,
|
|
73
|
+
timeframe: str = 'today 1-m',
|
|
60
74
|
):
|
|
61
75
|
self.headless = headless
|
|
62
76
|
self.proxy_server = proxy_server
|
|
@@ -66,4 +80,10 @@ class BrowserConfig:
|
|
|
66
80
|
self.humanize = humanize
|
|
67
81
|
self.os = os
|
|
68
82
|
self.geoip = geoip if proxy_server else False
|
|
83
|
+
self.rotate_fingerprint = rotate_fingerprint
|
|
84
|
+
self.min_delay = min_delay
|
|
85
|
+
self.max_delay = max_delay
|
|
86
|
+
self.persistent_context = persistent_context
|
|
87
|
+
self.custom_config = custom_config or {}
|
|
88
|
+
self.timeframe = timeframe
|
|
69
89
|
|
|
@@ -92,7 +92,6 @@ class TrendReq:
|
|
|
92
92
|
|
|
93
93
|
⚠️ LIMITATIONS when using browser_config:
|
|
94
94
|
- Only 1 keyword supported (no comparison)
|
|
95
|
-
- Only 'today 1-m' timeframe supported
|
|
96
95
|
- Only WORLDWIDE geo supported (no geo filtering)
|
|
97
96
|
- Requires Chrome/Chromium browser installed
|
|
98
97
|
"""
|
|
@@ -109,7 +108,7 @@ class TrendReq:
|
|
|
109
108
|
warnings.warn(
|
|
110
109
|
"⚠️ Camoufox browser mode is EXPERIMENTAL and has limitations:\n"
|
|
111
110
|
" - Only 1 keyword supported (no keyword comparison)\n"
|
|
112
|
-
" -
|
|
111
|
+
" - Timeframe: 'today 1-m' (default) or 'today 12-m'\n"
|
|
113
112
|
" - Only WORLDWIDE geo supported\n"
|
|
114
113
|
" - Requires Google account login (first run)\n"
|
|
115
114
|
" - Login session is saved for future runs",
|
|
@@ -170,6 +169,44 @@ class TrendReq:
|
|
|
170
169
|
"""Get a random user agent"""
|
|
171
170
|
return random.choice(USER_AGENTS) if self.rotate_user_agent else USER_AGENTS[0]
|
|
172
171
|
|
|
172
|
+
def _browserforge_to_camoufox(self, fingerprint) -> Dict:
|
|
173
|
+
"""Convert BrowserForge fingerprint to Camoufox config"""
|
|
174
|
+
config = {}
|
|
175
|
+
|
|
176
|
+
# Navigator properties
|
|
177
|
+
if hasattr(fingerprint, 'navigator'):
|
|
178
|
+
nav = fingerprint.navigator
|
|
179
|
+
if hasattr(nav, 'userAgent'):
|
|
180
|
+
config['navigator.userAgent'] = nav.userAgent
|
|
181
|
+
if hasattr(nav, 'language'):
|
|
182
|
+
config['navigator.language'] = nav.language
|
|
183
|
+
if hasattr(nav, 'hardwareConcurrency'):
|
|
184
|
+
config['navigator.hardwareConcurrency'] = nav.hardwareConcurrency
|
|
185
|
+
if hasattr(nav, 'maxTouchPoints'):
|
|
186
|
+
config['navigator.maxTouchPoints'] = nav.maxTouchPoints
|
|
187
|
+
|
|
188
|
+
# Screen properties
|
|
189
|
+
if hasattr(fingerprint, 'screen'):
|
|
190
|
+
scr = fingerprint.screen
|
|
191
|
+
if hasattr(scr, 'width'):
|
|
192
|
+
config['screen.width'] = scr.width
|
|
193
|
+
if hasattr(scr, 'height'):
|
|
194
|
+
config['screen.height'] = scr.height
|
|
195
|
+
if hasattr(scr, 'availWidth'):
|
|
196
|
+
config['screen.availWidth'] = scr.availWidth
|
|
197
|
+
if hasattr(scr, 'availHeight'):
|
|
198
|
+
config['screen.availHeight'] = scr.availHeight
|
|
199
|
+
|
|
200
|
+
return config
|
|
201
|
+
|
|
202
|
+
def _add_request_delay(self) -> None:
|
|
203
|
+
"""Add random delay between requests to avoid rate limiting"""
|
|
204
|
+
if hasattr(self.browser_config, 'min_delay') and hasattr(self.browser_config, 'max_delay'):
|
|
205
|
+
import time
|
|
206
|
+
import random
|
|
207
|
+
delay = random.uniform(self.browser_config.min_delay, self.browser_config.max_delay)
|
|
208
|
+
time.sleep(delay)
|
|
209
|
+
|
|
173
210
|
def _init_camoufox(self) -> None:
|
|
174
211
|
"""Initialize Camoufox browser with persistent context"""
|
|
175
212
|
try:
|
|
@@ -212,15 +249,19 @@ class TrendReq:
|
|
|
212
249
|
|
|
213
250
|
# Initialize Camoufox with persistent context
|
|
214
251
|
try:
|
|
252
|
+
# Note: Camoufox automatically generates BrowserForge fingerprints
|
|
253
|
+
# based on the 'os' parameter. No need to manually pass fingerprints.
|
|
254
|
+
|
|
215
255
|
# Camoufox() returns a context manager, we need to use __enter__() to get the context
|
|
216
256
|
camoufox_manager = Camoufox(
|
|
217
|
-
persistent_context=
|
|
218
|
-
user_data_dir=user_data_dir,
|
|
257
|
+
persistent_context=self.browser_config.persistent_context,
|
|
258
|
+
user_data_dir=user_data_dir if self.browser_config.persistent_context else None,
|
|
219
259
|
headless=self.browser_config.headless,
|
|
220
260
|
humanize=self.browser_config.humanize if hasattr(self.browser_config, 'humanize') else True,
|
|
221
261
|
os=self.browser_config.os if hasattr(self.browser_config, 'os') else 'linux',
|
|
222
262
|
geoip=self.browser_config.geoip if hasattr(self.browser_config, 'geoip') else True,
|
|
223
|
-
proxy=proxy_config
|
|
263
|
+
proxy=proxy_config,
|
|
264
|
+
config=self.browser_config.custom_config if self.browser_config.custom_config else None
|
|
224
265
|
)
|
|
225
266
|
|
|
226
267
|
# Enter the context manager to get the browser context
|
|
@@ -303,13 +344,27 @@ class TrendReq:
|
|
|
303
344
|
if not self.browser_page:
|
|
304
345
|
raise exceptions.BrowserError("Browser not initialized")
|
|
305
346
|
|
|
347
|
+
# Add random delay before request (anti-rate-limiting)
|
|
348
|
+
self._add_request_delay()
|
|
349
|
+
|
|
306
350
|
# Clear cache
|
|
307
351
|
self.browser_responses_cache.clear()
|
|
308
352
|
|
|
309
353
|
# Build URL
|
|
310
354
|
import urllib.parse
|
|
311
355
|
encoded_keyword = urllib.parse.quote(keyword)
|
|
312
|
-
|
|
356
|
+
|
|
357
|
+
# Get timeframe from config (default: 'today 1-m')
|
|
358
|
+
timeframe = getattr(self.browser_config, 'timeframe', 'today 1-m')
|
|
359
|
+
|
|
360
|
+
# Build URL with or without date parameter
|
|
361
|
+
if timeframe == 'today 12-m':
|
|
362
|
+
# Past 12 months - no date parameter needed
|
|
363
|
+
url = f"https://trends.google.com/trends/explore?q={encoded_keyword}&hl=en-GB"
|
|
364
|
+
else:
|
|
365
|
+
# Default: today 1-m or custom timeframe
|
|
366
|
+
encoded_timeframe = urllib.parse.quote(timeframe)
|
|
367
|
+
url = f"https://trends.google.com/trends/explore?date={encoded_timeframe}&q={encoded_keyword}&hl=en-GB"
|
|
313
368
|
|
|
314
369
|
try:
|
|
315
370
|
# Navigate and wait for network idle
|
|
@@ -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=
|
|
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,13 +207,27 @@ 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
|
|
|
195
216
|
# Build URL
|
|
196
217
|
import urllib.parse
|
|
197
218
|
encoded_keyword = urllib.parse.quote(keyword)
|
|
198
|
-
|
|
219
|
+
|
|
220
|
+
# Get timeframe from config (default: 'today 1-m')
|
|
221
|
+
timeframe = getattr(self.browser_config, 'timeframe', 'today 1-m')
|
|
222
|
+
|
|
223
|
+
# Build URL with or without date parameter
|
|
224
|
+
if timeframe == 'today 12-m':
|
|
225
|
+
# Past 12 months - no date parameter needed
|
|
226
|
+
url = f"https://trends.google.com/trends/explore?q={encoded_keyword}&hl=en-GB"
|
|
227
|
+
else:
|
|
228
|
+
# Default: today 1-m or custom timeframe
|
|
229
|
+
encoded_timeframe = urllib.parse.quote(timeframe)
|
|
230
|
+
url = f"https://trends.google.com/trends/explore?date={encoded_timeframe}&q={encoded_keyword}&hl=en-GB"
|
|
199
231
|
|
|
200
232
|
try:
|
|
201
233
|
# Navigate and wait for network idle
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pytrends-modern
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4
|
|
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
|
|
@@ -7,6 +7,7 @@ examples/basic_usage.py
|
|
|
7
7
|
examples/example_browser_mode.py
|
|
8
8
|
examples/example_docker_usage.py
|
|
9
9
|
examples/test_async_integration.py
|
|
10
|
+
examples/test_timeframes.py
|
|
10
11
|
pytrends_modern/__init__.py
|
|
11
12
|
pytrends_modern/browser_config.py
|
|
12
13
|
pytrends_modern/browser_config_camoufox.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pytrends_modern-0.2.2 → pytrends_modern-0.2.4}/pytrends_modern.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|