|  | @@ -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_campaigns(**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_campaigns(**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_campaigns(**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,35 @@ 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_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)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  class SBClient(BaseClient):
 | 
	
		
			
				|  |  |      def get_campaigns(self, **body):
 | 
	
	
		
			
				|  | @@ -234,38 +298,45 @@ if __name__ == '__main__':
 | 
	
		
			
				|  |  |          'lwa_client_secret': 'cbf0514186db4df91e04a8905f0a91b605eae4201254ced879d8bb90df4b474d',
 | 
	
		
			
				|  |  |          'profile_id': "3006125408623189"
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | +    sp = SPClient(**AWS_CREDENTIALS)
 | 
	
		
			
				|  |  | +    # sp.get_budget(campaign_ids=["123264383028090"])
 | 
	
		
			
				|  |  | +    # print(sp.get_campaigns())
 | 
	
		
			
				|  |  | +    for i in sp.iter_targets():
 | 
	
		
			
				|  |  | +        print(i)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      # 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 = 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",
 |