pytrends-modern 0.2.2__py3-none-any.whl → 0.2.3__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.
- pytrends_modern/__init__.py +1 -1
- pytrends_modern/browser_config_camoufox.py +17 -1
- pytrends_modern/request.py +48 -3
- pytrends_modern/request_async.py +24 -3
- {pytrends_modern-0.2.2.dist-info → pytrends_modern-0.2.3.dist-info}/METADATA +31 -1
- {pytrends_modern-0.2.2.dist-info → pytrends_modern-0.2.3.dist-info}/RECORD +10 -10
- {pytrends_modern-0.2.2.dist-info → pytrends_modern-0.2.3.dist-info}/WHEEL +0 -0
- {pytrends_modern-0.2.2.dist-info → pytrends_modern-0.2.3.dist-info}/entry_points.txt +0 -0
- {pytrends_modern-0.2.2.dist-info → pytrends_modern-0.2.3.dist-info}/licenses/LICENSE +0 -0
- {pytrends_modern-0.2.2.dist-info → pytrends_modern-0.2.3.dist-info}/top_level.txt +0 -0
pytrends_modern/__init__.py
CHANGED
|
@@ -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
|
|
pytrends_modern/request.py
CHANGED
|
@@ -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=
|
|
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
|
|
pytrends_modern/request_async.py
CHANGED
|
@@ -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,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.
|
|
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
|
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
pytrends_modern/__init__.py,sha256=
|
|
1
|
+
pytrends_modern/__init__.py,sha256=wyMSGntrOovgU4jGEXfczCVExZcFRZlmr45dnNyoCJ0,844
|
|
2
2
|
pytrends_modern/browser_config.py,sha256=aEznKEaAiJ1vFPYMeNc2wiyehijnleNBFoiZIXWQcRQ,3513
|
|
3
|
-
pytrends_modern/browser_config_camoufox.py,sha256=
|
|
3
|
+
pytrends_modern/browser_config_camoufox.py,sha256=x8OmanFxPl1goqXqdnpYIIQm0GHE8KNfr8byMj-BQ90,3634
|
|
4
4
|
pytrends_modern/camoufox_setup.py,sha256=WkytaNt64Gro8wJUPXaoUPhesDRvz460UeHGzETR6ds,10534
|
|
5
5
|
pytrends_modern/cli.py,sha256=lwuXzFY5S7glSGL180dFkpnTcVsM7JfTzJ2vLKs8TLA,12784
|
|
6
6
|
pytrends_modern/config.py,sha256=zPCMgdAFwcrQPMYO87dLAirkxnvpvsTx9DkWZGkl8zk,5198
|
|
7
7
|
pytrends_modern/exceptions.py,sha256=3WWDiYha1Tj2Hn2TNQfAPUncdGHIJbqMLFRQjLLXm3k,1712
|
|
8
8
|
pytrends_modern/proxy_extension.py,sha256=j-Cseb5G627q2G7dRdlkhqzGUG9dhmoTi1UK5wwF3u4,2214
|
|
9
9
|
pytrends_modern/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
-
pytrends_modern/request.py,sha256=
|
|
11
|
-
pytrends_modern/request_async.py,sha256=
|
|
10
|
+
pytrends_modern/request.py,sha256=uN15KUHoeRxkZoDX3Ml-XbhLf-rnm30Ly_DuXhpXk4w,48749
|
|
11
|
+
pytrends_modern/request_async.py,sha256=zOGq6tpZ3lF61fnxbVtsd5GcWSEBmhVD3hB1KKhCWvs,14499
|
|
12
12
|
pytrends_modern/rss.py,sha256=6Qq8MsVJu9WtE5UcYlUze7EexT81Mq4af7pPFOMcDS0,11414
|
|
13
13
|
pytrends_modern/scraper.py,sha256=x1xbFjUs7ULOBlIWLZ90G6WXJwB2EYuwUo0dWmHAjEQ,9706
|
|
14
14
|
pytrends_modern/utils.py,sha256=xPf4nz4c8Mn-737PZTe6o3HChjZvIR8brIftNg0-IFE,6875
|
|
15
|
-
pytrends_modern-0.2.
|
|
16
|
-
pytrends_modern-0.2.
|
|
17
|
-
pytrends_modern-0.2.
|
|
18
|
-
pytrends_modern-0.2.
|
|
19
|
-
pytrends_modern-0.2.
|
|
20
|
-
pytrends_modern-0.2.
|
|
15
|
+
pytrends_modern-0.2.3.dist-info/licenses/LICENSE,sha256=4K_FiN4IB1h5rffiOC8s5Tpxiv161v0eNIQJMDbvC0o,1469
|
|
16
|
+
pytrends_modern-0.2.3.dist-info/METADATA,sha256=VRYlrBVsq2BqAnjHVz2dg9iEMaJffyHjYAkOW8FsUN0,15645
|
|
17
|
+
pytrends_modern-0.2.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
18
|
+
pytrends_modern-0.2.3.dist-info/entry_points.txt,sha256=1ilOUXV2wt8NqQp2ViD-obi9k8iQANEa3eU-7S3jTgs,61
|
|
19
|
+
pytrends_modern-0.2.3.dist-info/top_level.txt,sha256=bbuIEWVfkaA-sBTKf-Dzau5Ll2zlHs21o0zWtCmQG50,16
|
|
20
|
+
pytrends_modern-0.2.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|