|
@@ -6,14 +6,14 @@ from urllib.parse import urljoin
|
|
|
from typing import List, Literal, Iterable, Iterator
|
|
|
import gzip
|
|
|
from pathlib import Path
|
|
|
-from nb_log import get_logger
|
|
|
+from logging import getLogger
|
|
|
|
|
|
URL_AUTH = "https://api.amazon.com/auth/o2/token"
|
|
|
URL_AD_API = "https://advertising-api.amazon.com"
|
|
|
|
|
|
cache = TTLCache(maxsize=10, ttl=3200)
|
|
|
|
|
|
-logger = get_logger(__name__)
|
|
|
+logger = getLogger(__name__)
|
|
|
|
|
|
|
|
|
def gz_decompress(file_path: str, chunk_size: int = 1024 * 1024):
|
|
@@ -93,6 +93,17 @@ class SPClient(BaseClient):
|
|
|
}
|
|
|
return self._request(url_path, method="POST", headers=headers, body=body)
|
|
|
|
|
|
+ def iter_campaigns(self, **body) -> Iterator[dict]:
|
|
|
+ if "maxResults" not in body:
|
|
|
+ body["maxResults"] = 100
|
|
|
+ while True:
|
|
|
+ info: dict = self.get_campaigns(**body)
|
|
|
+ yield from info["campaigns"]
|
|
|
+ if not info.get("nextToken"):
|
|
|
+ break
|
|
|
+ body["nextToken"] = info["nextToken"]
|
|
|
+ logger.info(f"总共数量:{info['totalResults']}")
|
|
|
+
|
|
|
def get_ad_groups(self, **body):
|
|
|
url_path = "/sp/adGroups/list"
|
|
|
headers = {
|
|
@@ -101,6 +112,17 @@ class SPClient(BaseClient):
|
|
|
}
|
|
|
return self._request(url_path, method="POST", body=body, headers=headers)
|
|
|
|
|
|
+ def iter_adGroups(self, **body) -> Iterator[dict]:
|
|
|
+ if "maxResults" not in body:
|
|
|
+ body["maxResults"] = 100
|
|
|
+ while True:
|
|
|
+ info: dict = self.get_ad_groups(**body)
|
|
|
+ yield from info["adGroups"]
|
|
|
+ if not info.get("nextToken"):
|
|
|
+ break
|
|
|
+ body["nextToken"] = info["nextToken"]
|
|
|
+ logger.info(f"总共数量:{info['totalResults']}")
|
|
|
+
|
|
|
def get_ads(self, **body):
|
|
|
url_path = "/sp/productAds/list"
|
|
|
headers = {
|
|
@@ -109,6 +131,17 @@ class SPClient(BaseClient):
|
|
|
}
|
|
|
return self._request(url_path, method="POST", body=body, headers=headers)
|
|
|
|
|
|
+ def iter_ads(self, **body) -> Iterator[dict]:
|
|
|
+ if "maxResults" not in body:
|
|
|
+ body["maxResults"] = 100
|
|
|
+ while True:
|
|
|
+ info: dict = self.get_ads(**body)
|
|
|
+ yield from info["productAds"]
|
|
|
+ if not info.get("nextToken"):
|
|
|
+ break
|
|
|
+ body["nextToken"] = info["nextToken"]
|
|
|
+ logger.info(f"总共数量:{info['totalResults']}")
|
|
|
+
|
|
|
def get_keywords(self, **body):
|
|
|
url_path = "/sp/keywords/list"
|
|
|
headers = {
|
|
@@ -116,6 +149,16 @@ class SPClient(BaseClient):
|
|
|
"Content-Type": "application/vnd.spKeyword.v3+json"
|
|
|
}
|
|
|
return self._request(url_path, method="POST", body=body, headers=headers)
|
|
|
+ def iter_keywords(self, **body) -> Iterator[dict]:
|
|
|
+ if "maxResults" not in body:
|
|
|
+ body["maxResults"] = 100
|
|
|
+ while True:
|
|
|
+ info: dict = self.get_keywords(**body)
|
|
|
+ yield from info["keywords"]
|
|
|
+ if not info.get("nextToken"):
|
|
|
+ break
|
|
|
+ body["nextToken"] = info["nextToken"]
|
|
|
+ logger.info(f"总共数量:{info['totalResults']}")
|
|
|
|
|
|
def get_targets(self, **body):
|
|
|
url_path = "/sp/targets/list"
|
|
@@ -125,14 +168,43 @@ class SPClient(BaseClient):
|
|
|
}
|
|
|
return self._request(url_path, method="POST", body=body, headers=headers)
|
|
|
|
|
|
+ def iter_targets(self, **body) -> Iterator[dict]:
|
|
|
+ if "maxResults" not in body:
|
|
|
+ body["maxResults"] = 100
|
|
|
+ while True:
|
|
|
+ info: dict = self.get_targets(**body)
|
|
|
+ yield from info["targetingClauses"]
|
|
|
+ if not info.get("nextToken"):
|
|
|
+ break
|
|
|
+ body["nextToken"] = info["nextToken"]
|
|
|
+ logger.info(f"总共数量:{info['totalResults']}")
|
|
|
+
|
|
|
def get_budget(self, campaign_ids: list):
|
|
|
url_path = "/sp/campaigns/budget/usage"
|
|
|
body = {
|
|
|
"campaignIds": campaign_ids
|
|
|
}
|
|
|
- ret_data = self._request(url_path, method="POST", body=body)
|
|
|
- print(json.dumps(ret_data, ensure_ascii=False))
|
|
|
+ return self._request(url_path, method="POST", body=body)
|
|
|
+
|
|
|
+ def get_adgroup_bidrecommendation(self,campaignId:str,adGroupId:str,targetingExpressions:list,recommendationType:str="BIDS_FOR_EXISTING_AD_GROUP"):
|
|
|
+ url_path = "/sp/targets/bid/recommendations"
|
|
|
+ headers = {
|
|
|
+ "Accept": "application/vnd.spthemebasedbidrecommendation.v3+json",
|
|
|
+ "Content-Type": "application/vnd.spthemebasedbidrecommendation.v3+json"
|
|
|
+ }
|
|
|
+ body = {"campaignId":campaignId,
|
|
|
+ "adGroupId":adGroupId,
|
|
|
+ "recommendationType":recommendationType,
|
|
|
+ "targetingExpressions":targetingExpressions}
|
|
|
+ return self._request(url_path, method="POST", body=body, headers=headers)
|
|
|
|
|
|
+ def get_keyword_bidrecommendation(self,adGroupId:str,keyword:list,matchType:list):
|
|
|
+ keywords = list(map(lambda x:{"keyword":x[0],"matchType":x[1]},list(zip(keyword,matchType))))
|
|
|
+ print(keywords)
|
|
|
+ url_path = "/v2/sp/keywords/bidRecommendations"
|
|
|
+ body = {"adGroupId":adGroupId,
|
|
|
+ "keywords":keywords}
|
|
|
+ return self._request(url_path, method="POST", body=body)
|
|
|
|
|
|
class SBClient(BaseClient):
|
|
|
def get_campaigns(self, **body):
|
|
@@ -152,10 +224,70 @@ class SBClient(BaseClient):
|
|
|
if not info.get("nextToken"):
|
|
|
break
|
|
|
body["nextToken"] = info["nextToken"]
|
|
|
- logger.info(f"总共数量:{info['totalResults']}")
|
|
|
+ # logger.info(f"总共数量:{info['totalResults']}")
|
|
|
+
|
|
|
+ def get_ad_groups(self,**body):
|
|
|
+ url_path = "/sb/v4/adGroups/list"
|
|
|
+ headers = {
|
|
|
+ 'Content-Type': "application/vnd.sbadgroupresource.v4+json",
|
|
|
+ 'Accept': "application/vnd.sbadgroupresource.v4+json"
|
|
|
+ }
|
|
|
+ return self._request(url_path, method="POST", headers=headers,body=body)
|
|
|
+
|
|
|
+ def iter_adGroups(self, **body) -> Iterator[dict]:
|
|
|
+ if "maxResults" not in body:
|
|
|
+ body["maxResults"] = 100
|
|
|
+ while True:
|
|
|
+ info: dict = self.get_ad_groups(**body)
|
|
|
+ print(info)
|
|
|
+ yield from info["adGroups"]
|
|
|
+ if not info.get("nextToken"):
|
|
|
+ break
|
|
|
+ body["nextToken"] = info["nextToken"]
|
|
|
|
|
|
- def get_ad_groups(self):
|
|
|
- pass
|
|
|
+ def get_ads(self,**body):
|
|
|
+ url_path = "/sb/v4/ads/list"
|
|
|
+ headers = {'Content-Type': "application/vnd.sbadresource.v4+json",
|
|
|
+ 'Accept': "application/vnd.sbadresource.v4+json"
|
|
|
+ }
|
|
|
+ return self._request(url_path, method="POST", headers=headers,body=body)
|
|
|
+
|
|
|
+ def iter_ads(self,**body):
|
|
|
+ if "maxResults" not in body:
|
|
|
+ body["maxResults"] = 100
|
|
|
+ while True:
|
|
|
+ info: dict = self.get_ads(**body)
|
|
|
+ print(info)
|
|
|
+ yield from info["ads"]
|
|
|
+ if not info.get("nextToken"):
|
|
|
+ break
|
|
|
+ body["nextToken"] = info["nextToken"]
|
|
|
+ def get_keywords(self):
|
|
|
+ url_path = "/sb/keywords"
|
|
|
+ return self._request(url_path, method="GET")
|
|
|
+
|
|
|
+ def get_targets(self,**body):
|
|
|
+ url_path = "/sb/targets/list"
|
|
|
+ return self._request(url_path, method="POST", body=body)
|
|
|
+
|
|
|
+ def iter_targets(self,**body):
|
|
|
+ if "maxResults" not in body:
|
|
|
+ body["maxResults"] = 100
|
|
|
+ while True:
|
|
|
+ info: dict = self.get_targets(**body)
|
|
|
+ # print(info)
|
|
|
+ yield from info["targets"]
|
|
|
+ if not info.get("nextToken"):
|
|
|
+ break
|
|
|
+ body["nextToken"] = info["nextToken"]
|
|
|
+
|
|
|
+ def get_budget(self,campaignIds:list):
|
|
|
+ url_path = "/sb/campaigns/budget/usage"
|
|
|
+ body = {"campaignIds":campaignIds}
|
|
|
+ return self._request(url_path, method="POST",body=body)
|
|
|
+ def get_keyword_bidrecommendation(self,**body):
|
|
|
+ url_path = "/sb/recommendations/bids"
|
|
|
+ return self._request(url_path, method="POST", body=body)
|
|
|
|
|
|
def get_report(
|
|
|
self,
|
|
@@ -226,6 +358,11 @@ class SDClient(BaseClient):
|
|
|
url_path = "/sd/campaigns"
|
|
|
return self._request(url_path, params=params)
|
|
|
|
|
|
+class Account(BaseClient):
|
|
|
+ def get_portfolio(self):
|
|
|
+ url_path = "/v2/portfolios/extended"
|
|
|
+ return self._request(url_path)
|
|
|
+
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
AWS_CREDENTIALS = {
|
|
@@ -234,40 +371,46 @@ if __name__ == '__main__':
|
|
|
'lwa_client_secret': 'cbf0514186db4df91e04a8905f0a91b605eae4201254ced879d8bb90df4b474d',
|
|
|
'profile_id': "3006125408623189"
|
|
|
}
|
|
|
+ # sp = SPClient(**AWS_CREDENTIALS)
|
|
|
+ # print(sp.get_keyword_bidrecommendation(adGroupId="119753215871672",keyword=["8mp security camera system","8mp security camera system"],matchType=["broad","exact"]))
|
|
|
+ sb = SBClient(**AWS_CREDENTIALS)
|
|
|
+ # print(list(sb.iter_targets()))
|
|
|
+ print(sb.get_keyword_bidrecommendation(**{'campaignId': 27333596383941,'keywords':[{"matchType":'broad',"keywordText":"4k security camera system"}]}))
|
|
|
+ print(sb.get_budget([27333596383941]))
|
|
|
# sd = SDClient(**AWS_CREDENTIALS)
|
|
|
# print(sd.get_campaigns(startIndex=10, count=10))
|
|
|
|
|
|
- sb = SBClient(**AWS_CREDENTIALS)
|
|
|
- metrics = [
|
|
|
- 'applicableBudgetRuleId',
|
|
|
- 'applicableBudgetRuleName',
|
|
|
- 'attributedConversions14d',
|
|
|
- 'attributedConversions14dSameSKU',
|
|
|
- 'attributedDetailPageViewsClicks14d',
|
|
|
- 'attributedOrderRateNewToBrand14d',
|
|
|
- 'attributedOrdersNewToBrand14d',
|
|
|
- 'attributedOrdersNewToBrandPercentage14d',
|
|
|
- 'attributedSales14d',
|
|
|
- 'attributedSales14dSameSKU',
|
|
|
- 'attributedSalesNewToBrand14d',
|
|
|
- 'attributedSalesNewToBrandPercentage14d',
|
|
|
- 'attributedUnitsOrderedNewToBrand14d',
|
|
|
- 'attributedUnitsOrderedNewToBrandPercentage14d',
|
|
|
- 'campaignBudget',
|
|
|
- 'campaignBudgetType',
|
|
|
- 'campaignId',
|
|
|
- 'campaignName',
|
|
|
- 'campaignRuleBasedBudget',
|
|
|
- 'campaignStatus',
|
|
|
- 'clicks',
|
|
|
- 'cost',
|
|
|
- 'dpv14d',
|
|
|
- 'impressions',
|
|
|
- 'unitsSold14d',
|
|
|
- 'attributedBrandedSearches14d',
|
|
|
- 'topOfSearchImpressionShare']
|
|
|
- sb.get_report(
|
|
|
- record_type="campaigns",
|
|
|
- report_date="20231008",
|
|
|
- metrics=metrics
|
|
|
- )
|
|
|
+ # sb = SBClient(**AWS_CREDENTIALS)
|
|
|
+ # metrics = [
|
|
|
+ # 'applicableBudgetRuleId',
|
|
|
+ # 'applicableBudgetRuleName',
|
|
|
+ # 'attributedConversions14d',
|
|
|
+ # 'attributedConversions14dSameSKU',
|
|
|
+ # 'attributedDetailPageViewsClicks14d',
|
|
|
+ # 'attributedOrderRateNewToBrand14d',
|
|
|
+ # 'attributedOrdersNewToBrand14d',
|
|
|
+ # 'attributedOrdersNewToBrandPercentage14d',
|
|
|
+ # 'attributedSales14d',
|
|
|
+ # 'attributedSales14dSameSKU',
|
|
|
+ # 'attributedSalesNewToBrand14d',
|
|
|
+ # 'attributedSalesNewToBrandPercentage14d',
|
|
|
+ # 'attributedUnitsOrderedNewToBrand14d',
|
|
|
+ # 'attributedUnitsOrderedNewToBrandPercentage14d',
|
|
|
+ # 'campaignBudget',
|
|
|
+ # 'campaignBudgetType',
|
|
|
+ # 'campaignId',
|
|
|
+ # 'campaignName',
|
|
|
+ # 'campaignRuleBasedBudget',
|
|
|
+ # 'campaignStatus',
|
|
|
+ # 'clicks',
|
|
|
+ # 'cost',
|
|
|
+ # 'dpv14d',
|
|
|
+ # 'impressions',
|
|
|
+ # 'unitsSold14d',
|
|
|
+ # 'attributedBrandedSearches14d',
|
|
|
+ # 'topOfSearchImpressionShare']
|
|
|
+ # sb.get_report(
|
|
|
+ # record_type="campaigns",
|
|
|
+ # report_date="20231008",
|
|
|
+ # metrics=metrics
|
|
|
+ # )
|