__copyright__ = "Copyright (c) 2024-2025 Alex Laird"
__license__ = "MIT"
from typing import Optional
[docs]
class Selector:
"""
Can be used to extend the definition of a CSS selector, allowing for programmatic inspection
of the selections results before determining if selector matches.
"""
def __init__(self,
css_selector: str,
text: Optional[str] = None,
text_contains: Optional[str] = None) -> None:
#: The CSS selector.
self.css_selector: str = css_selector
#: The text within the tag that must match exactly (after stripping).
self.text: Optional[str] = text
#: A substring within the tag's text that must be present (case-insensitive). Evaluated only when
#: :attr:`text` is not set.
self.text_contains: Optional[str] = text_contains
[docs]
class Selectors:
"""
A class containing CSS selectors. Extend and override with ``selectors_class`` in the config:
.. code-block:: python
from amazonorders.conf import AmazonOrdersConfig
config = AmazonOrdersConfig(data={"selectors_class": "my_module.MySelectors"})
"""
##########################################################################
# CSS selectors for Pages
##########################################################################
BAD_INDEX_SELECTOR = "html.a-tablet"
##########################################################################
# CSS selectors for AuthForms
##########################################################################
SIGN_IN_FORM_SELECTOR = "form[name='signIn']"
CLAIM_FORM_SELECTOR = "form[name='signIn'].auth-validate-form"
INTENT_FORM_SELECTOR = "form#intent-confirmation-form"
INTENT_MESSAGE_SELECTOR = "div#intent-confirmation-container"
MFA_DEVICE_SELECT_FORM_SELECTOR = "form#auth-select-device-form"
MFA_DEVICE_SELECT_INPUT_SELECTOR = "input[name='otpDeviceContext']"
MFA_DEVICE_SELECT_INPUT_SELECTOR_VALUE = "value"
MFA_FORM_SELECTOR = "form#auth-mfa-form"
CAPTCHA_1_FORM_SELECTOR = "form.cvf-widget-form-captcha"
CAPTCHA_2_FORM_SELECTOR = ["form:has(input[id^='captchacharacters'])", "form[action$='validateCaptcha']"]
CAPTCHA_OTP_FORM_SELECTOR = "form#verification-code-form"
DEFAULT_ERROR_TAG_SELECTOR = "div#auth-error-message-box"
CAPTCHA_1_ERROR_SELECTOR = "div.cvf-widget-alert"
CAPTCHA_2_ERROR_SELECTOR = "div.a-alert-info"
##########################################################################
# CSS selectors for pagination
##########################################################################
NEXT_PAGE_LINK_SELECTOR = "ul.a-pagination li.a-last a"
##########################################################################
# CSS selectors for Entities and Fields
#
# A ``FIELD_`` selector can be either a ``str`` or a ``list``. If a
# ``list`` is given, each selector in the list will be tried. The
# ``Parsable`` contains helper functions for parsing fields, including
# ``simple_parse()``, which is suitable for most fields when a ``FIELD_``
# is passed.
##########################################################################
ORDER_HISTORY_ENTITY_SELECTOR = ["div.order-card",
"div.order"]
ORDER_HISTORY_COUNT_SELECTOR = ".js-yo-container span.num-orders"
ORDER_DETAILS_ENTITY_SELECTOR = ["div#orderDetails",
"div#ordersContainer"]
ITEM_ENTITY_SELECTOR = ["[data-component='purchasedItems'] .a-fixed-left-grid",
"div:has(> div.yohtmlc-item)",
".item-box"]
SHIPMENT_ENTITY_SELECTOR = ["[data-component='orderCard'] [data-component='shipments'] .a-box",
"div.shipment",
"div.delivery-box"]
# Selectors defined here mean we don't have a reliable way to parse all details in an Order, so Items and
# Shipments will be skipped
ORDER_SKIP_ITEMS = [
# Identifies an Amazon Fresh order
".brand-info-box .brand-logo img",
# Identifies a Whole Foods Market order
"a.yohtmlc-order-details-link[href^='/wholefoodsmarket']",
# Identifies an order from a physical Amazon store
Selector("div.yohtmlc-shipment-status-primaryText", "Purchased at Amazon")
]
# Selectors defined here mean the Order will not have parsable totals
ORDER_SKIP_TOTALS = [
# Identifies a cancelled order on the history page
Selector("div.yohtmlc-shipment-status-primaryText", "Cancelled"),
# Identifies a cancelled order on the details page
Selector("h4.a-alert-heading", text_contains="cancelled")
]
#####################################
# CSS selectors for Item fields
#####################################
FIELD_ITEM_IMG_LINK_SELECTOR = "a img"
FIELD_ITEM_QUANTITY_SELECTOR = [".od-item-view-qty",
"span.item-view-qty",
"span.product-image__qty"]
FIELD_ITEM_TITLE_SELECTOR = ["[data-component='itemTitle']",
".yohtmlc-item a", ".yohtmlc-product-title"]
FIELD_ITEM_LINK_SELECTOR = ["[data-component='itemTitle'] a",
".yohtmlc-item a",
"a:has(> .yohtmlc-product-title)",
".yohtmlc-product-title a"]
FIELD_ITEM_TAG_ITERATOR_SELECTOR = [".yohtmlc-item div"]
FIELD_ITEM_PRICE_SELECTOR = ["[data-component='unitPrice'] .a-text-price :not(.a-offscreen)",
".yohtmlc-item .a-color-price"]
FIELD_ITEM_SELLER_SELECTOR = ["[data-component='orderedMerchant']"] + FIELD_ITEM_TAG_ITERATOR_SELECTOR
FIELD_ITEM_RETURN_SELECTOR = ["[data-component='itemReturnEligibility']"] + FIELD_ITEM_TAG_ITERATOR_SELECTOR
#####################################
# CSS selectors for Order fields
#####################################
FIELD_ORDER_DETAILS_LINK_SELECTOR = ["a.yohtmlc-order-details-link",
# Would like to use this or similar, but not yet sure how consistent it is
# ".order-header__header-link-list-item:first-of-type a"
]
FIELD_ORDER_NUMBER_SELECTOR = ["[data-component='orderId']",
"[data-component='briefOrderInfo'] div.a-column",
".order-date-invoice-item :is(bdi, span)[dir='ltr']",
".yohtmlc-order-id :is(bdi, span)[dir='ltr']",
":is(bdi, span)[dir='ltr']"]
FIELD_ORDER_GRAND_TOTAL_SELECTOR = ["div.yohtmlc-order-total span.value",
"div.order-header div.a-column.a-span2",
"div.order-header div.a-col-left .a-span9"]
FIELD_ORDER_PLACED_DATE_SELECTOR = ["[data-component='orderDate']",
"span.order-date-invoice-item",
"[data-component='briefOrderInfo'] div.a-column",
"div:is(.a-span3, .a-span12)"]
FIELD_ORDER_PAYMENT_METHOD_SELECTOR = "img.pmts-payment-credit-card-instrument-logo"
FIELD_ORDER_PAYMENT_METHOD_LAST_4_SELECTOR = "span:has(img.pmts-payment-credit-card-instrument-logo):last-child"
FIELD_ORDER_SUBTOTALS_TAG_ITERATOR_SELECTOR = ["[data-component='orderSubtotals'] div.a-row",
"div#od-subtotals div.a-row"]
FIELD_ORDER_SUBTOTALS_TAG_POPOVER_PRELOAD_SELECTOR = ".a-popover-preload"
FIELD_ORDER_SUBTOTALS_INNER_TAG_SELECTOR = "div.a-span-last"
FIELD_ORDER_ADDRESS_SELECTOR = ["div.displayAddressDiv", "[data-component='shippingAddress']"]
FIELD_ORDER_ADDRESS_FALLBACK_1_SELECTOR = "div.recipient span.a-declarative"
FIELD_ORDER_ADDRESS_FALLBACK_2_SELECTOR = "script[id^='shipToData']"
FIELD_ORDER_GIFT_CARD_INSTANCE_SELECTOR = ".gift-card-instance"
#####################################
# CSS selectors for Shipment fields
#####################################
FIELD_SHIPMENT_TRACKING_LINK_SELECTOR = ["span.track-package-button a",
"a[href*='ship-track?itemId=']"]
FIELD_SHIPMENT_DELIVERY_STATUS_SELECTOR = ["div.js-shipment-info-container div.a-row",
"span.delivery-box__primary-text",
".yohtmlc-shipment-status-primaryText",
".od-status-message"]
#####################################
# CSS selectors for Recipient fields
#####################################
FIELD_RECIPIENT_NAME_SELECTOR = ["li.displayAddressFullName",
"div:nth-child(1)",
"li:nth-child(1)"]
FIELD_RECIPIENT_ADDRESS1_SELECTOR = "li.displayAddressAddressLine1"
FIELD_RECIPIENT_ADDRESS2_SELECTOR = "li.displayAddressAddressLine2"
FIELD_RECIPIENT_ADDRESS_CITY_STATE_POSTAL_SELECTOR = "li.displayAddressCityStateOrRegionPostalCode"
FIELD_RECIPIENT_ADDRESS_COUNTRY_SELECTOR = "li.displayAddressCountryName"
FIELD_RECIPIENT_ADDRESS_FALLBACK_SELECTOR = ["div:nth-child(2)",
"li:nth-child(2)"]
#####################################
# CSS selectors for Seller fields
#####################################
FIELD_SELLER_NAME_SELECTOR = ["a", "span"]
FIELD_SELLER_LINK_SELECTOR = "a"
#####################################
# CSS selectors for Transaction fields
#####################################
TRANSACTION_HISTORY_FORM_SELECTOR = "form:has(input[name='ppw-widgetState'])"
TRANSACTION_HISTORY_CONTAINER_SELECTOR = ".pmts-portal-component"
TRANSACTION_DATE_CONTAINERS_SELECTOR = "div.apx-transaction-date-container"
TRANSACTIONS_CONTAINER_SELECTOR = "div"
TRANSACTIONS_SELECTOR = "div.apx-transactions-line-item-component-container:has(*)"
TRANSACTIONS_NEXT_PAGE_INPUT_SELECTOR = [
"input[type='submit'][name^='ppw-widgetEvent:DefaultNextPageNavigationEvent']"]
TRANSACTIONS_NEXT_PAGE_INPUT_STATE_SELECTOR = "input[name='ppw-widgetState']"
TRANSACTIONS_NEXT_PAGE_INPUT_IE_SELECTOR = "input[name='ie']"
FIELD_TRANSACTION_COMPLETED_DATE_SELECTOR = "span"
FIELD_TRANSACTION_PAYMENT_METHOD_SELECTOR = [
"div.apx-transactions-line-item-component-container > div:nth-child(1) span.a-size-base"]
FIELD_TRANSACTION_GRAND_TOTAL_SELECTOR = [
"div.apx-transactions-line-item-component-container > div:nth-child(1) span.a-size-base-plus"]
FIELD_TRANSACTION_ORDER_NUMBER_SELECTOR = [
"div.apx-transactions-line-item-component-container div .a-span12"]
FIELD_TRANSACTION_ORDER_LINK_SELECTOR = [
"div.apx-transactions-line-item-component-container a.a-link-normal"]
FIELD_TRANSACTION_SELLER_NAME_SELECTOR = [
"div.apx-transactions-line-item-component-container :has(a.a-link-normal) + div"]