newsdataapi 0.1.26__tar.gz → 0.1.28__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.
- {newsdataapi-0.1.26/newsdataapi.egg-info → newsdataapi-0.1.28}/PKG-INFO +1 -1
- {newsdataapi-0.1.26 → newsdataapi-0.1.28}/newsdataapi/constants.py +6 -2
- {newsdataapi-0.1.26 → newsdataapi-0.1.28}/newsdataapi/helpers.py +0 -16
- newsdataapi-0.1.28/newsdataapi/newsdataapi_client.py +707 -0
- {newsdataapi-0.1.26 → newsdataapi-0.1.28/newsdataapi.egg-info}/PKG-INFO +1 -1
- {newsdataapi-0.1.26 → newsdataapi-0.1.28}/setup.py +1 -1
- {newsdataapi-0.1.26 → newsdataapi-0.1.28}/tests/test_newsdataapi.py +4 -0
- newsdataapi-0.1.26/newsdataapi/newsdataapi_client.py +0 -346
- {newsdataapi-0.1.26 → newsdataapi-0.1.28}/LICENSE +0 -0
- {newsdataapi-0.1.26 → newsdataapi-0.1.28}/MANIFEST.in +0 -0
- {newsdataapi-0.1.26 → newsdataapi-0.1.28}/README.md +0 -0
- {newsdataapi-0.1.26 → newsdataapi-0.1.28}/newsdataapi/__init__.py +0 -0
- {newsdataapi-0.1.26 → newsdataapi-0.1.28}/newsdataapi/newsdataapi_exception.py +0 -0
- {newsdataapi-0.1.26 → newsdataapi-0.1.28}/newsdataapi/utils.py +0 -0
- {newsdataapi-0.1.26 → newsdataapi-0.1.28}/newsdataapi.egg-info/SOURCES.txt +0 -0
- {newsdataapi-0.1.26 → newsdataapi-0.1.28}/newsdataapi.egg-info/dependency_links.txt +0 -0
- {newsdataapi-0.1.26 → newsdataapi-0.1.28}/newsdataapi.egg-info/requires.txt +0 -0
- {newsdataapi-0.1.26 → newsdataapi-0.1.28}/newsdataapi.egg-info/top_level.txt +0 -0
- {newsdataapi-0.1.26 → newsdataapi-0.1.28}/requirements.txt +0 -0
- {newsdataapi-0.1.26 → newsdataapi-0.1.28}/setup.cfg +0 -0
- {newsdataapi-0.1.26 → newsdataapi-0.1.28}/tests/__init__.py +0 -0
|
@@ -17,9 +17,13 @@ COUNT_ENDPOINT = 'count'
|
|
|
17
17
|
|
|
18
18
|
CRYPTO_COUNT_ENDPOINT = 'crypto/count'
|
|
19
19
|
|
|
20
|
+
# News Market URL
|
|
21
|
+
|
|
22
|
+
MARKET_ENDPOINT = 'market'
|
|
23
|
+
|
|
20
24
|
# Default request values
|
|
21
25
|
DEFAULT_REQUEST_TIMEOUT = 300
|
|
22
26
|
DEFAULT_MAX_RETRIES = 5
|
|
23
27
|
DEFAULT_RETRY_DELAY = 1800
|
|
24
|
-
|
|
25
|
-
|
|
28
|
+
|
|
29
|
+
PAGINATION_DELAY = 1
|
|
@@ -1,21 +1,5 @@
|
|
|
1
1
|
import time,os,csv
|
|
2
2
|
|
|
3
|
-
def get(request_method, URL, URL_parameters_encoded, proxies, request_timeout):
|
|
4
|
-
if proxies is None:
|
|
5
|
-
return request_method.get(URL + "?" + URL_parameters_encoded, timeout=request_timeout)
|
|
6
|
-
else:
|
|
7
|
-
return request_method.get(URL + "?" + URL_parameters_encoded, timeout=request_timeout, proxies = proxies)
|
|
8
|
-
|
|
9
|
-
def MaxRetries(response, max_retries, retry_delay, request_method, URL, URL_parameters_encoded, proxies, request_timeout):
|
|
10
|
-
while (max_retries):
|
|
11
|
-
time.sleep(retry_delay)
|
|
12
|
-
response = get(request_method, URL, URL_parameters_encoded, proxies, request_timeout)
|
|
13
|
-
if response.status_code!=500:
|
|
14
|
-
break
|
|
15
|
-
max_retries-=1
|
|
16
|
-
return response
|
|
17
|
-
|
|
18
|
-
|
|
19
3
|
class FileHandler:
|
|
20
4
|
|
|
21
5
|
def __init__(self,folder_path:str=None) -> None:
|
|
@@ -0,0 +1,707 @@
|
|
|
1
|
+
from warnings import warn
|
|
2
|
+
import requests,time,logging
|
|
3
|
+
from typing import Dict, Any, List
|
|
4
|
+
from newsdataapi import constants
|
|
5
|
+
from typing import Optional,Union
|
|
6
|
+
from datetime import datetime,timezone
|
|
7
|
+
from newsdataapi.helpers import FileHandler
|
|
8
|
+
from requests.exceptions import RequestException
|
|
9
|
+
from newsdataapi.newsdataapi_exception import NewsdataException
|
|
10
|
+
from urllib.parse import urlencode, quote,urlparse,parse_qs,urljoin
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
class NewsDataApiClient(FileHandler):
|
|
15
|
+
|
|
16
|
+
def __init__(
|
|
17
|
+
self,
|
|
18
|
+
apikey: str,
|
|
19
|
+
session: Optional[bool] = False,
|
|
20
|
+
proxies: Optional[Dict[str, Any]] = None,
|
|
21
|
+
max_retries: Optional[int] = constants.DEFAULT_MAX_RETRIES,
|
|
22
|
+
retry_delay: Optional[int] = constants.DEFAULT_RETRY_DELAY,
|
|
23
|
+
pagination_delay: Optional[int] = constants.PAGINATION_DELAY,
|
|
24
|
+
request_timeout: Optional[int] = constants.DEFAULT_REQUEST_TIMEOUT,
|
|
25
|
+
max_result: Optional[int] = 10**10,
|
|
26
|
+
max_pages: Optional[int] = 10**10,
|
|
27
|
+
debug: Optional[bool] = False,
|
|
28
|
+
folder_path: Optional[str] = None,
|
|
29
|
+
include_headers: Optional[bool] = False
|
|
30
|
+
) -> None:
|
|
31
|
+
"""Initializes newsdata client object for access Newsdata APIs."""
|
|
32
|
+
self.apikey = apikey
|
|
33
|
+
self.request_method:requests = requests if session == False else requests.Session()
|
|
34
|
+
self.max_result = max_result
|
|
35
|
+
self.max_pages = max_pages
|
|
36
|
+
self.max_retries = max_retries
|
|
37
|
+
self.retry_delay = retry_delay
|
|
38
|
+
self.pagination_delay = pagination_delay
|
|
39
|
+
self.proxies = proxies
|
|
40
|
+
self.request_timeout = request_timeout
|
|
41
|
+
# self.is_debug = debug
|
|
42
|
+
self.include_headers = include_headers
|
|
43
|
+
self.set_base_url()
|
|
44
|
+
super().__init__(folder_path=folder_path)
|
|
45
|
+
|
|
46
|
+
def set_base_url(self,new_base_url:Optional[str]=constants.BASE_URL)->None:
|
|
47
|
+
self.latest_url = urljoin(new_base_url,constants.LATEST_ENDPOINT)
|
|
48
|
+
self.archive_url = urljoin(new_base_url,constants.ARCHIVE_ENDPOINT)
|
|
49
|
+
self.crypto_url = urljoin(new_base_url,constants.CRYPTO_ENDPOINT)
|
|
50
|
+
self.sources_url = urljoin(new_base_url,constants.SOURCES_ENDPOINT)
|
|
51
|
+
self.count_url = urljoin(new_base_url,constants.COUNT_ENDPOINT)
|
|
52
|
+
self.crypto_count_url = urljoin(new_base_url,constants.CRYPTO_COUNT_ENDPOINT)
|
|
53
|
+
self.market_url = urljoin(new_base_url,constants.MARKET_ENDPOINT)
|
|
54
|
+
|
|
55
|
+
def set_retries( self, max_retries:int, retry_delay:int)->None:
|
|
56
|
+
""" API maximum retry and delay"""
|
|
57
|
+
self.max_retries = max_retries
|
|
58
|
+
self.retry_delay = retry_delay
|
|
59
|
+
|
|
60
|
+
def set_request_timeout( self, request_timeout:int)->None:
|
|
61
|
+
""" API maximum timeout for the request """
|
|
62
|
+
self.request_timeout = request_timeout
|
|
63
|
+
|
|
64
|
+
def api_proxies( self, proxies:dict)->None:
|
|
65
|
+
""" Configure Proxie dictionary """
|
|
66
|
+
self.proxies = proxies
|
|
67
|
+
|
|
68
|
+
def set_pagination_delay( self, pagination_delay:int)->None:
|
|
69
|
+
""" Set delay between paginated requests """
|
|
70
|
+
self.pagination_delay = pagination_delay
|
|
71
|
+
|
|
72
|
+
def __validate_parms(self, user_param: Dict[str, Any]) -> Dict[str, Any]:
|
|
73
|
+
bool_params = {'full_content','image','video','removeduplicate'}
|
|
74
|
+
int_params = {'size'}
|
|
75
|
+
string_params = {
|
|
76
|
+
'q','qInTitle','country','category','language','domain','domainurl','excludedomain','timezone','page',
|
|
77
|
+
'from_date','to_date','qInMeta','prioritydomain','timeframe','tag','sentiment','region','coin',
|
|
78
|
+
'excludefield','excludecategory','id','excludelanguage','organization','url','sort','symbol','webhook',
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
def validate_url(url:str)-> str:
|
|
82
|
+
valid_fn_parms = {k.lower() for k in user_param.keys()}
|
|
83
|
+
parsed_url = urlparse(url)
|
|
84
|
+
if parsed_url.netloc:
|
|
85
|
+
q_string = parse_qs(parsed_url.query)
|
|
86
|
+
else:
|
|
87
|
+
q_string = parse_qs(url)
|
|
88
|
+
|
|
89
|
+
valid_q_string = {}
|
|
90
|
+
for k,v in q_string.items():
|
|
91
|
+
k = k.strip().strip('%').strip('?').lower()
|
|
92
|
+
if k in valid_fn_parms:
|
|
93
|
+
valid_q_string[k] = v[0]
|
|
94
|
+
else:
|
|
95
|
+
raise TypeError(f'Provided parameter is invalid: {k}')
|
|
96
|
+
|
|
97
|
+
return valid_q_string
|
|
98
|
+
|
|
99
|
+
if user_param.get('raw_query'):
|
|
100
|
+
value = user_param['raw_query']
|
|
101
|
+
if not isinstance(value,str):
|
|
102
|
+
raise TypeError('raw_query should be of type string.')
|
|
103
|
+
return validate_url(url=value)
|
|
104
|
+
|
|
105
|
+
valid_parms = {}
|
|
106
|
+
for param,value in user_param.items():
|
|
107
|
+
if value is None:
|
|
108
|
+
continue
|
|
109
|
+
|
|
110
|
+
if param in string_params:
|
|
111
|
+
if isinstance(value,list):
|
|
112
|
+
value = ','.join(value)
|
|
113
|
+
if not isinstance(value,str):
|
|
114
|
+
raise TypeError(f'{param} should be of type string.')
|
|
115
|
+
elif param in bool_params:
|
|
116
|
+
if not isinstance(value,bool):
|
|
117
|
+
raise TypeError(f'{param} should be of type bool.')
|
|
118
|
+
value = 1 if value == True else 0
|
|
119
|
+
elif param in int_params:
|
|
120
|
+
if not isinstance(value,int):
|
|
121
|
+
raise TypeError(f'{param} should be of type int.')
|
|
122
|
+
|
|
123
|
+
valid_parms[param] = value
|
|
124
|
+
|
|
125
|
+
return valid_parms
|
|
126
|
+
|
|
127
|
+
def __get_feeds(self, endpoint: str, query_params: dict) -> Dict[str, Any]:
|
|
128
|
+
for retry_count in range(1,self.max_retries+1):
|
|
129
|
+
try:
|
|
130
|
+
s_time = time.perf_counter()
|
|
131
|
+
params = query_params.copy()
|
|
132
|
+
params['apikey'] = self.apikey
|
|
133
|
+
url = f"{endpoint}?{urlencode(params, quote_via=quote)}"
|
|
134
|
+
|
|
135
|
+
logger.info(f"Fetching data from URL: {url}")
|
|
136
|
+
response = self.request_method.get(url=url, proxies=self.proxies, timeout=self.request_timeout)
|
|
137
|
+
logger.info(f"Time taken to fetch data: {time.perf_counter() - s_time:.2f} seconds")
|
|
138
|
+
|
|
139
|
+
X_API_Limit_Remaining = response.headers.get("X-API-Limit-Remaining")
|
|
140
|
+
X_RateLimit_Remaining = response.headers.get("X-RateLimit-Remaining")
|
|
141
|
+
logger.info(f"X-API-Limit-Remaining: {X_API_Limit_Remaining}, X-RateLimit-Remaining: {X_RateLimit_Remaining}")
|
|
142
|
+
|
|
143
|
+
feeds_data: dict = response.json()
|
|
144
|
+
|
|
145
|
+
if (
|
|
146
|
+
response.status_code == 200
|
|
147
|
+
and feeds_data.get('status') == 'success'
|
|
148
|
+
and (feeds_data.get('results') is not None or feeds_data.get('result') is not None)
|
|
149
|
+
):
|
|
150
|
+
if self.include_headers:
|
|
151
|
+
feeds_data['response_headers'] = dict(response.headers)
|
|
152
|
+
return feeds_data
|
|
153
|
+
|
|
154
|
+
elif response.status_code == 500:
|
|
155
|
+
logger.error(f"Encountered 'ServerError' - sleeping for {self.retry_delay}s. (Attempt {retry_count}/{self.max_retries})")
|
|
156
|
+
time.sleep(self.retry_delay)
|
|
157
|
+
continue
|
|
158
|
+
|
|
159
|
+
elif response.status_code == 429:
|
|
160
|
+
Retry_After = int(response.headers.get("Retry-After", self.retry_delay))
|
|
161
|
+
logger.error(f"Rate limit exceeded - sleeping for {Retry_After}s. (Attempt {retry_count}/{self.max_retries})")
|
|
162
|
+
time.sleep(Retry_After)
|
|
163
|
+
continue
|
|
164
|
+
|
|
165
|
+
else:
|
|
166
|
+
raise NewsdataException(feeds_data)
|
|
167
|
+
|
|
168
|
+
except NewsdataException as e:
|
|
169
|
+
raise e
|
|
170
|
+
|
|
171
|
+
except RequestException as e:
|
|
172
|
+
logger.error(f"ConnectionError on attempt {retry_count}/{self.max_retries}: {e}. Sleeping {self.retry_delay}s.")
|
|
173
|
+
time.sleep(self.retry_delay)
|
|
174
|
+
|
|
175
|
+
except Exception as e:
|
|
176
|
+
logger.error(f"Unexpected error on attempt {retry_count}/{self.max_retries}: {e}. Sleeping {self.retry_delay}s.")
|
|
177
|
+
time.sleep(self.retry_delay)
|
|
178
|
+
|
|
179
|
+
raise NewsdataException(f"Maximum retry limit reached: {self.max_retries}.")
|
|
180
|
+
|
|
181
|
+
def __get_feeds_all(self, endpoint:str,query_params:dict, max_result: Optional[int] = None) -> Dict[str, Any]:
|
|
182
|
+
|
|
183
|
+
if max_result is None:
|
|
184
|
+
max_result = self.max_result
|
|
185
|
+
|
|
186
|
+
self.request_method = requests.Session()
|
|
187
|
+
|
|
188
|
+
data = {'totalResults':None,'results':[],'nextPage':None}
|
|
189
|
+
while True:
|
|
190
|
+
response:dict = self.__get_feeds(endpoint=endpoint,query_params=query_params)
|
|
191
|
+
data['totalResults'] = response.get('totalResults')
|
|
192
|
+
data['results'].extend(response.get('results',[]))
|
|
193
|
+
data['nextPage'] = response.get('nextPage')
|
|
194
|
+
|
|
195
|
+
query_params['page'] = data['nextPage']
|
|
196
|
+
|
|
197
|
+
if self.include_headers:
|
|
198
|
+
data['response_headers'] = response.get('response_headers')
|
|
199
|
+
|
|
200
|
+
logger.info(f"Total results: {data['totalResults']} | Extracted: {len(data['results'])}")
|
|
201
|
+
|
|
202
|
+
if not data['nextPage']:
|
|
203
|
+
return data
|
|
204
|
+
|
|
205
|
+
if max_result and len(data['results']) >= max_result:
|
|
206
|
+
return data
|
|
207
|
+
|
|
208
|
+
time.sleep(self.pagination_delay)
|
|
209
|
+
|
|
210
|
+
def __paginate_results(self,endpoint:str,query_params:dict,max_pages: Optional[int] = None):
|
|
211
|
+
if max_pages is None:
|
|
212
|
+
max_pages = self.max_pages
|
|
213
|
+
|
|
214
|
+
self.request_method = requests.Session()
|
|
215
|
+
current_result_count = 0
|
|
216
|
+
page = 0
|
|
217
|
+
while True:
|
|
218
|
+
response = self.__get_feeds(endpoint=endpoint,query_params=query_params)
|
|
219
|
+
current_result_count += len(response['results'])
|
|
220
|
+
page += 1
|
|
221
|
+
logger.info(f"Total result: {response['totalResults']}, Current result count: {current_result_count}, Page: {page}")
|
|
222
|
+
yield response
|
|
223
|
+
|
|
224
|
+
if page >= max_pages:
|
|
225
|
+
logger.info(f"Reached maximum page limit: {max_pages}, ending pagination.")
|
|
226
|
+
return
|
|
227
|
+
|
|
228
|
+
if response['nextPage'] is None:
|
|
229
|
+
logger.info("No more pages to fetch, ending pagination.")
|
|
230
|
+
return
|
|
231
|
+
|
|
232
|
+
query_params['page'] = response['nextPage']
|
|
233
|
+
time.sleep(self.pagination_delay)
|
|
234
|
+
|
|
235
|
+
def news_api(
|
|
236
|
+
self,
|
|
237
|
+
q:Optional[str]=None,
|
|
238
|
+
qInTitle:Optional[str]=None,
|
|
239
|
+
country:Optional[Union[str, List[str]]]=None,
|
|
240
|
+
category:Optional[Union[str, List[str]]]=None,
|
|
241
|
+
language:Optional[Union[str, List[str]]]=None,
|
|
242
|
+
domain:Optional[Union[str, List[str]]]=None,
|
|
243
|
+
timeframe:Optional[Union[int, str]]=None,
|
|
244
|
+
size:Optional[int]=None,
|
|
245
|
+
domainurl:Optional[Union[str, List[str]]]=None,
|
|
246
|
+
excludedomain:Optional[Union[str, List[str]]]=None,
|
|
247
|
+
timezone:Optional[str]=None,
|
|
248
|
+
full_content:Optional[bool]=None,
|
|
249
|
+
image:Optional[bool]=None,
|
|
250
|
+
video:Optional[bool]=None,
|
|
251
|
+
prioritydomain:Optional[str]=None,
|
|
252
|
+
page:Optional[str]=None,
|
|
253
|
+
scroll:Optional[bool]=False,
|
|
254
|
+
max_result:Optional[int]=None,
|
|
255
|
+
qInMeta:Optional[str]=None,
|
|
256
|
+
tag:Optional[Union[str, List[str]]]=None,
|
|
257
|
+
sentiment:Optional[str]=None,
|
|
258
|
+
region:Optional[Union[str, List[str]]]=None,
|
|
259
|
+
excludefield:Optional[Union[str, List[str]]]=None,
|
|
260
|
+
removeduplicate:Optional[bool]=None,
|
|
261
|
+
raw_query:Optional[str]=None
|
|
262
|
+
)->dict:
|
|
263
|
+
"""
|
|
264
|
+
Sending GET request to the news api.
|
|
265
|
+
For more information about parameters and input, Please visit our documentation page: https://newsdata.io/documentation
|
|
266
|
+
"""
|
|
267
|
+
warn('This method is deprecated and will be removed in upcoming updates, Instead use latest_api()', DeprecationWarning, stacklevel=2)
|
|
268
|
+
params = {
|
|
269
|
+
'q':q,
|
|
270
|
+
'qInTitle':qInTitle,
|
|
271
|
+
'country':country,
|
|
272
|
+
'category':category,
|
|
273
|
+
'language':language,
|
|
274
|
+
'domain':domain,
|
|
275
|
+
'timeframe':str(timeframe) if timeframe else timeframe,
|
|
276
|
+
'size':size,
|
|
277
|
+
'domainurl':domainurl,
|
|
278
|
+
'excludedomain':excludedomain,
|
|
279
|
+
'timezone':timezone,
|
|
280
|
+
'full_content':full_content,
|
|
281
|
+
'image':image,
|
|
282
|
+
'video':video,
|
|
283
|
+
'prioritydomain':prioritydomain,
|
|
284
|
+
'page':page,
|
|
285
|
+
'qInMeta':qInMeta,
|
|
286
|
+
'tag':tag,
|
|
287
|
+
'sentiment':sentiment,
|
|
288
|
+
'region':region,
|
|
289
|
+
'excludefield':excludefield,
|
|
290
|
+
'removeduplicate':removeduplicate,
|
|
291
|
+
'raw_query':raw_query
|
|
292
|
+
}
|
|
293
|
+
URL_parameters = self.__validate_parms(user_param=params)
|
|
294
|
+
if scroll:
|
|
295
|
+
return self.__get_feeds_all(endpoint=self.latest_url,query_params=URL_parameters,max_result=max_result)
|
|
296
|
+
else:
|
|
297
|
+
return self.__get_feeds(endpoint=self.latest_url,query_params=URL_parameters)
|
|
298
|
+
|
|
299
|
+
def latest_api(
|
|
300
|
+
self,
|
|
301
|
+
q: Optional[str] = None,
|
|
302
|
+
qInTitle: Optional[str] = None,
|
|
303
|
+
qInMeta: Optional[str] = None,
|
|
304
|
+
country: Optional[Union[str, List[str]]] = None,
|
|
305
|
+
category: Optional[Union[str, List[str]]] = None,
|
|
306
|
+
language: Optional[Union[str, List[str]]] = None,
|
|
307
|
+
domain: Optional[Union[str, List[str]]] = None,
|
|
308
|
+
domainurl: Optional[Union[str, List[str]]] = None,
|
|
309
|
+
prioritydomain: Optional[str] = None,
|
|
310
|
+
excludedomain: Optional[Union[str, List[str]]] = None,
|
|
311
|
+
timeframe: Optional[Union[int, str]] = None,
|
|
312
|
+
size: Optional[int] = None,
|
|
313
|
+
timezone: Optional[str] = None,
|
|
314
|
+
full_content: Optional[bool] = None,
|
|
315
|
+
image: Optional[bool] = None,
|
|
316
|
+
video: Optional[bool] = None,
|
|
317
|
+
page: Optional[str] = None,
|
|
318
|
+
tag: Optional[Union[str, List[str]]] = None,
|
|
319
|
+
sentiment: Optional[str] = None,
|
|
320
|
+
region: Optional[Union[str, List[str]]] = None,
|
|
321
|
+
excludefield: Optional[Union[str, List[str]]] = None,
|
|
322
|
+
removeduplicate: Optional[bool] = None,
|
|
323
|
+
excludecategory: Optional[Union[str, List[str]]] = None,
|
|
324
|
+
id: Optional[str] = None,
|
|
325
|
+
excludelanguage: Optional[Union[str, List[str]]] = None,
|
|
326
|
+
organization: Optional[str] = None,
|
|
327
|
+
url: Optional[str] = None,
|
|
328
|
+
sort: Optional[str] = None,
|
|
329
|
+
|
|
330
|
+
raw_query: Optional[str] = None,
|
|
331
|
+
max_result: Optional[int] = None,
|
|
332
|
+
scroll: Optional[bool] = False,
|
|
333
|
+
paginate: Optional[bool] = False,
|
|
334
|
+
max_pages: Optional[int] = None,
|
|
335
|
+
) -> Dict[str, Any]:
|
|
336
|
+
"""
|
|
337
|
+
Sending GET request to the latest api.
|
|
338
|
+
For more information about parameters and input, Please visit our documentation page: https://newsdata.io/documentation
|
|
339
|
+
"""
|
|
340
|
+
params = {
|
|
341
|
+
'q':q,
|
|
342
|
+
'qInTitle':qInTitle,
|
|
343
|
+
'country':country,
|
|
344
|
+
'category':category,
|
|
345
|
+
'language':language,
|
|
346
|
+
'domain':domain,
|
|
347
|
+
'timeframe':str(timeframe) if timeframe else timeframe,
|
|
348
|
+
'size':size,
|
|
349
|
+
'domainurl':domainurl,
|
|
350
|
+
'excludedomain':excludedomain,
|
|
351
|
+
'timezone':timezone,
|
|
352
|
+
'full_content':full_content,
|
|
353
|
+
'image':image,
|
|
354
|
+
'video':video,
|
|
355
|
+
'prioritydomain':prioritydomain,
|
|
356
|
+
'page':page,
|
|
357
|
+
'qInMeta':qInMeta,
|
|
358
|
+
'tag':tag,
|
|
359
|
+
'sentiment':sentiment,
|
|
360
|
+
'region':region,
|
|
361
|
+
'excludefield':excludefield,
|
|
362
|
+
'removeduplicate':removeduplicate,
|
|
363
|
+
'raw_query':raw_query,
|
|
364
|
+
'excludecategory':excludecategory,
|
|
365
|
+
'id':id,
|
|
366
|
+
'excludelanguage':excludelanguage,
|
|
367
|
+
'organization':organization,
|
|
368
|
+
'url':url,
|
|
369
|
+
'sort':sort
|
|
370
|
+
}
|
|
371
|
+
if scroll and paginate:
|
|
372
|
+
raise NewsdataException("Both 'scroll' and 'paginate' cannot be True at the same time.")
|
|
373
|
+
|
|
374
|
+
URL_parameters = self.__validate_parms(user_param=params)
|
|
375
|
+
if scroll:
|
|
376
|
+
return self.__get_feeds_all(endpoint=self.latest_url,query_params=URL_parameters,max_result=max_result)
|
|
377
|
+
elif paginate:
|
|
378
|
+
return self.__paginate_results(endpoint=self.latest_url,query_params=URL_parameters,max_pages=max_pages)
|
|
379
|
+
else:
|
|
380
|
+
return self.__get_feeds(endpoint=self.latest_url,query_params=URL_parameters)
|
|
381
|
+
|
|
382
|
+
def archive_api(
|
|
383
|
+
self,
|
|
384
|
+
q: Optional[str] = None,
|
|
385
|
+
qInTitle: Optional[str] = None,
|
|
386
|
+
country: Optional[Union[str, List[str]]] = None,
|
|
387
|
+
category: Optional[Union[str, List[str]]] = None,
|
|
388
|
+
language: Optional[Union[str, List[str]]] = None,
|
|
389
|
+
domain: Optional[Union[str, List[str]]] = None,
|
|
390
|
+
size: Optional[int] = None,
|
|
391
|
+
domainurl: Optional[Union[str, List[str]]] = None,
|
|
392
|
+
excludedomain: Optional[Union[str, List[str]]] = None,
|
|
393
|
+
timezone: Optional[str] = None,
|
|
394
|
+
full_content: Optional[bool] = None,
|
|
395
|
+
image: Optional[bool] = None,
|
|
396
|
+
video: Optional[bool] = None,
|
|
397
|
+
prioritydomain: Optional[str] = None,
|
|
398
|
+
page: Optional[str] = None,
|
|
399
|
+
from_date: Optional[str] = None,
|
|
400
|
+
to_date: Optional[str] = None,
|
|
401
|
+
qInMeta: Optional[str] = None,
|
|
402
|
+
excludefield: Optional[Union[str, List[str]]] = None,
|
|
403
|
+
excludecategory: Optional[Union[str, List[str]]] = None,
|
|
404
|
+
id: Optional[str] = None,
|
|
405
|
+
excludelanguage: Optional[Union[str, List[str]]] = None,
|
|
406
|
+
url: Optional[str] = None,
|
|
407
|
+
sort: Optional[str] = None,
|
|
408
|
+
|
|
409
|
+
raw_query: Optional[str] = None,
|
|
410
|
+
scroll: Optional[bool] = False,
|
|
411
|
+
max_result: Optional[int] = None,
|
|
412
|
+
paginate: Optional[bool] = False,
|
|
413
|
+
max_pages: Optional[int] = None,
|
|
414
|
+
) -> Dict[str, Any]:
|
|
415
|
+
"""
|
|
416
|
+
Sending GET request to the archive api
|
|
417
|
+
For more information about parameters and input, Please visit our documentation page: https://newsdata.io/documentation
|
|
418
|
+
"""
|
|
419
|
+
params = {
|
|
420
|
+
'q':q,
|
|
421
|
+
'qInTitle':qInTitle,
|
|
422
|
+
'country':country,
|
|
423
|
+
'category':category,
|
|
424
|
+
'language':language,
|
|
425
|
+
'domain':domain,
|
|
426
|
+
'size':size,
|
|
427
|
+
'domainurl':domainurl,
|
|
428
|
+
'excludedomain':excludedomain,
|
|
429
|
+
'timezone':timezone,
|
|
430
|
+
'full_content':full_content,
|
|
431
|
+
'image':image,
|
|
432
|
+
'video':video,
|
|
433
|
+
'prioritydomain':prioritydomain,
|
|
434
|
+
'page':page,
|
|
435
|
+
'from_date':from_date,
|
|
436
|
+
'to_date':to_date,
|
|
437
|
+
'qInMeta':qInMeta,
|
|
438
|
+
'excludefield':excludefield,
|
|
439
|
+
'raw_query':raw_query,
|
|
440
|
+
'excludecategory':excludecategory,
|
|
441
|
+
'id':id,
|
|
442
|
+
'excludelanguage':excludelanguage,
|
|
443
|
+
'url':url,
|
|
444
|
+
'sort':sort
|
|
445
|
+
}
|
|
446
|
+
if scroll and paginate:
|
|
447
|
+
raise NewsdataException("Both 'scroll' and 'paginate' cannot be True at the same time.")
|
|
448
|
+
|
|
449
|
+
URL_parameters = self.__validate_parms(user_param=params)
|
|
450
|
+
if scroll:
|
|
451
|
+
return self.__get_feeds_all(endpoint=self.archive_url,query_params=URL_parameters,max_result=max_result)
|
|
452
|
+
elif paginate:
|
|
453
|
+
return self.__paginate_results(endpoint=self.archive_url,query_params=URL_parameters,max_pages=max_pages)
|
|
454
|
+
else:
|
|
455
|
+
return self.__get_feeds(endpoint=self.archive_url,query_params=URL_parameters)
|
|
456
|
+
|
|
457
|
+
def sources_api(
|
|
458
|
+
self,
|
|
459
|
+
country: Optional[Union[str, List[str]]]= None,
|
|
460
|
+
category: Optional[Union[str, List[str]]] = None,
|
|
461
|
+
language: Optional[Union[str, List[str]]] = None,
|
|
462
|
+
prioritydomain: Optional[str] = None,
|
|
463
|
+
domainurl: Optional[Union[str, List[str]]] = None,
|
|
464
|
+
|
|
465
|
+
raw_query: Optional[str] = None
|
|
466
|
+
) -> Dict[str, Any]:
|
|
467
|
+
"""
|
|
468
|
+
Sending GET request to the sources api
|
|
469
|
+
For more information about parameters and input, Please visit our documentation page: https://newsdata.io/documentation
|
|
470
|
+
"""
|
|
471
|
+
params = {
|
|
472
|
+
'country':country,
|
|
473
|
+
'category':category,
|
|
474
|
+
'language':language,
|
|
475
|
+
'prioritydomain':prioritydomain,
|
|
476
|
+
'domainurl':domainurl,
|
|
477
|
+
'raw_query':raw_query
|
|
478
|
+
}
|
|
479
|
+
URL_parameters = self.__validate_parms(user_param=params)
|
|
480
|
+
return self.__get_feeds(endpoint=self.sources_url,query_params=URL_parameters)
|
|
481
|
+
|
|
482
|
+
def crypto_api(
|
|
483
|
+
self,
|
|
484
|
+
q: Optional[str] = None,
|
|
485
|
+
qInTitle: Optional[str] = None,
|
|
486
|
+
language: Optional[Union[str, List[str]]] = None,
|
|
487
|
+
domain: Optional[Union[str, List[str]]] = None,
|
|
488
|
+
timeframe: Optional[Union[int, str]] = None,
|
|
489
|
+
size: Optional[int] = None,
|
|
490
|
+
domainurl: Optional[Union[str, List[str]]] = None,
|
|
491
|
+
excludedomain: Optional[Union[str, List[str]]] = None,
|
|
492
|
+
timezone: Optional[str] = None,
|
|
493
|
+
full_content: Optional[bool] = None,
|
|
494
|
+
image: Optional[bool] = None,
|
|
495
|
+
video: Optional[bool] = None,
|
|
496
|
+
prioritydomain: Optional[str] = None,
|
|
497
|
+
page: Optional[str] = None,
|
|
498
|
+
qInMeta: Optional[str] = None,
|
|
499
|
+
tag: Optional[Union[str, List[str]]] = None,
|
|
500
|
+
sentiment: Optional[str] = None,
|
|
501
|
+
coin: Optional[Union[str, List[str]]] = None,
|
|
502
|
+
excludefield: Optional[Union[str, List[str]]] = None,
|
|
503
|
+
from_date: Optional[str] = None,
|
|
504
|
+
to_date: Optional[str] = None,
|
|
505
|
+
removeduplicate: Optional[bool] = None,
|
|
506
|
+
raw_query: Optional[str] = None,
|
|
507
|
+
id: Optional[str] = None,
|
|
508
|
+
excludelanguage: Optional[Union[str, List[str]]] = None,
|
|
509
|
+
url: Optional[str] = None,
|
|
510
|
+
sort: Optional[str] = None,
|
|
511
|
+
|
|
512
|
+
scroll: Optional[bool] = False,
|
|
513
|
+
max_result: Optional[int] = None,
|
|
514
|
+
paginate: Optional[bool] = False,
|
|
515
|
+
max_pages: Optional[int] = None,
|
|
516
|
+
) -> Dict[str, Any]:
|
|
517
|
+
"""
|
|
518
|
+
Sending GET request to the crypto api
|
|
519
|
+
For more information about parameters and input, Please visit our documentation page: https://newsdata.io/documentation
|
|
520
|
+
"""
|
|
521
|
+
|
|
522
|
+
params = {
|
|
523
|
+
'q':q,
|
|
524
|
+
'qInTitle':qInTitle,
|
|
525
|
+
'language':language,
|
|
526
|
+
'domain':domain,
|
|
527
|
+
'size':size,
|
|
528
|
+
'domainurl':domainurl,
|
|
529
|
+
'excludedomain':excludedomain,
|
|
530
|
+
'timezone':timezone,
|
|
531
|
+
'full_content':full_content,
|
|
532
|
+
'image':image,
|
|
533
|
+
'video':video,
|
|
534
|
+
'prioritydomain':prioritydomain,
|
|
535
|
+
'page':page,
|
|
536
|
+
'timeframe':str(timeframe) if timeframe else timeframe,
|
|
537
|
+
'qInMeta':qInMeta,
|
|
538
|
+
'tag':tag,
|
|
539
|
+
'sentiment':sentiment,
|
|
540
|
+
'coin':coin,
|
|
541
|
+
'excludefield':excludefield,
|
|
542
|
+
'from_date':from_date,
|
|
543
|
+
'to_date':to_date,
|
|
544
|
+
'removeduplicate':removeduplicate,
|
|
545
|
+
'raw_query':raw_query,
|
|
546
|
+
'id':id,
|
|
547
|
+
'excludelanguage':excludelanguage,
|
|
548
|
+
'url':url,
|
|
549
|
+
'sort':sort
|
|
550
|
+
}
|
|
551
|
+
if scroll and paginate:
|
|
552
|
+
raise NewsdataException("Both 'scroll' and 'paginate' cannot be True at the same time.")
|
|
553
|
+
|
|
554
|
+
URL_parameters = self.__validate_parms(user_param=params)
|
|
555
|
+
if scroll:
|
|
556
|
+
return self.__get_feeds_all(endpoint=self.crypto_url,query_params=URL_parameters,max_result=max_result)
|
|
557
|
+
elif paginate:
|
|
558
|
+
return self.__paginate_results(endpoint=self.crypto_url,query_params=URL_parameters,max_pages=max_pages)
|
|
559
|
+
else:
|
|
560
|
+
return self.__get_feeds(endpoint=self.crypto_url,query_params=URL_parameters)
|
|
561
|
+
|
|
562
|
+
def count_api(
|
|
563
|
+
self,
|
|
564
|
+
q: Optional[str] = None,
|
|
565
|
+
qInTitle: Optional[str] = None,
|
|
566
|
+
qInMeta: Optional[str] = None,
|
|
567
|
+
country: Optional[Union[str, List[str]]] = None,
|
|
568
|
+
category: Optional[Union[str, List[str]]] = None,
|
|
569
|
+
language: Optional[Union[str, List[str]]] = None,
|
|
570
|
+
from_date: Optional[str] = None,
|
|
571
|
+
to_date: Optional[str] = None,
|
|
572
|
+
|
|
573
|
+
raw_query: Optional[str] = None,
|
|
574
|
+
) -> Dict[str, Any]:
|
|
575
|
+
"""
|
|
576
|
+
Sending GET request to the count api
|
|
577
|
+
For more information about parameters and input, Please visit our documentation page: https://newsdata.io/documentation
|
|
578
|
+
"""
|
|
579
|
+
params = {
|
|
580
|
+
'q':q,
|
|
581
|
+
'qInTitle':qInTitle,
|
|
582
|
+
'country':country,
|
|
583
|
+
'category':category,
|
|
584
|
+
'language':language,
|
|
585
|
+
'from_date':from_date,
|
|
586
|
+
'to_date':to_date,
|
|
587
|
+
'qInMeta':qInMeta,
|
|
588
|
+
'raw_query':raw_query
|
|
589
|
+
}
|
|
590
|
+
URL_parameters = self.__validate_parms(user_param=params)
|
|
591
|
+
return self.__get_feeds(endpoint=self.count_url,query_params=URL_parameters)
|
|
592
|
+
|
|
593
|
+
def crypto_count_api(
|
|
594
|
+
self,
|
|
595
|
+
q: Optional[str] = None,
|
|
596
|
+
qInTitle: Optional[str] = None,
|
|
597
|
+
qInMeta: Optional[str] = None,
|
|
598
|
+
language: Optional[Union[str, List[str]]] = None,
|
|
599
|
+
from_date: Optional[str] = None,
|
|
600
|
+
to_date: Optional[str] = None,
|
|
601
|
+
coin: Optional[Union[str, List[str]]] = None,
|
|
602
|
+
|
|
603
|
+
raw_query: Optional[str] = None,
|
|
604
|
+
) -> Dict[str, Any]:
|
|
605
|
+
"""
|
|
606
|
+
Sending GET request to the crypto count api
|
|
607
|
+
For more information about parameters and input, Please visit our documentation page: https://newsdata.io/documentation
|
|
608
|
+
"""
|
|
609
|
+
params = {
|
|
610
|
+
'q':q,
|
|
611
|
+
'qInTitle':qInTitle,
|
|
612
|
+
'language':language,
|
|
613
|
+
'from_date':from_date,
|
|
614
|
+
'to_date':to_date,
|
|
615
|
+
'coin':coin,
|
|
616
|
+
'qInMeta':qInMeta,
|
|
617
|
+
'raw_query':raw_query
|
|
618
|
+
}
|
|
619
|
+
URL_parameters = self.__validate_parms(user_param=params)
|
|
620
|
+
return self.__get_feeds(endpoint=self.crypto_count_url,query_params=URL_parameters)
|
|
621
|
+
|
|
622
|
+
def market_api(
|
|
623
|
+
self,
|
|
624
|
+
q: Optional[str] = None,
|
|
625
|
+
qInTitle: Optional[str] = None,
|
|
626
|
+
qInMeta: Optional[str] = None,
|
|
627
|
+
from_date: Optional[str] = None,
|
|
628
|
+
to_date: Optional[str] = None,
|
|
629
|
+
domain: Optional[str] = None,
|
|
630
|
+
language: Optional[Union[str, List[str]]] = None,
|
|
631
|
+
page: Optional[str] = None,
|
|
632
|
+
full_content: Optional[bool] = None,
|
|
633
|
+
image: Optional[bool] = None,
|
|
634
|
+
video: Optional[bool] = None,
|
|
635
|
+
timeframe: Optional[Union[int, str]] = None,
|
|
636
|
+
prioritydomain: Optional[str] = None,
|
|
637
|
+
timezone: Optional[str] = None,
|
|
638
|
+
size: Optional[int] = None,
|
|
639
|
+
domainurl: Optional[Union[str, List[str]]] = None,
|
|
640
|
+
excludedomain: Optional[Union[str, List[str]]] = None,
|
|
641
|
+
tag: Optional[Union[str, List[str]]] = None,
|
|
642
|
+
sentiment: Optional[str] = None,
|
|
643
|
+
id: Optional[str] = None,
|
|
644
|
+
excludefield: Optional[Union[str, List[str]]] = None,
|
|
645
|
+
removeduplicate: Optional[bool] = None,
|
|
646
|
+
webhook: Optional[str] = None,
|
|
647
|
+
excludelanguage: Optional[Union[str, List[str]]] = None,
|
|
648
|
+
organization: Optional[str] = None,
|
|
649
|
+
url: Optional[str] = None,
|
|
650
|
+
sort: Optional[str] = None,
|
|
651
|
+
symbol: Optional[str] = None,
|
|
652
|
+
country: Optional[Union[str, List[str]]] = None,
|
|
653
|
+
|
|
654
|
+
max_result: Optional[int] = None,
|
|
655
|
+
scroll: Optional[bool] = False,
|
|
656
|
+
paginate: Optional[bool] = False,
|
|
657
|
+
max_pages: Optional[int] = None,
|
|
658
|
+
) -> Dict[str, Any]:
|
|
659
|
+
"""
|
|
660
|
+
Sending GET request to the market api
|
|
661
|
+
For more information about parameters and input, Please visit our documentation page: https://newsdata.io/documentation
|
|
662
|
+
"""
|
|
663
|
+
params = {
|
|
664
|
+
'domain': domain,
|
|
665
|
+
'language': language,
|
|
666
|
+
'page': page,
|
|
667
|
+
'q': q,
|
|
668
|
+
'qInTitle': qInTitle,
|
|
669
|
+
'qInMeta': qInMeta,
|
|
670
|
+
'from_date': from_date,
|
|
671
|
+
'to_date': to_date,
|
|
672
|
+
'full_content': full_content,
|
|
673
|
+
'image': image,
|
|
674
|
+
'video': video,
|
|
675
|
+
'timeframe': str(timeframe) if timeframe else timeframe,
|
|
676
|
+
'prioritydomain': prioritydomain,
|
|
677
|
+
'timezone': timezone,
|
|
678
|
+
'size': size,
|
|
679
|
+
'domainurl': domainurl,
|
|
680
|
+
'excludedomain': excludedomain,
|
|
681
|
+
'tag': tag,
|
|
682
|
+
'sentiment': sentiment,
|
|
683
|
+
'id': id,
|
|
684
|
+
'excludefield': excludefield,
|
|
685
|
+
'removeduplicate': removeduplicate,
|
|
686
|
+
'webhook': webhook,
|
|
687
|
+
'excludelanguage': excludelanguage,
|
|
688
|
+
'organization': organization,
|
|
689
|
+
'url': url,
|
|
690
|
+
'sort': sort,
|
|
691
|
+
'symbol': symbol,
|
|
692
|
+
'country': country
|
|
693
|
+
}
|
|
694
|
+
if scroll and paginate:
|
|
695
|
+
raise NewsdataException("Both 'scroll' and 'paginate' cannot be True at the same time.")
|
|
696
|
+
|
|
697
|
+
URL_parameters = self.__validate_parms(user_param=params)
|
|
698
|
+
if scroll:
|
|
699
|
+
return self.__get_feeds_all(endpoint=self.market_url,query_params=URL_parameters,max_result=max_result)
|
|
700
|
+
elif paginate:
|
|
701
|
+
return self.__paginate_results(endpoint=self.market_url,query_params=URL_parameters,max_pages=max_pages)
|
|
702
|
+
else:
|
|
703
|
+
return self.__get_feeds(endpoint=self.market_url,query_params=URL_parameters)
|
|
704
|
+
|
|
705
|
+
def __del__(self):
|
|
706
|
+
if isinstance(self.request_method,requests.Session):
|
|
707
|
+
self.request_method.close()
|
|
@@ -39,3 +39,7 @@ class test_newsdataapi(unittest.TestCase):
|
|
|
39
39
|
def test_crypto_count_api(self):
|
|
40
40
|
response = self.api.crypto_count_api(q='bitcoin',language='en')
|
|
41
41
|
self.assertEqual(response['status'], "success")
|
|
42
|
+
|
|
43
|
+
def test_market_api(self):
|
|
44
|
+
response = self.api.market_api()
|
|
45
|
+
self.assertEqual(response['status'], "success")
|
|
@@ -1,346 +0,0 @@
|
|
|
1
|
-
import requests,time
|
|
2
|
-
from warnings import warn
|
|
3
|
-
from newsdataapi import constants
|
|
4
|
-
from typing import Optional,Union
|
|
5
|
-
from datetime import datetime,timezone
|
|
6
|
-
from newsdataapi.helpers import FileHandler
|
|
7
|
-
from requests.exceptions import RequestException
|
|
8
|
-
from newsdataapi.newsdataapi_exception import NewsdataException
|
|
9
|
-
from urllib.parse import urlencode, quote,urlparse,parse_qs,urljoin
|
|
10
|
-
|
|
11
|
-
class NewsDataApiClient(FileHandler):
|
|
12
|
-
|
|
13
|
-
def __init__(
|
|
14
|
-
self, apikey:str, session:bool= False, max_retries:int= constants.DEFAULT_MAX_RETRIES, retry_delay:int= constants.DEFAULT_RETRY_DELAY,
|
|
15
|
-
proxies:Optional[dict]=None, request_timeout:int= constants.DEFAULT_REQUEST_TIMEOUT,max_result:int=10**10, debug:Optional[bool]=False,
|
|
16
|
-
folder_path:str=None,include_headers:bool=False
|
|
17
|
-
):
|
|
18
|
-
"""Initializes newsdata client object for access Newsdata APIs."""
|
|
19
|
-
self.apikey = apikey
|
|
20
|
-
self.request_method:requests = requests if session == False else requests.Session()
|
|
21
|
-
self.max_result = max_result
|
|
22
|
-
self.max_retries = max_retries
|
|
23
|
-
self.retry_delay = retry_delay
|
|
24
|
-
self.proxies = proxies
|
|
25
|
-
self.request_timeout = request_timeout
|
|
26
|
-
self.is_debug = debug
|
|
27
|
-
self.include_headers = include_headers
|
|
28
|
-
self.set_base_url()
|
|
29
|
-
super().__init__(folder_path=folder_path)
|
|
30
|
-
|
|
31
|
-
def set_base_url(self,new_base_url:str=constants.BASE_URL)->None:
|
|
32
|
-
self.latest_url = urljoin(new_base_url,constants.LATEST_ENDPOINT)
|
|
33
|
-
self.archive_url = urljoin(new_base_url,constants.ARCHIVE_ENDPOINT)
|
|
34
|
-
self.crypto_url = urljoin(new_base_url,constants.CRYPTO_ENDPOINT)
|
|
35
|
-
self.sources_url = urljoin(new_base_url,constants.SOURCES_ENDPOINT)
|
|
36
|
-
self.count_url = urljoin(new_base_url,constants.COUNT_ENDPOINT)
|
|
37
|
-
self.crypto_count_url = urljoin(new_base_url,constants.CRYPTO_COUNT_ENDPOINT)
|
|
38
|
-
|
|
39
|
-
def set_retries( self, max_retries:int, retry_delay:int)->None:
|
|
40
|
-
""" API maximum retry and delay"""
|
|
41
|
-
self.max_retries = max_retries
|
|
42
|
-
self.retry_delay = retry_delay
|
|
43
|
-
|
|
44
|
-
def set_request_timeout( self, request_timeout:int)->None:
|
|
45
|
-
""" API maximum timeout for the request """
|
|
46
|
-
self.request_timeout = request_timeout
|
|
47
|
-
|
|
48
|
-
def get_current_dt(self)->str:
|
|
49
|
-
return datetime.now(tz=timezone.utc).replace(microsecond=0).strftime("%Y-%m-%d %H:%M:%S")
|
|
50
|
-
|
|
51
|
-
def api_proxies( self, proxies:dict)->None:
|
|
52
|
-
""" Configure Proxie dictionary """
|
|
53
|
-
self.proxies = proxies
|
|
54
|
-
|
|
55
|
-
def __validate_parms(self,user_param:dict)->dict:
|
|
56
|
-
bool_params = {'full_content','image','video','removeduplicate'}
|
|
57
|
-
int_params = {'size'}
|
|
58
|
-
string_params = {
|
|
59
|
-
'q','qInTitle','country','category','language','domain','domainurl','excludedomain','timezone','page',
|
|
60
|
-
'from_date','to_date','apikey','qInMeta','prioritydomain','timeframe','tag','sentiment','region','coin',
|
|
61
|
-
'excludefield','excludecategory','id'
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
def validate_url(url:str)-> str:
|
|
65
|
-
valid_fn_parms = {k.lower() for k in user_param.keys()}
|
|
66
|
-
parsed_url = urlparse(url)
|
|
67
|
-
if parsed_url.netloc:
|
|
68
|
-
q_string = parse_qs(parsed_url.query)
|
|
69
|
-
else:
|
|
70
|
-
q_string = parse_qs(url)
|
|
71
|
-
|
|
72
|
-
valid_q_string = {}
|
|
73
|
-
for k,v in q_string.items():
|
|
74
|
-
k = k.strip().strip('%').strip('?').lower()
|
|
75
|
-
if k in valid_fn_parms:
|
|
76
|
-
valid_q_string[k] = v[0]
|
|
77
|
-
else:
|
|
78
|
-
raise TypeError(f'Provided parameter is invalid: {k}')
|
|
79
|
-
|
|
80
|
-
if not valid_q_string.get('apikey'):
|
|
81
|
-
valid_q_string['apikey'] = self.apikey
|
|
82
|
-
|
|
83
|
-
return valid_q_string
|
|
84
|
-
|
|
85
|
-
if user_param.get('raw_query'):
|
|
86
|
-
value = user_param['raw_query']
|
|
87
|
-
if not isinstance(value,str):
|
|
88
|
-
raise TypeError('raw_query should be of type string.')
|
|
89
|
-
return validate_url(url=value)
|
|
90
|
-
|
|
91
|
-
valid_parms = {}
|
|
92
|
-
for param,value in user_param.items():
|
|
93
|
-
if not value:continue
|
|
94
|
-
if param in string_params:
|
|
95
|
-
if isinstance(value,list):
|
|
96
|
-
value = ','.join(value)
|
|
97
|
-
if not isinstance(value,str):
|
|
98
|
-
raise TypeError(f'{param} should be of type string.')
|
|
99
|
-
elif param in bool_params:
|
|
100
|
-
if not isinstance(value,bool):
|
|
101
|
-
raise TypeError(f'{param} should be of type bool.')
|
|
102
|
-
value = 1 if value == True else 0
|
|
103
|
-
elif param in int_params:
|
|
104
|
-
if not isinstance(value,int):
|
|
105
|
-
raise TypeError(f'{param} should be of type int.')
|
|
106
|
-
|
|
107
|
-
valid_parms[param] = value
|
|
108
|
-
|
|
109
|
-
return valid_parms
|
|
110
|
-
|
|
111
|
-
def __get_feeds(self,url:str,retry_count:int=None)-> dict:
|
|
112
|
-
try:
|
|
113
|
-
if retry_count is None:
|
|
114
|
-
retry_count = self.max_retries
|
|
115
|
-
|
|
116
|
-
if retry_count <= 0:
|
|
117
|
-
raise NewsdataException('Maximum retry limit reached. For more information use debug parameter while initializing NewsDataApiClient.')
|
|
118
|
-
|
|
119
|
-
response = self.request_method.get(url=url,proxies=self.proxies,timeout=self.request_timeout)
|
|
120
|
-
headers = dict(response.headers)
|
|
121
|
-
|
|
122
|
-
if self.is_debug == True:
|
|
123
|
-
print(f'Debug | {self.get_current_dt()} | x_rate_limit_remaining: {headers.get("x_rate_limit_remaining")} | x_api_limit_remaining: {headers.get("x_api_limit_remaining")}')
|
|
124
|
-
|
|
125
|
-
feeds_data:dict = response.json()
|
|
126
|
-
if self.include_headers == True:
|
|
127
|
-
feeds_data.update({'response_headers':headers})
|
|
128
|
-
|
|
129
|
-
if response.status_code != 200:
|
|
130
|
-
|
|
131
|
-
if response.status_code == 500:
|
|
132
|
-
if self.is_debug == True:
|
|
133
|
-
print(f"Debug | {self.get_current_dt()} | Encountered 'ServerError' going to sleep for: {self.retry_delay} seconds.")
|
|
134
|
-
time.sleep(self.retry_delay)
|
|
135
|
-
return self.__get_feeds(url=url,retry_count=retry_count-1)
|
|
136
|
-
|
|
137
|
-
elif feeds_data.get('results',{}).get('code') == 'TooManyRequests':
|
|
138
|
-
if self.is_debug == True:
|
|
139
|
-
print(f"Debug | {self.get_current_dt()} | Encountered 'TooManyRequests' going to sleep for: {constants.DEFAULT_RETRY_DELAY_TooManyRequests} seconds.")
|
|
140
|
-
time.sleep(constants.DEFAULT_RETRY_DELAY_TooManyRequests)
|
|
141
|
-
return self.__get_feeds(url=url,retry_count=retry_count-1)
|
|
142
|
-
|
|
143
|
-
elif feeds_data.get('results',{}).get('code') == 'RateLimitExceeded':
|
|
144
|
-
if self.is_debug == True:
|
|
145
|
-
print(f"Debug | {self.get_current_dt()} | Encountered 'RateLimitExceeded' going to sleep for: {constants.DEFAULT_RETRY_DELAY_RateLimitExceeded} seconds.")
|
|
146
|
-
time.sleep(constants.DEFAULT_RETRY_DELAY_RateLimitExceeded)
|
|
147
|
-
return self.__get_feeds(url=url,retry_count=retry_count-1)
|
|
148
|
-
|
|
149
|
-
else:
|
|
150
|
-
raise NewsdataException(response.json())
|
|
151
|
-
|
|
152
|
-
else:
|
|
153
|
-
return feeds_data
|
|
154
|
-
|
|
155
|
-
except RequestException:
|
|
156
|
-
|
|
157
|
-
if self.is_debug == True:
|
|
158
|
-
print(f"Debug | {self.get_current_dt()} | Encountered 'ConnectionError' going to sleep for: {self.retry_delay} seconds.")
|
|
159
|
-
time.sleep(self.retry_delay)
|
|
160
|
-
|
|
161
|
-
if isinstance(self.request_method,requests.Session):
|
|
162
|
-
self.request_method = requests.Session()
|
|
163
|
-
|
|
164
|
-
return self.__get_feeds(url=url,retry_count=retry_count-1)
|
|
165
|
-
|
|
166
|
-
def __get_feeds_all(self,url:str,max_result:int)-> dict:
|
|
167
|
-
|
|
168
|
-
if max_result is None:
|
|
169
|
-
max_result = self.max_result
|
|
170
|
-
|
|
171
|
-
if not isinstance(max_result,int):
|
|
172
|
-
raise TypeError('max_result should be of type int.')
|
|
173
|
-
|
|
174
|
-
if not isinstance(self.request_method,requests.Session):
|
|
175
|
-
self.request_method = requests.Session()
|
|
176
|
-
|
|
177
|
-
feeds_count = 0
|
|
178
|
-
data = {'totalResults':None,'results':[],'nextPage':True}
|
|
179
|
-
while data.get("nextPage"):
|
|
180
|
-
try:
|
|
181
|
-
response = self.__get_feeds(url=f'{url}&page={data.get("nextPage")}' if data.get('results') else url)
|
|
182
|
-
except NewsdataException as e:
|
|
183
|
-
if data['totalResults'] is None:
|
|
184
|
-
raise e
|
|
185
|
-
return data
|
|
186
|
-
data['totalResults'] = response.get('totalResults')
|
|
187
|
-
results = response.get('results')
|
|
188
|
-
data['results'].extend(results)
|
|
189
|
-
data['nextPage'] = response.get('nextPage')
|
|
190
|
-
if self.include_headers:
|
|
191
|
-
data['response_headers'] = response.get('response_headers')
|
|
192
|
-
feeds_count+=len(results)
|
|
193
|
-
if self.is_debug == True:
|
|
194
|
-
print(f"Debug | {self.get_current_dt()} | total results: {data['totalResults']} | extracted: {feeds_count}")
|
|
195
|
-
if feeds_count >= max_result:
|
|
196
|
-
return data
|
|
197
|
-
time.sleep(0.5)
|
|
198
|
-
return data
|
|
199
|
-
|
|
200
|
-
def news_api(
|
|
201
|
-
self, q:Optional[str]=None, qInTitle:Optional[str]=None, country:Optional[Union[str, list]]=None, category:Optional[Union[str, list]]=None,
|
|
202
|
-
language:Optional[Union[str, list]]=None, domain:Optional[Union[str, list]]=None, timeframe:Optional[Union[int,str]]=None, size:Optional[int]=None,
|
|
203
|
-
domainurl:Optional[Union[str, list]]=None, excludedomain:Optional[Union[str, list]]=None, timezone:Optional[str]=None, full_content:Optional[bool]=None,
|
|
204
|
-
image:Optional[bool]=None, video:Optional[bool]=None, prioritydomain:Optional[str]=None, page:Optional[str]=None, scroll:Optional[bool]=False,
|
|
205
|
-
max_result:Optional[int]=None, qInMeta:Optional[str]=None, tag:Optional[Union[str,list]]=None, sentiment:Optional[str]=None,
|
|
206
|
-
region:Optional[Union[str,list]]=None,excludefield:Optional[Union[str,list]]=None,removeduplicate:Optional[bool]=None,raw_query:Optional[str]=None
|
|
207
|
-
)->dict:
|
|
208
|
-
"""
|
|
209
|
-
Sending GET request to the news api.
|
|
210
|
-
For more information about parameters and input, Please visit our documentation page: https://newsdata.io/documentation
|
|
211
|
-
"""
|
|
212
|
-
warn('This method is deprecated and will be removed in upcoming updates, Instead use latest_api()', DeprecationWarning, stacklevel=2)
|
|
213
|
-
params = {
|
|
214
|
-
'apikey':self.apikey,'q':q,'qInTitle':qInTitle,'country':country,'category':category,'language':language,'domain':domain,'timeframe':str(timeframe) if timeframe else timeframe,
|
|
215
|
-
'size':size,'domainurl':domainurl,'excludedomain':excludedomain,'timezone':timezone,'full_content':full_content,'image':image,'video':video,'prioritydomain':prioritydomain,
|
|
216
|
-
'page':page,'qInMeta':qInMeta,'tag':tag, 'sentiment':sentiment, 'region':region,'excludefield':excludefield,'removeduplicate':removeduplicate,'raw_query':raw_query
|
|
217
|
-
}
|
|
218
|
-
URL_parameters = self.__validate_parms(user_param=params)
|
|
219
|
-
URL_parameters_encoded = urlencode(URL_parameters, quote_via=quote)
|
|
220
|
-
if scroll == True:
|
|
221
|
-
return self.__get_feeds_all(url=f'{self.latest_url}?{URL_parameters_encoded}',max_result=max_result)
|
|
222
|
-
else:
|
|
223
|
-
return self.__get_feeds(url=f'{self.latest_url}?{URL_parameters_encoded}')
|
|
224
|
-
|
|
225
|
-
def latest_api(
|
|
226
|
-
self, q:Optional[str]=None, qInTitle:Optional[str]=None, country:Optional[Union[str, list]]=None, category:Optional[Union[str, list]]=None,
|
|
227
|
-
language:Optional[Union[str, list]]=None, domain:Optional[Union[str, list]]=None, timeframe:Optional[Union[int,str]]=None, size:Optional[int]=None,
|
|
228
|
-
domainurl:Optional[Union[str, list]]=None, excludedomain:Optional[Union[str, list]]=None, timezone:Optional[str]=None, full_content:Optional[bool]=None,
|
|
229
|
-
image:Optional[bool]=None, video:Optional[bool]=None, prioritydomain:Optional[str]=None, page:Optional[str]=None, scroll:Optional[bool]=False,
|
|
230
|
-
max_result:Optional[int]=None, qInMeta:Optional[str]=None, tag:Optional[Union[str,list]]=None, sentiment:Optional[str]=None,
|
|
231
|
-
region:Optional[Union[str,list]]=None,excludefield:Optional[Union[str,list]]=None,removeduplicate:Optional[bool]=None,raw_query:Optional[str]=None,
|
|
232
|
-
excludecategory:Optional[Union[str,list]]=None,id:Optional[str]=None
|
|
233
|
-
)->dict:
|
|
234
|
-
"""
|
|
235
|
-
Sending GET request to the latest api.
|
|
236
|
-
For more information about parameters and input, Please visit our documentation page: https://newsdata.io/documentation
|
|
237
|
-
"""
|
|
238
|
-
params = {
|
|
239
|
-
'apikey':self.apikey,'q':q,'qInTitle':qInTitle,'country':country,'category':category,'language':language,'domain':domain,'timeframe':str(timeframe) if timeframe else timeframe,
|
|
240
|
-
'size':size,'domainurl':domainurl,'excludedomain':excludedomain,'timezone':timezone,'full_content':full_content,'image':image,'video':video,'prioritydomain':prioritydomain,
|
|
241
|
-
'page':page,'qInMeta':qInMeta,'tag':tag, 'sentiment':sentiment, 'region':region,'excludefield':excludefield,'removeduplicate':removeduplicate,'raw_query':raw_query,
|
|
242
|
-
'excludecategory':excludecategory,'id':id
|
|
243
|
-
}
|
|
244
|
-
URL_parameters = self.__validate_parms(user_param=params)
|
|
245
|
-
URL_parameters_encoded = urlencode(URL_parameters, quote_via=quote)
|
|
246
|
-
if scroll == True:
|
|
247
|
-
return self.__get_feeds_all(url=f'{self.latest_url}?{URL_parameters_encoded}',max_result=max_result)
|
|
248
|
-
else:
|
|
249
|
-
return self.__get_feeds(url=f'{self.latest_url}?{URL_parameters_encoded}')
|
|
250
|
-
|
|
251
|
-
def archive_api(
|
|
252
|
-
self, q:Optional[str]=None, qInTitle:Optional[str]=None, country:Optional[Union[str, list]]=None, category:Optional[Union[str, list]]=None,
|
|
253
|
-
language:Optional[Union[str, list]]=None, domain:Optional[Union[str, list]]=None, size:Optional[int]=None,domainurl:Optional[Union[str, list]]=None,
|
|
254
|
-
excludedomain:Optional[Union[str, list]]=None, timezone:Optional[str]=None, full_content:Optional[bool]=None,image:Optional[bool]=None,
|
|
255
|
-
video:Optional[bool]=None,prioritydomain:Optional[str]=None, page:Optional[str]=None, scroll:Optional[bool]=False, max_result:Optional[int]=None,
|
|
256
|
-
from_date:Optional[str]=None, to_date:Optional[str]=None, qInMeta:Optional[str]=None, excludefield:Optional[Union[str,list]]=None,raw_query:Optional[str]=None,
|
|
257
|
-
excludecategory:Optional[Union[str,list]]=None,id:Optional[str]=None
|
|
258
|
-
) -> dict:
|
|
259
|
-
"""
|
|
260
|
-
Sending GET request to the archive api
|
|
261
|
-
For more information about parameters and input, Please visit our documentation page: https://newsdata.io/documentation
|
|
262
|
-
"""
|
|
263
|
-
params = {
|
|
264
|
-
'q':q,'qInTitle':qInTitle,'country':country,'category':category,'language':language,'domain':domain,'size':size,'domainurl':domainurl,'excludedomain':excludedomain,
|
|
265
|
-
'timezone':timezone,'full_content':full_content,'image':image,'video':video,'prioritydomain':prioritydomain,'page':page,'from_date':from_date,'to_date':to_date,
|
|
266
|
-
'apikey':self.apikey,'qInMeta':qInMeta,'excludefield':excludefield,'raw_query':raw_query,'excludecategory':excludecategory,'id':id
|
|
267
|
-
}
|
|
268
|
-
URL_parameters = self.__validate_parms(user_param=params)
|
|
269
|
-
URL_parameters_encoded = urlencode(URL_parameters, quote_via=quote)
|
|
270
|
-
if scroll == True:
|
|
271
|
-
return self.__get_feeds_all(url=f'{self.archive_url}?{URL_parameters_encoded}',max_result=max_result)
|
|
272
|
-
else:
|
|
273
|
-
return self.__get_feeds(url=f'{self.archive_url}?{URL_parameters_encoded}')
|
|
274
|
-
|
|
275
|
-
def sources_api( self, country:Optional[str]= None, category:Optional[str]= None, language:Optional[str]= None, prioritydomain:Optional[str]= None,raw_query:Optional[str]=None):
|
|
276
|
-
"""
|
|
277
|
-
Sending GET request to the sources api
|
|
278
|
-
For more information about parameters and input, Please visit our documentation page: https://newsdata.io/documentation
|
|
279
|
-
"""
|
|
280
|
-
params = {"apikey":self.apikey, "country":country, "category":category, "language":language, "prioritydomain":prioritydomain,'raw_query':raw_query}
|
|
281
|
-
URL_parameters = self.__validate_parms(user_param=params)
|
|
282
|
-
URL_parameters_encoded = urlencode(URL_parameters, quote_via=quote)
|
|
283
|
-
return self.__get_feeds(url=f'{self.sources_url}?{URL_parameters_encoded}')
|
|
284
|
-
|
|
285
|
-
def crypto_api(
|
|
286
|
-
self, q:Optional[str]=None, qInTitle:Optional[str]=None,language:Optional[Union[str, list]]=None, domain:Optional[Union[str, list]]=None,
|
|
287
|
-
timeframe:Optional[Union[int,str]]=None, size:Optional[int]=None,domainurl:Optional[Union[str, list]]=None, excludedomain:Optional[Union[str, list]]=None,
|
|
288
|
-
timezone:Optional[str]=None, full_content:Optional[bool]=None,image:Optional[bool]=None, video:Optional[bool]=None, prioritydomain:Optional[str]=None,
|
|
289
|
-
page:Optional[str]=None, scroll:Optional[bool]=False,max_result:Optional[int]=None, qInMeta:Optional[str]=None,tag:Optional[Union[str,list]]=None,
|
|
290
|
-
sentiment:Optional[str]=None,coin:Optional[Union[str, list]]=None,excludefield:Optional[Union[str,list]]=None,from_date:Optional[str]=None,
|
|
291
|
-
to_date:Optional[str]=None,removeduplicate:Optional[bool]=None,raw_query:Optional[str]=None,id:Optional[str]=None,
|
|
292
|
-
)->dict:
|
|
293
|
-
"""
|
|
294
|
-
Sending GET request to the crypto api
|
|
295
|
-
For more information about parameters and input, Please visit our documentation page: https://newsdata.io/documentation
|
|
296
|
-
"""
|
|
297
|
-
|
|
298
|
-
params = {
|
|
299
|
-
'apikey':self.apikey,'q':q,'qInTitle':qInTitle,'language':language,'domain':domain,'size':size,'domainurl':domainurl,
|
|
300
|
-
'excludedomain':excludedomain,'timezone':timezone,'full_content':full_content,'image':image,'video':video,'prioritydomain':prioritydomain,'page':page,
|
|
301
|
-
'timeframe':str(timeframe) if timeframe else timeframe,'qInMeta':qInMeta,'tag':tag, 'sentiment':sentiment,'coin':coin,'excludefield':excludefield,
|
|
302
|
-
'from_date':from_date,'to_date':to_date,'removeduplicate':removeduplicate,'raw_query':raw_query,'id':id
|
|
303
|
-
}
|
|
304
|
-
URL_parameters = self.__validate_parms(user_param=params)
|
|
305
|
-
URL_parameters_encoded = urlencode(URL_parameters, quote_via=quote)
|
|
306
|
-
if scroll == True:
|
|
307
|
-
return self.__get_feeds_all(url=f'{self.crypto_url}?{URL_parameters_encoded}',max_result=max_result)
|
|
308
|
-
else:
|
|
309
|
-
return self.__get_feeds(url=f'{self.crypto_url}?{URL_parameters_encoded}')
|
|
310
|
-
|
|
311
|
-
def count_api(
|
|
312
|
-
self, q:Optional[str]=None, qInTitle:Optional[str]=None, qInMeta:Optional[str]=None, country:Optional[Union[str, list]]=None,
|
|
313
|
-
category:Optional[Union[str, list]]=None,language:Optional[Union[str, list]]=None, from_date:Optional[str]=None,
|
|
314
|
-
to_date:Optional[str]=None,raw_query:Optional[str]=None
|
|
315
|
-
) -> dict:
|
|
316
|
-
"""
|
|
317
|
-
Sending GET request to the count api
|
|
318
|
-
For more information about parameters and input, Please visit our documentation page: https://newsdata.io/documentation
|
|
319
|
-
"""
|
|
320
|
-
params = {
|
|
321
|
-
'q':q,'qInTitle':qInTitle,'country':country,'category':category,'language':language,'from_date':from_date,'to_date':to_date,
|
|
322
|
-
'apikey':self.apikey,'qInMeta':qInMeta,'raw_query':raw_query
|
|
323
|
-
}
|
|
324
|
-
URL_parameters = self.__validate_parms(user_param=params)
|
|
325
|
-
URL_parameters_encoded = urlencode(URL_parameters, quote_via=quote)
|
|
326
|
-
return self.__get_feeds(url=f'{self.count_url}?{URL_parameters_encoded}')
|
|
327
|
-
|
|
328
|
-
def crypto_count_api(
|
|
329
|
-
self, q:Optional[str]=None, qInTitle:Optional[str]=None, qInMeta:Optional[str]=None,language:Optional[Union[str, list]]=None,
|
|
330
|
-
from_date:Optional[str]=None,to_date:Optional[str]=None,coin:Optional[str]=None,raw_query:Optional[str]=None
|
|
331
|
-
) -> dict:
|
|
332
|
-
"""
|
|
333
|
-
Sending GET request to the crypto count api
|
|
334
|
-
For more information about parameters and input, Please visit our documentation page: https://newsdata.io/documentation
|
|
335
|
-
"""
|
|
336
|
-
params = {
|
|
337
|
-
'q':q,'qInTitle':qInTitle,'language':language,'from_date':from_date,'to_date':to_date,'coin':coin,
|
|
338
|
-
'apikey':self.apikey,'qInMeta':qInMeta,'raw_query':raw_query
|
|
339
|
-
}
|
|
340
|
-
URL_parameters = self.__validate_parms(user_param=params)
|
|
341
|
-
URL_parameters_encoded = urlencode(URL_parameters, quote_via=quote)
|
|
342
|
-
return self.__get_feeds(url=f'{self.crypto_count_url}?{URL_parameters_encoded}')
|
|
343
|
-
|
|
344
|
-
def __del__(self):
|
|
345
|
-
if isinstance(self.request_method,requests.Session):
|
|
346
|
-
self.request_method.close()
|
|
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
|