@shaivpidadi/trends-js 0.0.0-beta.2 → 0.0.0-beta.4
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.
- package/README.md +201 -0
- package/lib/constants.js +1 -1
- package/lib/helpers/googleTrendsAPI.d.ts +1 -1
- package/lib/helpers/googleTrendsAPI.js +46 -23
- package/lib/helpers/request.d.ts +5 -1
- package/lib/helpers/request.js +67 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
🚧 WIP: Temporary workaround for [#175](https://github.com/pat310/google-trends-api/issues/175)
|
|
2
|
+
|
|
3
|
+
# @shaivpidadi/trends-js
|
|
4
|
+
|
|
5
|
+
A TypeScript library for interacting with the Google Trends API. This package provides a simple and type-safe way to access Google Trends data programmatically.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @shaivpidadi/trends-js
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Features
|
|
14
|
+
|
|
15
|
+
- Get daily trending topics
|
|
16
|
+
- Get real-time trending topics
|
|
17
|
+
- Get autocomplete suggestions
|
|
18
|
+
- Explore trends data
|
|
19
|
+
- Get interest by region data
|
|
20
|
+
- TypeScript support
|
|
21
|
+
- Promise-based API
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
### Importing
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
import GoogleTrendsApi from '@shaivpidadi/trends-js';
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Daily Trends
|
|
32
|
+
|
|
33
|
+
Get daily trending topics for a specific region:
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
const result = await GoogleTrendsApi.dailyTrends({
|
|
37
|
+
geo: 'US', // Default: 'US'
|
|
38
|
+
lang: 'en' // Default: 'en'
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// Result structure:
|
|
42
|
+
// {
|
|
43
|
+
// allTrendingStories: Array<...>,
|
|
44
|
+
// summary: string[]
|
|
45
|
+
// }
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Real-Time Trends
|
|
49
|
+
|
|
50
|
+
Get real-time trending topics:
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
const result = await GoogleTrendsApi.realTimeTrends({
|
|
54
|
+
geo: 'US', // Default: 'US'
|
|
55
|
+
trendingHours: 4 // Default: 4
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// Result structure:
|
|
59
|
+
// {
|
|
60
|
+
// allTrendingStories: Array<...>,
|
|
61
|
+
// summary: string[]
|
|
62
|
+
// }
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Autocomplete
|
|
66
|
+
|
|
67
|
+
Get search suggestions for a keyword:
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
const suggestions = await GoogleTrendsApi.autocomplete(
|
|
71
|
+
'bitcoin', // Keyword to get suggestions for
|
|
72
|
+
'en-US' // Language (default: 'en-US')
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
// Returns: string[]
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Explore
|
|
79
|
+
|
|
80
|
+
Get widget data for a keyword:
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
const result = await GoogleTrendsApi.explore({
|
|
84
|
+
keyword: 'bitcoin',
|
|
85
|
+
geo: 'US', // Default: 'US'
|
|
86
|
+
time: 'today 12-m', // Default: 'today 12-m'
|
|
87
|
+
category: 0, // Default: 0
|
|
88
|
+
property: '', // Default: ''
|
|
89
|
+
hl: 'en-US' // Default: 'en-US'
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// Result structure:
|
|
93
|
+
// {
|
|
94
|
+
// widgets: Array<{
|
|
95
|
+
// id: string,
|
|
96
|
+
// request: {...},
|
|
97
|
+
// token: string
|
|
98
|
+
// }>
|
|
99
|
+
// }
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Interest by Region
|
|
103
|
+
|
|
104
|
+
Get interest data by region:
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
const result = await GoogleTrendsApi.interestByRegion({
|
|
108
|
+
keyword: 'Stock Market', // Required - string or string[]
|
|
109
|
+
startTime: new Date('2024-01-01'), // Optional - defaults to 2004-01-01
|
|
110
|
+
endTime: new Date(), // Optional - defaults to current date
|
|
111
|
+
geo: 'US', // Optional - string or string[] - defaults to 'US'
|
|
112
|
+
resolution: 'REGION', // Optional - 'COUNTRY' | 'REGION' | 'CITY' | 'DMA'
|
|
113
|
+
hl: 'en-US', // Optional - defaults to 'en-US'
|
|
114
|
+
timezone: -240, // Optional - defaults to local timezone
|
|
115
|
+
category: 0 // Optional - defaults to 0
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// Result structure:
|
|
119
|
+
// {
|
|
120
|
+
// default: {
|
|
121
|
+
// geoMapData: Array<{
|
|
122
|
+
// geoCode: string,
|
|
123
|
+
// geoName: string,
|
|
124
|
+
// value: number[],
|
|
125
|
+
// formattedValue: string[],
|
|
126
|
+
// maxValueIndex: number,
|
|
127
|
+
// hasData: boolean[],
|
|
128
|
+
// coordinates?: {
|
|
129
|
+
// lat: number,
|
|
130
|
+
// lng: number
|
|
131
|
+
// }
|
|
132
|
+
// }>
|
|
133
|
+
// }
|
|
134
|
+
// }
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Example with multiple keywords and regions:
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
const result = await GoogleTrendsApi.interestByRegion({
|
|
141
|
+
keyword: ['wine', 'peanuts'],
|
|
142
|
+
geo: ['US-CA', 'US-VA'],
|
|
143
|
+
startTime: new Date('2024-01-01'),
|
|
144
|
+
endTime: new Date(),
|
|
145
|
+
resolution: 'CITY'
|
|
146
|
+
});
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## API Reference
|
|
150
|
+
|
|
151
|
+
### DailyTrendsOptions
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
interface DailyTrendsOptions {
|
|
155
|
+
geo?: string; // Default: 'US'
|
|
156
|
+
lang?: string; // Default: 'en'
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### RealTimeTrendsOptions
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
interface RealTimeTrendsOptions {
|
|
164
|
+
geo: string;
|
|
165
|
+
trendingHours?: number; // Default: 4
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### ExploreOptions
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
interface ExploreOptions {
|
|
173
|
+
keyword: string;
|
|
174
|
+
geo?: string; // Default: 'US'
|
|
175
|
+
time?: string; // Default: 'today 12-m'
|
|
176
|
+
category?: number; // Default: 0
|
|
177
|
+
property?: string; // Default: ''
|
|
178
|
+
hl?: string; // Default: 'en-US'
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### InterestByRegionOptions
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
interface InterestByRegionOptions {
|
|
186
|
+
keyword: string | string[]; // Required - search term(s)
|
|
187
|
+
startTime?: Date; // Optional - start date
|
|
188
|
+
endTime?: Date; // Optional - end date
|
|
189
|
+
geo?: string | string[]; // Optional - geocode(s)
|
|
190
|
+
resolution?: 'COUNTRY' | 'REGION' | 'CITY' | 'DMA'; // Optional
|
|
191
|
+
hl?: string; // Optional - language code
|
|
192
|
+
timezone?: number; // Optional - timezone offset
|
|
193
|
+
category?: number; // Optional - category number
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Development
|
|
198
|
+
|
|
199
|
+
### Building
|
|
200
|
+
|
|
201
|
+
```
|
package/lib/constants.js
CHANGED
|
@@ -23,7 +23,7 @@ exports.GOOGLE_TRENDS_MAPPER = {
|
|
|
23
23
|
},
|
|
24
24
|
["explore" /* GoogleTrendsEndpoints.explore */]: {
|
|
25
25
|
path: '/trends/api/explore',
|
|
26
|
-
method: '
|
|
26
|
+
method: 'POST',
|
|
27
27
|
host: GOOGLE_TRENDS_BASE_URL,
|
|
28
28
|
url: `https://${GOOGLE_TRENDS_BASE_URL}/trends/api/explore`,
|
|
29
29
|
headers: {},
|
|
@@ -4,7 +4,7 @@ export declare class GoogleTrendsApi {
|
|
|
4
4
|
dailyTrends({ geo, lang }: DailyTrendingTopicsOptions): Promise<DailyTrendingTopics>;
|
|
5
5
|
realTimeTrends({ geo, trendingHours }: RealTimeTrendsOptions): Promise<DailyTrendingTopics>;
|
|
6
6
|
explore({ keyword, geo, time, category, property, hl, }: ExploreOptions): Promise<ExploreResponse>;
|
|
7
|
-
interestByRegion({ keyword,
|
|
7
|
+
interestByRegion({ keyword, startTime, endTime, geo, resolution, hl, timezone, category }: InterestByRegionOptions): Promise<InterestByRegionResponse>;
|
|
8
8
|
}
|
|
9
9
|
declare const _default: GoogleTrendsApi;
|
|
10
10
|
export default _default;
|
|
@@ -120,44 +120,68 @@ class GoogleTrendsApi {
|
|
|
120
120
|
}
|
|
121
121
|
});
|
|
122
122
|
}
|
|
123
|
-
|
|
123
|
+
//
|
|
124
|
+
interestByRegion({ keyword, startTime = new Date('2004-01-01'), endTime = new Date(), geo = 'US', resolution = 'REGION', hl = 'en-US', timezone = new Date().getTimezoneOffset(), category = 0 }) {
|
|
124
125
|
return __awaiter(this, void 0, void 0, function* () {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
126
|
+
const formatDate = (date) => {
|
|
127
|
+
return date.toISOString().split('T')[0];
|
|
128
|
+
};
|
|
129
|
+
const formatTrendsDate = (date) => {
|
|
130
|
+
const pad = (n) => n.toString().padStart(2, '0');
|
|
131
|
+
const yyyy = date.getFullYear();
|
|
132
|
+
const mm = pad(date.getMonth() + 1);
|
|
133
|
+
const dd = pad(date.getDate());
|
|
134
|
+
const hh = pad(date.getHours());
|
|
135
|
+
const min = pad(date.getMinutes());
|
|
136
|
+
const ss = pad(date.getSeconds());
|
|
137
|
+
return `${yyyy}-${mm}-${dd}T${hh}\\:${min}\\:${ss}`;
|
|
138
|
+
};
|
|
139
|
+
const getDateRangeParam = (date) => {
|
|
140
|
+
const yesterday = new Date(date);
|
|
141
|
+
yesterday.setDate(date.getDate() - 1);
|
|
142
|
+
const formattedStart = formatTrendsDate(yesterday);
|
|
143
|
+
const formattedEnd = formatTrendsDate(date);
|
|
144
|
+
return `${formattedStart} ${formattedEnd}`;
|
|
145
|
+
};
|
|
146
|
+
const exploreResponse = yield this.explore({
|
|
147
|
+
keyword: Array.isArray(keyword) ? keyword[0] : keyword,
|
|
148
|
+
geo: Array.isArray(geo) ? geo[0] : geo,
|
|
149
|
+
time: `${getDateRangeParam(startTime)} ${getDateRangeParam(endTime)}`,
|
|
150
|
+
category,
|
|
151
|
+
hl
|
|
152
|
+
});
|
|
153
|
+
const widget = exploreResponse.widgets.find(w => w.id === 'GEO_MAP');
|
|
128
154
|
if (!widget) {
|
|
129
155
|
return { default: { geoMapData: [] } };
|
|
130
156
|
}
|
|
131
157
|
const options = Object.assign(Object.assign({}, constants_1.GOOGLE_TRENDS_MAPPER["interestByRegion" /* GoogleTrendsEndpoints.interestByRegion */]), { qs: {
|
|
132
158
|
hl,
|
|
133
|
-
tz:
|
|
159
|
+
tz: timezone.toString(),
|
|
134
160
|
req: JSON.stringify({
|
|
135
|
-
geo: {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
161
|
+
geo: {
|
|
162
|
+
country: Array.isArray(geo) ? geo[0] : geo
|
|
163
|
+
},
|
|
164
|
+
comparisonItem: [{
|
|
165
|
+
time: `${formatDate(startTime)} ${formatDate(endTime)}`,
|
|
139
166
|
complexKeywordsRestriction: {
|
|
140
|
-
keyword: [
|
|
141
|
-
{
|
|
167
|
+
keyword: [{
|
|
142
168
|
type: 'BROAD',
|
|
143
|
-
value: keyword
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
},
|
|
148
|
-
],
|
|
169
|
+
value: Array.isArray(keyword) ? keyword[0] : keyword
|
|
170
|
+
}]
|
|
171
|
+
}
|
|
172
|
+
}],
|
|
149
173
|
resolution,
|
|
150
174
|
locale: hl,
|
|
151
175
|
requestOptions: {
|
|
152
176
|
property: '',
|
|
153
|
-
backend: '
|
|
154
|
-
category
|
|
177
|
+
backend: 'CM',
|
|
178
|
+
category
|
|
155
179
|
},
|
|
156
180
|
userConfig: {
|
|
157
|
-
userType: '
|
|
158
|
-
}
|
|
181
|
+
userType: 'USER_TYPE_LEGIT_USER'
|
|
182
|
+
}
|
|
159
183
|
}),
|
|
160
|
-
token: widget.token
|
|
184
|
+
token: widget.token
|
|
161
185
|
} });
|
|
162
186
|
try {
|
|
163
187
|
const response = yield (0, request_1.request)(options.url, options);
|
|
@@ -167,7 +191,6 @@ class GoogleTrendsApi {
|
|
|
167
191
|
return data;
|
|
168
192
|
}
|
|
169
193
|
catch (error) {
|
|
170
|
-
console.error('Interest by region request failed:', error);
|
|
171
194
|
return { default: { geoMapData: [] } };
|
|
172
195
|
}
|
|
173
196
|
});
|
package/lib/helpers/request.d.ts
CHANGED
package/lib/helpers/request.js
CHANGED
|
@@ -8,10 +8,75 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
8
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
11
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
15
|
exports.request = void 0;
|
|
16
|
+
const https_1 = __importDefault(require("https"));
|
|
17
|
+
const querystring_1 = __importDefault(require("querystring"));
|
|
18
|
+
let cookieVal;
|
|
19
|
+
function rereq(options) {
|
|
20
|
+
return new Promise((resolve, reject) => {
|
|
21
|
+
const req = https_1.default.request(options, (res) => {
|
|
22
|
+
let chunk = '';
|
|
23
|
+
res.on('data', (data) => {
|
|
24
|
+
chunk += data;
|
|
25
|
+
});
|
|
26
|
+
res.on('end', () => {
|
|
27
|
+
resolve(chunk.toString());
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
req.on('error', (e) => {
|
|
31
|
+
reject(e);
|
|
32
|
+
});
|
|
33
|
+
req.end();
|
|
34
|
+
});
|
|
35
|
+
}
|
|
13
36
|
const request = (url, options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
14
|
-
const
|
|
15
|
-
|
|
37
|
+
const parsedUrl = new URL(url);
|
|
38
|
+
const requestOptions = {
|
|
39
|
+
host: parsedUrl.host,
|
|
40
|
+
method: options.method || 'POST',
|
|
41
|
+
path: `${parsedUrl.pathname}${options.qs ? '?' + querystring_1.default.stringify(options.qs) : ''}`,
|
|
42
|
+
headers: {}
|
|
43
|
+
};
|
|
44
|
+
if (cookieVal) {
|
|
45
|
+
requestOptions.headers = { 'cookie': cookieVal };
|
|
46
|
+
}
|
|
47
|
+
const response = yield new Promise((resolve, reject) => {
|
|
48
|
+
const req = https_1.default.request(requestOptions, (res) => {
|
|
49
|
+
let chunk = '';
|
|
50
|
+
res.on('data', (data) => {
|
|
51
|
+
chunk += data;
|
|
52
|
+
});
|
|
53
|
+
res.on('end', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
54
|
+
if (res.statusCode === 429 && res.headers['set-cookie']) {
|
|
55
|
+
cookieVal = res.headers['set-cookie'][0].split(';')[0];
|
|
56
|
+
requestOptions.headers = { 'cookie': cookieVal };
|
|
57
|
+
try {
|
|
58
|
+
const retryResponse = yield rereq(requestOptions);
|
|
59
|
+
resolve(retryResponse);
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
reject(err);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
resolve(chunk.toString());
|
|
67
|
+
}
|
|
68
|
+
}));
|
|
69
|
+
});
|
|
70
|
+
if (options.body) {
|
|
71
|
+
req.write(options.body);
|
|
72
|
+
}
|
|
73
|
+
req.on('error', (e) => {
|
|
74
|
+
reject(e);
|
|
75
|
+
});
|
|
76
|
+
req.end();
|
|
77
|
+
});
|
|
78
|
+
return {
|
|
79
|
+
text: () => Promise.resolve(response)
|
|
80
|
+
};
|
|
16
81
|
});
|
|
17
82
|
exports.request = request;
|