pytrends-modern 0.2.1__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.
- {pytrends_modern-0.2.1/pytrends_modern.egg-info → pytrends_modern-0.2.3}/PKG-INFO +39 -4
- {pytrends_modern-0.2.1 → pytrends_modern-0.2.3}/README.md +38 -3
- {pytrends_modern-0.2.1 → pytrends_modern-0.2.3}/examples/example_browser_mode.py +3 -3
- pytrends_modern-0.2.3/examples/example_docker_usage.py +50 -0
- {pytrends_modern-0.2.1 → pytrends_modern-0.2.3}/pyproject.toml +1 -1
- {pytrends_modern-0.2.1 → pytrends_modern-0.2.3}/pytrends_modern/__init__.py +1 -1
- {pytrends_modern-0.2.1 → pytrends_modern-0.2.3}/pytrends_modern/browser_config_camoufox.py +21 -3
- {pytrends_modern-0.2.1 → pytrends_modern-0.2.3}/pytrends_modern/request.py +48 -3
- {pytrends_modern-0.2.1 → pytrends_modern-0.2.3}/pytrends_modern/request_async.py +24 -3
- {pytrends_modern-0.2.1 → pytrends_modern-0.2.3/pytrends_modern.egg-info}/PKG-INFO +39 -4
- {pytrends_modern-0.2.1 → pytrends_modern-0.2.3}/pytrends_modern.egg-info/SOURCES.txt +1 -0
- {pytrends_modern-0.2.1 → pytrends_modern-0.2.3}/LICENSE +0 -0
- {pytrends_modern-0.2.1 → pytrends_modern-0.2.3}/MANIFEST.in +0 -0
- {pytrends_modern-0.2.1 → pytrends_modern-0.2.3}/examples/advanced_usage.py +0 -0
- {pytrends_modern-0.2.1 → pytrends_modern-0.2.3}/examples/basic_usage.py +0 -0
- {pytrends_modern-0.2.1 → pytrends_modern-0.2.3}/examples/test_async_integration.py +0 -0
- {pytrends_modern-0.2.1 → pytrends_modern-0.2.3}/pytrends_modern/browser_config.py +0 -0
- {pytrends_modern-0.2.1 → pytrends_modern-0.2.3}/pytrends_modern/camoufox_setup.py +0 -0
- {pytrends_modern-0.2.1 → pytrends_modern-0.2.3}/pytrends_modern/cli.py +0 -0
- {pytrends_modern-0.2.1 → pytrends_modern-0.2.3}/pytrends_modern/config.py +0 -0
- {pytrends_modern-0.2.1 → pytrends_modern-0.2.3}/pytrends_modern/exceptions.py +0 -0
- {pytrends_modern-0.2.1 → pytrends_modern-0.2.3}/pytrends_modern/proxy_extension.py +0 -0
- {pytrends_modern-0.2.1 → pytrends_modern-0.2.3}/pytrends_modern/py.typed +0 -0
- {pytrends_modern-0.2.1 → pytrends_modern-0.2.3}/pytrends_modern/rss.py +0 -0
- {pytrends_modern-0.2.1 → pytrends_modern-0.2.3}/pytrends_modern/scraper.py +0 -0
- {pytrends_modern-0.2.1 → pytrends_modern-0.2.3}/pytrends_modern/utils.py +0 -0
- {pytrends_modern-0.2.1 → pytrends_modern-0.2.3}/pytrends_modern.egg-info/dependency_links.txt +0 -0
- {pytrends_modern-0.2.1 → pytrends_modern-0.2.3}/pytrends_modern.egg-info/entry_points.txt +0 -0
- {pytrends_modern-0.2.1 → pytrends_modern-0.2.3}/pytrends_modern.egg-info/requires.txt +0 -0
- {pytrends_modern-0.2.1 → pytrends_modern-0.2.3}/pytrends_modern.egg-info/top_level.txt +0 -0
- {pytrends_modern-0.2.1 → pytrends_modern-0.2.3}/setup.cfg +0 -0
- {pytrends_modern-0.2.1 → pytrends_modern-0.2.3}/tests/conftest.py +0 -0
- {pytrends_modern-0.2.1 → 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.
|
|
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
|
|
@@ -175,11 +205,16 @@ RUN mkdir -p /root/.config && \
|
|
|
175
205
|
cd /root/.config && \
|
|
176
206
|
tar -xzf /tmp/profile.tar.gz
|
|
177
207
|
|
|
178
|
-
# 3. Use headless
|
|
179
|
-
config = BrowserConfig(headless=
|
|
208
|
+
# 3. Use headless="virtual" in container
|
|
209
|
+
config = BrowserConfig(headless="virtual") # Use Xvfb for Docker
|
|
180
210
|
```
|
|
181
211
|
|
|
182
|
-
|
|
212
|
+
**Headless Options:**
|
|
213
|
+
- `headless=False` - Show browser window (local development)
|
|
214
|
+
- `headless=True` - Standard headless (servers with display)
|
|
215
|
+
- `headless="virtual"` - Xvfb virtual display (Docker containers)
|
|
216
|
+
|
|
217
|
+
See `Dockerfile.example` and `examples/example_docker_usage.py` for complete Docker setup.
|
|
183
218
|
|
|
184
219
|
⚠️ **Security**: Profile contains Google session - keep secure, don't commit to git!
|
|
185
220
|
|
|
@@ -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
|
|
@@ -125,11 +155,16 @@ RUN mkdir -p /root/.config && \
|
|
|
125
155
|
cd /root/.config && \
|
|
126
156
|
tar -xzf /tmp/profile.tar.gz
|
|
127
157
|
|
|
128
|
-
# 3. Use headless
|
|
129
|
-
config = BrowserConfig(headless=
|
|
158
|
+
# 3. Use headless="virtual" in container
|
|
159
|
+
config = BrowserConfig(headless="virtual") # Use Xvfb for Docker
|
|
130
160
|
```
|
|
131
161
|
|
|
132
|
-
|
|
162
|
+
**Headless Options:**
|
|
163
|
+
- `headless=False` - Show browser window (local development)
|
|
164
|
+
- `headless=True` - Standard headless (servers with display)
|
|
165
|
+
- `headless="virtual"` - Xvfb virtual display (Docker containers)
|
|
166
|
+
|
|
167
|
+
See `Dockerfile.example` and `examples/example_docker_usage.py` for complete Docker setup.
|
|
133
168
|
|
|
134
169
|
⚠️ **Security**: Profile contains Google session - keep secure, don't commit to git!
|
|
135
170
|
|
|
@@ -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,50 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Example: Using pytrends-modern in Docker containers
|
|
4
|
+
|
|
5
|
+
When running in Docker, use headless="virtual" to enable Xvfb virtual display.
|
|
6
|
+
This prevents display errors in containerized environments.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import asyncio
|
|
10
|
+
from pytrends_modern import AsyncTrendReq, BrowserConfig
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
async def main():
|
|
14
|
+
"""Example async usage in Docker container"""
|
|
15
|
+
|
|
16
|
+
# Configure for Docker environment
|
|
17
|
+
config = BrowserConfig(
|
|
18
|
+
headless="virtual", # Use Xvfb virtual display for Docker
|
|
19
|
+
humanize=True,
|
|
20
|
+
os='linux',
|
|
21
|
+
geoip=True
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
# For local development, use:
|
|
25
|
+
# config = BrowserConfig(headless=False) # Show browser window
|
|
26
|
+
# config = BrowserConfig(headless=True) # Standard headless
|
|
27
|
+
|
|
28
|
+
print("🐳 Running pytrends-modern in Docker container...")
|
|
29
|
+
print(f"📁 Profile: {config.user_data_dir}")
|
|
30
|
+
|
|
31
|
+
async with AsyncTrendReq(browser_config=config) as pytrends:
|
|
32
|
+
pytrends.kw_list = ["Docker"]
|
|
33
|
+
|
|
34
|
+
# Get interest over time
|
|
35
|
+
print("\n📊 Fetching interest_over_time...")
|
|
36
|
+
df = await pytrends.interest_over_time()
|
|
37
|
+
print(f"✓ Got {len(df)} rows")
|
|
38
|
+
print(df.head())
|
|
39
|
+
|
|
40
|
+
# Get interest by region
|
|
41
|
+
print("\n🌍 Fetching interest_by_region...")
|
|
42
|
+
df_region = await pytrends.interest_by_region()
|
|
43
|
+
print(f"✓ Got {len(df_region)} rows")
|
|
44
|
+
print(df_region.head())
|
|
45
|
+
|
|
46
|
+
print("\n✅ Complete!")
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
if __name__ == "__main__":
|
|
50
|
+
asyncio.run(main())
|
|
@@ -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.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"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Browser configuration for Camoufox automation"""
|
|
2
2
|
|
|
3
|
-
from typing import Optional
|
|
3
|
+
from typing import Optional, Union, Dict, Any
|
|
4
4
|
import os as os_module
|
|
5
5
|
|
|
6
6
|
|
|
@@ -18,7 +18,9 @@ class BrowserConfig:
|
|
|
18
18
|
|
|
19
19
|
Args:
|
|
20
20
|
headless: Run browser in headless mode (default: False)
|
|
21
|
-
|
|
21
|
+
- False: Show browser window (for local development)
|
|
22
|
+
- True: Standard headless mode (for servers with display)
|
|
23
|
+
- 'virtual': Use Xvfb virtual display (for Docker containers)
|
|
22
24
|
proxy_server: Proxy server URL (e.g., 'http://proxy.com:8080')
|
|
23
25
|
proxy_username: Proxy username (for authenticated proxies)
|
|
24
26
|
proxy_password: Proxy password (for authenticated proxies)
|
|
@@ -27,6 +29,12 @@ class BrowserConfig:
|
|
|
27
29
|
humanize: Enable human-like cursor movement (default: True)
|
|
28
30
|
os: Operating system for fingerprint ('windows', 'macos', 'linux')
|
|
29
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)
|
|
30
38
|
|
|
31
39
|
Example:
|
|
32
40
|
>>> from pytrends_modern import TrendReq, BrowserConfig
|
|
@@ -47,7 +55,7 @@ class BrowserConfig:
|
|
|
47
55
|
|
|
48
56
|
def __init__(
|
|
49
57
|
self,
|
|
50
|
-
headless: bool = False,
|
|
58
|
+
headless: Union[bool, str] = False,
|
|
51
59
|
proxy_server: Optional[str] = None,
|
|
52
60
|
proxy_username: Optional[str] = None,
|
|
53
61
|
proxy_password: Optional[str] = None,
|
|
@@ -55,6 +63,11 @@ class BrowserConfig:
|
|
|
55
63
|
humanize: bool = True,
|
|
56
64
|
os: str = 'linux',
|
|
57
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,
|
|
58
71
|
):
|
|
59
72
|
self.headless = headless
|
|
60
73
|
self.proxy_server = proxy_server
|
|
@@ -64,4 +77,9 @@ class BrowserConfig:
|
|
|
64
77
|
self.humanize = humanize
|
|
65
78
|
self.os = os
|
|
66
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 {}
|
|
67
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=
|
|
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=
|
|
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
|
|
@@ -175,11 +205,16 @@ RUN mkdir -p /root/.config && \
|
|
|
175
205
|
cd /root/.config && \
|
|
176
206
|
tar -xzf /tmp/profile.tar.gz
|
|
177
207
|
|
|
178
|
-
# 3. Use headless
|
|
179
|
-
config = BrowserConfig(headless=
|
|
208
|
+
# 3. Use headless="virtual" in container
|
|
209
|
+
config = BrowserConfig(headless="virtual") # Use Xvfb for Docker
|
|
180
210
|
```
|
|
181
211
|
|
|
182
|
-
|
|
212
|
+
**Headless Options:**
|
|
213
|
+
- `headless=False` - Show browser window (local development)
|
|
214
|
+
- `headless=True` - Standard headless (servers with display)
|
|
215
|
+
- `headless="virtual"` - Xvfb virtual display (Docker containers)
|
|
216
|
+
|
|
217
|
+
See `Dockerfile.example` and `examples/example_docker_usage.py` for complete Docker setup.
|
|
183
218
|
|
|
184
219
|
⚠️ **Security**: Profile contains Google session - keep secure, don't commit to git!
|
|
185
220
|
|
|
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.1 → pytrends_modern-0.2.3}/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
|