簡體   English   中英

如何使用 AWS API 獲得 AWS 月度發票 PDF?

[英]How can I get AWS monthly invoice PDF using AWS API?

我如何以編程方式下載會計部門每月詢問我的 PDF 月度發票?

我可以從 AWS 控制台(例如https://console.aws.amazon.com/billing/home?region=eu-west-3#/bills?year=2019&month=3 )獲取它們,其中有指向發票。

單擊下載發票的那一刻,我可以看到 HTTP 對以下 URL 的請求: https://console.aws.amazon.com/billing/rest/v1.0/bill/invoice/generate?generatenew=true&invoiceGroupId=_SOME_ID_&invoicenumber=_SOME_ID_

然后對 URL 的最終請求實際服務於 PDF 文件: https://console.aws.amazon.com/billing/rest/v1.0/bill/invoice/download?invoiceGroupId=_SOME_ID_&invoicenumber=_SOME_ID_

我在 AWS API 上找不到文檔來獲取此類發票文檔(有一些用於賬單報告和其他內容,但沒有用於“官方”文檔)所以我開始問自己它是否可用?

在抓取 AWS 控制台(通過ScrapySeleniumPuppeteer )之前,我詢問了社區。

注意:我知道 AWS 可以通過電子郵件發送發票 PDF,但我寧願直接從 AWS 獲取它,而不是從 IMAP/POP 電子郵件服務器獲取。

您可以使用aws cli或aws sdk來獲取json格式的數據。 然后將json轉換為pdf(此答案未涵蓋)。

AWS CLI

aws cli提供get-cost-and-usage命令。 通過擺弄參數,您可以獲得與計費發票所產生的輸出相匹配的輸出。

此命令的用法示例:

    aws ce get-cost-and-usage \
        --time-period Start=2019-03-01,End=2019-04-01 \
        --granularity MONTHLY \
        --metrics "BlendedCost" "UnblendedCost" "UsageQuantity" \
        --group-by Type=DIMENSION,Key=SERVICE

產生以下輸出

{
  "GroupDefinitions": [
    {
      "Type": "DIMENSION",
      "Key": "SERVICE"
    }
  ],
  "ResultsByTime": [
    {
      "TimePeriod": {
        "Start": "2019-03-01",
        "End": "2019-04-01"
      },
      "Total": {},
      "Groups": [
        {
          "Keys": [
            "AWS Budgets"
          ],
          "Metrics": {
            "BlendedCost": {
              "Amount": "3.0392156805",
              "Unit": "USD"
            },
            "UnblendedCost": {
              "Amount": "3",
              "Unit": "USD"
            },
            "UsageQuantity": {
              "Amount": "155",
              "Unit": "N/A"
            }
          }
        },
        {
          "Keys": [
            "AWS CloudTrail"
          ],
          "Metrics": {
            "BlendedCost": {
              "Amount": "0",
              "Unit": "USD"
            },
            "UnblendedCost": {
              "Amount": "0",
              "Unit": "USD"
            },
            "UsageQuantity": {
              "Amount": "720042",
              "Unit": "N/A"
            }
          }
        },
...

AWS開發工具包

您還可以通過編程方式獲取相同類型的數據。 最簡單的方法是使用aws sdk。 請參閱要使用的SDK的文檔。 例如,可以在此處找到有關python sdk的此功能的信息。

具體到發票,這是不幸的,但直到今天,除了手動下載它們或幸運地獲得並必須通過 email https://aws.amazon 處理所有發票之外,還沒有本地下載它們的方法。 com/premiumsupport/knowledge-center/download-pdf-invoice/

https://github.com/iann0036/aws-bill-export (它不使用本機 API 而是抓取網頁並通過 lambda 和 nodejs 設置)。

我剛剛寫完一些 python + selenium,它更“可怕”但完成了工作(至少對於今天的 UI/2023 年 1 月)...

import os
import sys
import time
import argparse
from os.path import expanduser
from datetime import datetime
from dateutil.relativedelta import relativedelta
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.action_chains import ActionChains

home = expanduser("~")
# Variables grabbed from CLI arguments
parser = argparse.ArgumentParser(
    description='AWS Console Login, programming the unprogrammatically-accessible (via CLI/API, using selenium instead).')
parser.add_argument(
    '-i', '--interactive',
    help="Use False for Headless mode",
    default=False,
    required=False
)
args = parser.parse_args()

# ChromeDriver options
options = webdriver.ChromeOptions()
if args.interactive == False:
    options.add_argument('--headless')
    download_directory = "./aws_invoice_downloads"
    if not os.path.exists(download_directory):
        os.makedirs(download_directory)
else:
    download_directory = home + "/Downloads"
options.add_argument("--window-size=1920x1080")
options.add_argument("--remote-debugging-port=9222")
options.add_argument('--no-sandbox')
options.add_argument("--disable-gpu")
options.add_argument('--disable-dev-shm-usage')
options.add_experimental_option("prefs", {
    "download.default_directory": download_directory,
    "download.prompt_for_download": False
})
# Initiate ChromeDriver
driver = webdriver.Chrome(executable_path='chromedriver', options=options)
# create action chain object
action = ActionChains(driver)

# Set the default selenium timeout
delay = 30  # seconds

# Abort function
def abort_function():
    print ("Aborting!")
    driver.close()
    sys.exit(1)

# Wait for download function
def download_wait(path_to_downloads):
    seconds = 0
    dl_wait = True
    while dl_wait and seconds < 30:
        time.sleep(1)
        dl_wait = False
        for fname in os.listdir(path_to_downloads):
            if fname.endswith('.crdownload'):
                dl_wait = True
        seconds += 1
    return seconds

def download_invoices(Id, Network):
    print("Switching to the " + Network + "/" + Id + " org account...")
    # remove_existing_conflicts(Network)
    driver.get("https://signin.aws.amazon.com/switchrole?account=" + Id + "&roleName=YOUR_ROLE_NAME&displayName=" + Network + "%20Org%20Master")
    time.sleep(1)
    elem = WebDriverWait(driver, delay).until(
        EC.presence_of_element_located((By.XPATH, '//*[@type="submit"]'))
    )
    elem.click()
    time.sleep(3)
    print("Downloading invoices...")
    # Notes
    # Can provide YYYY and MM in the URL to get a specific YYYY/MM billing period
    # https://us-east-1.console.aws.amazon.com/billing/home?region=us-east-1#/bills?year=2023&month=1

    # Get today's YYYY
    today = datetime.now()
    last_month = today - relativedelta(months=1)
    year = last_month.strftime("%Y")
    month = last_month.strftime("%m")

    driver.get("https://us-east-1.console.aws.amazon.com/billing/home?region=us-east-1#/bills?year=" + year + "&month=" + month)
    WebDriverWait(driver, 13).until(
        EC.presence_of_element_located((By.XPATH, '//*[@data-testid="main-spinner"]'))
    )
    time.sleep(2)

    elem = WebDriverWait(driver, 13).until(
        EC.presence_of_all_elements_located((By.XPATH, '(//*[text()[contains(., " Charges")]])[position() < last() - 1]'))
    )
    # Count the number of items in the list
    elem_count = len(elem)
    print("Found " + str(elem_count) + " items in the list...")

    # Loop through the list and expand each item
    for i in range(1, elem_count + 1):
        print("Expanding item " + str(i) + " of " + str(elem_count) + "...")
        # (//*[text()[contains(., " Charges")]])[position() < last() - 1][i]
        elem = WebDriverWait(driver, 13).until(
            EC.presence_of_element_located((By.XPATH, '(//*[text()[contains(., " Charges")]])[position() < last() - 1][' + str(i) + ']'))
        )
        desired_y = (elem.size['height'] / 2) + elem.location['y']
        current_y = (driver.execute_script('return window.innerHeight') / 2) + driver.execute_script('return window.pageYOffset')
        scroll_y_by = desired_y - current_y
        driver.execute_script("window.scrollBy(0, arguments[0]);", scroll_y_by)
        time.sleep(2) # Fixes content shift and ElementClickInterceptedException by waiting, checking the elem, and scrolling again
        elem = WebDriverWait(driver, delay).until(
            EC.visibility_of_element_located((By.XPATH, '(//*[text()[contains(., " Charges")]])[position() < last() - 1][' + str(i) + ']')))
        driver.execute_script("arguments[0].scrollIntoView(true); window.scrollBy(0, -100);", elem)
        action.move_to_element(elem).move_by_offset(0,0).click().perform()
        # Count the number of invoices with that item
        # (//*[text()[contains(., " Charges")]])[position() < last() - 1][2]/following-sibling::div//*[@title="Download Invoice"]
        elem = WebDriverWait(driver, 13).until(
            EC.presence_of_all_elements_located((By.XPATH, '(//*[text()[contains(., " Charges")]])[position() < last() - 1][' + str(i) + ']/following-sibling::div//*[@title="Download Invoice"]'))
        )
        # Count the number of items in the list
        invoice_count = len(elem)
        # Loop through the list and download each invoice
        for j in range(1, invoice_count + 1):
            print("Downloading invoice " + str(j) + " of " + str(invoice_count) + "...")
            # (//*[text()[contains(., " Charges")]])[position() < last() - 1][2]/following-sibling::div//*[@title="Download Invoice"][1]
            elem = WebDriverWait(driver, 13).until(
                EC.presence_of_element_located((By.XPATH, '((//*[text()[contains(., " Charges")]])[position() < last() - 1][' + str(i) + ']/following-sibling::div//*[@title="Download Invoice"])[' + str(j) + ']'))
            )
            desired_y = (elem.size['height'] / 2) + elem.location['y']
            current_y = (driver.execute_script('return window.innerHeight') / 2) + driver.execute_script('return window.pageYOffset')
            scroll_y_by = desired_y - current_y
            driver.execute_script("window.scrollBy(0, arguments[0]);", scroll_y_by)
            time.sleep(2) # Fixes content shift and ElementClickInterceptedException by waiting, checking the elem, and scrolling again
            elem = WebDriverWait(driver, delay).until(
                EC.visibility_of_element_located((By.XPATH, '((//*[text()[contains(., " Charges")]])[position() < last() - 1][' + str(i) + ']/following-sibling::div//*[@title="Download Invoice"])[' + str(j) + ']')))
            driver.execute_script("arguments[0].scrollIntoView(true); window.scrollBy(0, -100);", elem)
            action.move_to_element(elem).move_by_offset(0,0).click().perform()
            download_wait(download_directory)
            time.sleep(3)
        # Find the parent again
        elem = WebDriverWait(driver, 13).until(
            EC.presence_of_element_located((By.XPATH, '(//*[text()[contains(., " Charges")]])[position() < last() - 1][' + str(i) + ']'))
        )
        # Collapse the parent
        desired_y = (elem.size['height'] / 2) + elem.location['y']
        current_y = (driver.execute_script('return window.innerHeight') / 2) + driver.execute_script('return window.pageYOffset')
        scroll_y_by = desired_y - current_y
        driver.execute_script("window.scrollBy(0, arguments[0]);", scroll_y_by)
        time.sleep(2) # Fixes content shift and ElementClickInterceptedException by waiting, checking the elem, and scrolling again
        elem = WebDriverWait(driver, delay).until(
            EC.visibility_of_element_located((By.XPATH, '(//*[text()[contains(., " Charges")]])[position() < last() - 1][' + str(i) + ']')))
        action.move_to_element(elem).move_by_offset(0,0).click().perform()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM