api pricing
This commit is contained in:
parent
e13b871fda
commit
da492180b4
@ -363,6 +363,38 @@ class OrderProducts(Base):
|
|||||||
quantity = Column(Integer)
|
quantity = Column(Integer)
|
||||||
unit_price = Column(Float)
|
unit_price = Column(Float)
|
||||||
|
|
||||||
|
class APIPricing(Base):
|
||||||
|
__tablename__ = 'api_pricing'
|
||||||
|
|
||||||
|
id = Column(String, primary_key=True)
|
||||||
|
product_id = Column(String, ForeignKey('products.id'))
|
||||||
|
pricing_data = Column(String)
|
||||||
|
date_created = Column(DateTime, default=datetime.now)
|
||||||
|
date_modified = Column(DateTime, default=datetime.now, onupdate=datetime.now)
|
||||||
|
|
||||||
|
class TCGPlayerInventory(Base):
|
||||||
|
__tablename__ = 'tcgplayer_inventory'
|
||||||
|
|
||||||
|
id = Column(String, primary_key=True)
|
||||||
|
tcgplayer_id = Column(Integer)
|
||||||
|
product_line = Column(String)
|
||||||
|
set_name = Column(String)
|
||||||
|
product_name = Column(String)
|
||||||
|
title = Column(String)
|
||||||
|
number = Column(String)
|
||||||
|
rarity = Column(String)
|
||||||
|
condition = Column(String)
|
||||||
|
tcg_market_price = Column(Float)
|
||||||
|
tcg_direct_low = Column(Float)
|
||||||
|
tcg_low_price_with_shipping = Column(Float)
|
||||||
|
tcg_low_price = Column(Float)
|
||||||
|
total_quantity = Column(Integer)
|
||||||
|
add_to_quantity = Column(Integer)
|
||||||
|
tcg_marketplace_price = Column(Float)
|
||||||
|
photo_url = Column(String)
|
||||||
|
date_created = Column(DateTime, default=datetime.now)
|
||||||
|
date_modified = Column(DateTime, default=datetime.now, onupdate=datetime.now)
|
||||||
|
|
||||||
# enums
|
# enums
|
||||||
|
|
||||||
class RarityEnum(str, Enum):
|
class RarityEnum(str, Enum):
|
||||||
|
@ -66,10 +66,11 @@ def get_box_service(
|
|||||||
def get_task_service(
|
def get_task_service(
|
||||||
db: DB,
|
db: DB,
|
||||||
product_service: Annotated[ProductService, Depends(get_product_service)],
|
product_service: Annotated[ProductService, Depends(get_product_service)],
|
||||||
pricing_service: Annotated[PricingService, Depends(get_pricing_service)]
|
pricing_service: Annotated[PricingService, Depends(get_pricing_service)],
|
||||||
|
tcgplayer_api_service: Annotated[TCGPlayerAPIService, Depends(get_tcgplayer_api_service)]
|
||||||
) -> TaskService:
|
) -> TaskService:
|
||||||
"""TaskService depends on ProductService and TCGPlayerService"""
|
"""TaskService depends on ProductService and TCGPlayerService"""
|
||||||
return TaskService(db, product_service, pricing_service)
|
return TaskService(db, product_service, pricing_service, tcgplayer_api_service)
|
||||||
|
|
||||||
# Form data dependencies
|
# Form data dependencies
|
||||||
def get_create_file_metadata(
|
def get_create_file_metadata(
|
||||||
|
@ -15,6 +15,7 @@ from app.dependencies import (
|
|||||||
get_product_service,
|
get_product_service,
|
||||||
get_storage_service,
|
get_storage_service,
|
||||||
get_inventory_service,
|
get_inventory_service,
|
||||||
|
get_tcgplayer_api_service
|
||||||
)
|
)
|
||||||
|
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
@ -69,7 +70,8 @@ async def startup_event():
|
|||||||
tcgplayer_service = get_tcgplayer_service(db, file_service)
|
tcgplayer_service = get_tcgplayer_service(db, file_service)
|
||||||
pricing_service = get_pricing_service(db, file_service, tcgplayer_service)
|
pricing_service = get_pricing_service(db, file_service, tcgplayer_service)
|
||||||
product_service = get_product_service(db, file_service, tcgplayer_service, storage_service)
|
product_service = get_product_service(db, file_service, tcgplayer_service, storage_service)
|
||||||
task_service = get_task_service(db, product_service, pricing_service)
|
tcgplayer_api_service = get_tcgplayer_api_service(db)
|
||||||
|
task_service = get_task_service(db, product_service, pricing_service, tcgplayer_api_service)
|
||||||
|
|
||||||
# Start task service
|
# Start task service
|
||||||
await task_service.start()
|
await task_service.start()
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from app.db.models import File, CardTCGPlayer, Price
|
from app.db.models import File, CardTCGPlayer, Price, TCGPlayerInventory
|
||||||
from app.services.util._dataframe import TCGPlayerPricingRow, DataframeUtil
|
from app.services.util._dataframe import TCGPlayerPricingRow, DataframeUtil
|
||||||
from app.services.file import FileService
|
from app.services.file import FileService
|
||||||
from app.services.tcgplayer import TCGPlayerService
|
from app.services.tcgplayer import TCGPlayerService
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
from app.db.utils import db_transaction
|
from app.db.utils import db_transaction
|
||||||
from typing import List, Dict
|
from typing import List, Dict
|
||||||
|
from decimal import Decimal, ROUND_HALF_UP
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
@ -115,34 +116,44 @@ class PricingService:
|
|||||||
|
|
||||||
def default_pricing_algo(self, row: pd.Series) -> pd.Series:
|
def default_pricing_algo(self, row: pd.Series) -> pd.Series:
|
||||||
"""Default pricing algorithm with complex pricing rules"""
|
"""Default pricing algorithm with complex pricing rules"""
|
||||||
tcg_low = row.get('tcg_low_price')
|
|
||||||
tcg_low_shipping = row.get('tcg_low_price_with_shipping')
|
|
||||||
tcg_market_price = row.get('tcg_market_price')
|
|
||||||
|
|
||||||
if pd.isna(tcg_low) or pd.isna(tcg_low_shipping):
|
# Convert input values to Decimal for precise arithmetic
|
||||||
|
tcg_low = Decimal(str(row.get('tcg_low_price'))) if not pd.isna(row.get('tcg_low_price')) else None
|
||||||
|
tcg_low_shipping = Decimal(str(row.get('tcg_low_price_with_shipping'))) if not pd.isna(row.get('tcg_low_price_with_shipping')) else None
|
||||||
|
tcg_market_price = Decimal(str(row.get('tcg_market_price'))) if not pd.isna(row.get('tcg_market_price')) else None
|
||||||
|
|
||||||
|
if tcg_low is None or tcg_low_shipping is None:
|
||||||
logger.warning(f"Missing pricing data for row: {row}")
|
logger.warning(f"Missing pricing data for row: {row}")
|
||||||
row['new_price'] = None
|
row['new_price'] = None
|
||||||
return row
|
return row
|
||||||
|
|
||||||
# Apply pricing rules
|
# Define precision for rounding
|
||||||
if tcg_market_price < 1 and tcg_market_price > 0.25:
|
TWO_PLACES = Decimal('0.01')
|
||||||
new_price = tcg_market_price * 1.05
|
|
||||||
elif tcg_market_price < 0.25:
|
|
||||||
new_price = 0.25
|
|
||||||
elif tcg_low < 5 or tcg_low_shipping < 5:
|
|
||||||
new_price = round(tcg_low+((abs(tcg_market_price-tcg_low))*.75), 2)
|
|
||||||
elif tcg_low_shipping > 20:
|
|
||||||
new_price = round(tcg_low_shipping * 1.0125, 2)
|
|
||||||
else:
|
|
||||||
# new_price = round(tcg_low_shipping * 1.08, 2)
|
|
||||||
new_price = round(tcg_market_price * 1.03)
|
|
||||||
# if new price is less than half of market price, set to 90% market
|
|
||||||
if new_price < (tcg_market_price / 2):
|
|
||||||
new_price = round(tcg_market_price * 0.85, 2)
|
|
||||||
if new_price < 0.25:
|
|
||||||
new_price = 0.25
|
|
||||||
|
|
||||||
row['new_price'] = new_price
|
# Apply pricing rules
|
||||||
|
if tcg_market_price < Decimal('1') and tcg_market_price > Decimal('0.25'):
|
||||||
|
new_price = tcg_market_price * Decimal('1.05')
|
||||||
|
elif tcg_market_price < Decimal('0.25'):
|
||||||
|
new_price = Decimal('0.25')
|
||||||
|
elif tcg_low < Decimal('5') or tcg_low_shipping < Decimal('5'):
|
||||||
|
new_price = tcg_low + ((abs(tcg_market_price - tcg_low)) * Decimal('0.75'))
|
||||||
|
elif tcg_low_shipping > Decimal('20'):
|
||||||
|
new_price = tcg_low_shipping * Decimal('1.0125')
|
||||||
|
else:
|
||||||
|
new_price = tcg_market_price * Decimal('1.03')
|
||||||
|
|
||||||
|
# if new price is less than half of market price, set to 90% market
|
||||||
|
if new_price < (tcg_market_price / Decimal('2')):
|
||||||
|
new_price = tcg_market_price * Decimal('0.85')
|
||||||
|
|
||||||
|
if new_price < Decimal('0.25'):
|
||||||
|
new_price = Decimal('0.25')
|
||||||
|
|
||||||
|
# Ensure exactly 2 decimal places
|
||||||
|
new_price = new_price.quantize(TWO_PLACES, rounding=ROUND_HALF_UP)
|
||||||
|
|
||||||
|
# Convert back to float or string as needed for your dataframe
|
||||||
|
row['new_price'] = float(new_price)
|
||||||
return row
|
return row
|
||||||
|
|
||||||
def apply_pricing_algo(self, row: pd.Series, pricing_algo: callable = None) -> pd.Series:
|
def apply_pricing_algo(self, row: pd.Series, pricing_algo: callable = None) -> pd.Series:
|
||||||
@ -218,6 +229,19 @@ class PricingService:
|
|||||||
# Now do your column selection
|
# Now do your column selection
|
||||||
df = df[desired_columns]
|
df = df[desired_columns]
|
||||||
|
|
||||||
|
if update_type == 'update':
|
||||||
|
with db_transaction(self.db):
|
||||||
|
self.db.query(TCGPlayerInventory).delete()
|
||||||
|
self.db.flush()
|
||||||
|
# copy df to modify before inserting
|
||||||
|
df_copy = df.copy()
|
||||||
|
df_copy['id'] = df_copy.apply(lambda x: str(uuid4()), axis=1)
|
||||||
|
# rename columns lowercase no space
|
||||||
|
df_copy.columns = df_copy.columns.str.lower().str.replace(' ', '_')
|
||||||
|
for index, row in df_copy.iterrows():
|
||||||
|
tcgplayer_inventory = TCGPlayerInventory(**row.to_dict())
|
||||||
|
self.db.add(tcgplayer_inventory)
|
||||||
|
|
||||||
# remove any rows with no price
|
# remove any rows with no price
|
||||||
#df = df[df['TCG Marketplace Price'] != 0]
|
#df = df[df['TCG Marketplace Price'] != 0]
|
||||||
#df = df[df['TCG Marketplace Price'].notna()]
|
#df = df[df['TCG Marketplace Price'].notna()]
|
||||||
|
@ -5,16 +5,18 @@ from sqlalchemy.orm import Session
|
|||||||
from app.services.product import ProductService
|
from app.services.product import ProductService
|
||||||
from app.db.models import File
|
from app.db.models import File
|
||||||
from app.services.pricing import PricingService
|
from app.services.pricing import PricingService
|
||||||
|
from app.services.tcgplayer_api import TCGPlayerAPIService
|
||||||
|
|
||||||
|
|
||||||
class TaskService:
|
class TaskService:
|
||||||
def __init__(self, db: Session, product_service: ProductService, pricing_service: PricingService):
|
def __init__(self, db: Session, product_service: ProductService, pricing_service: PricingService, tcgplayer_api_service: TCGPlayerAPIService):
|
||||||
self.scheduler = BackgroundScheduler()
|
self.scheduler = BackgroundScheduler()
|
||||||
self.logger = logging.getLogger(__name__)
|
self.logger = logging.getLogger(__name__)
|
||||||
self.tasks: Dict[str, Callable] = {}
|
self.tasks: Dict[str, Callable] = {}
|
||||||
self.db = db
|
self.db = db
|
||||||
self.product_service = product_service
|
self.product_service = product_service
|
||||||
self.pricing_service = pricing_service
|
self.pricing_service = pricing_service
|
||||||
|
self.tcgplayer_api_service = tcgplayer_api_service
|
||||||
|
|
||||||
async def start(self):
|
async def start(self):
|
||||||
self.scheduler.start()
|
self.scheduler.start()
|
||||||
@ -24,12 +26,16 @@ class TaskService:
|
|||||||
|
|
||||||
def register_scheduled_tasks(self):
|
def register_scheduled_tasks(self):
|
||||||
self.scheduler.add_job(self.hourly_pricing, 'cron', minute='45')
|
self.scheduler.add_job(self.hourly_pricing, 'cron', minute='45')
|
||||||
|
self.scheduler.add_job(self.inventory_pricing, 'cron', minute='40')
|
||||||
self.logger.info("Scheduled tasks registered.")
|
self.logger.info("Scheduled tasks registered.")
|
||||||
|
|
||||||
def hourly_pricing(self):
|
def hourly_pricing(self):
|
||||||
self.logger.info("Running hourly pricing task")
|
self.logger.info("Running hourly pricing task")
|
||||||
self.pricing_service.cron_load_prices()
|
self.pricing_service.cron_load_prices()
|
||||||
self.logger.info("Finished hourly pricing task")
|
self.logger.info("Finished hourly pricing task")
|
||||||
|
|
||||||
|
def inventory_pricing(self):
|
||||||
|
self.tcgplayer_api_service.cron_tcgplayer_api_pricing()
|
||||||
|
|
||||||
async def process_manabox_file(self, file: File):
|
async def process_manabox_file(self, file: File):
|
||||||
self.logger.info("Processing ManaBox file")
|
self.logger.info("Processing ManaBox file")
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
import logging
|
import logging
|
||||||
from app.db.models import Orders, OrderProducts, CardTCGPlayer
|
from app.db.models import Orders, OrderProducts, CardTCGPlayer, CardManabox, APIPricing, TCGPlayerInventory
|
||||||
from app.services.util._requests import RequestsUtil
|
from app.services.util._requests import RequestsUtil
|
||||||
from app.services.util._docker import DockerUtil
|
from app.services.util._docker import DockerUtil
|
||||||
from app.db.utils import db_transaction
|
from app.db.utils import db_transaction
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
|
from datetime import datetime
|
||||||
from uuid import uuid4 as uuid
|
from uuid import uuid4 as uuid
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@ -23,6 +24,7 @@ class TCGPlayerAPIService:
|
|||||||
self.is_in_docker = self.docker_util.is_in_docker()
|
self.is_in_docker = self.docker_util.is_in_docker()
|
||||||
self.config = TCGPlayerAPIConfig()
|
self.config = TCGPlayerAPIConfig()
|
||||||
self.cookies = self.get_cookies()
|
self.cookies = self.get_cookies()
|
||||||
|
self.session = None
|
||||||
|
|
||||||
def get_cookies(self) -> dict:
|
def get_cookies(self) -> dict:
|
||||||
if self.is_in_docker:
|
if self.is_in_docker:
|
||||||
@ -105,4 +107,209 @@ class TCGPlayerAPIService:
|
|||||||
if order:
|
if order:
|
||||||
self.save_order(order)
|
self.save_order(order)
|
||||||
processed_orders.append(order_id)
|
processed_orders.append(order_id)
|
||||||
return processed_orders
|
return processed_orders
|
||||||
|
|
||||||
|
def get_scryfall_data(self, scryfall_id: str):
|
||||||
|
url = f"https://api.scryfall.com/cards/{scryfall_id}?format=json"
|
||||||
|
response = self.requests_util.bare_request(url, method='GET')
|
||||||
|
return response
|
||||||
|
|
||||||
|
def get_tcgplayer_pricing_data(self, tcgplayer_id: str):
|
||||||
|
if not self.session:
|
||||||
|
self.session = self.requests_util.get_session()
|
||||||
|
response = self.session.get("https://tcgplayer.com")
|
||||||
|
headers = {
|
||||||
|
'accept': 'application/json, text/plain, */*',
|
||||||
|
'accept-language': 'en-US,en;q=0.8',
|
||||||
|
'priority': 'u=1, i',
|
||||||
|
'sec-ch-ua': '"Not(A:Brand";v="99", "Brave";v="133", "Chromium";v="133"',
|
||||||
|
'sec-ch-ua-mobile': '?0',
|
||||||
|
'sec-ch-ua-platform': '"macOS"',
|
||||||
|
'sec-fetch-dest': 'empty',
|
||||||
|
'sec-fetch-mode': 'cors',
|
||||||
|
'sec-fetch-site': 'same-site',
|
||||||
|
'sec-gpc': '1',
|
||||||
|
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0 Safari/537.36'
|
||||||
|
}
|
||||||
|
url = f"https://mp-search-api.tcgplayer.com/v2/product/{tcgplayer_id}/details?mpfev=3279"
|
||||||
|
self.requests_util.rate_limit()
|
||||||
|
response = self.session.get(url, headers=headers)
|
||||||
|
self.requests_util.previous_request_time = datetime.now()
|
||||||
|
return response
|
||||||
|
|
||||||
|
# pricing
|
||||||
|
def get_tcgplayer_pricing_data_for_product(self, product_id: str):
|
||||||
|
# get tcgplayer pricing data for a single card by product id
|
||||||
|
# product_id to manabox card
|
||||||
|
manabox_card = self.db.query(CardManabox).filter(CardManabox.product_id == product_id).first()
|
||||||
|
tcgplayer_card = self.db.query(CardTCGPlayer).filter(CardTCGPlayer.product_id == product_id).first()
|
||||||
|
if not manabox_card or not tcgplayer_card:
|
||||||
|
logger.warning(f"Card with product id {product_id} missing in either Manabox or TCGPlayer")
|
||||||
|
return None
|
||||||
|
# get scryfall id, tcgplayer id, and tcgplayer sku
|
||||||
|
scryfall_id = manabox_card.scryfall_id
|
||||||
|
tcgplayer_sku = tcgplayer_card.tcgplayer_id
|
||||||
|
tcgplayer_id = self.get_scryfall_data(scryfall_id).json().get('tcgplayer_id')
|
||||||
|
tcgplayer_pricing = self.get_tcgplayer_pricing_data(tcgplayer_id)
|
||||||
|
if not tcgplayer_pricing:
|
||||||
|
logger.warning(f"TCGPlayer pricing data not found for product id {product_id}")
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
logger.info(f"TCGPlayer pricing data found for product id {product_id}")
|
||||||
|
return tcgplayer_pricing.json()
|
||||||
|
|
||||||
|
def save_tcgplayer_pricing_data(self, product_id: str, pricing_data: dict):
|
||||||
|
with db_transaction(self.db):
|
||||||
|
pricing_record = APIPricing(
|
||||||
|
id=str(uuid()),
|
||||||
|
product_id=product_id,
|
||||||
|
pricing_data=str(pricing_data)
|
||||||
|
)
|
||||||
|
self.db.add(pricing_record)
|
||||||
|
|
||||||
|
def cron_tcgplayer_api_pricing(self):
|
||||||
|
# Join both tables but retrieve both objects
|
||||||
|
results = self.db.query(TCGPlayerInventory, CardTCGPlayer).join(
|
||||||
|
CardTCGPlayer,
|
||||||
|
TCGPlayerInventory.tcgplayer_id == CardTCGPlayer.tcgplayer_id
|
||||||
|
).all()
|
||||||
|
|
||||||
|
for inventory, card in results:
|
||||||
|
# Now use card.product_id (from CardTCGPlayer)
|
||||||
|
pricing_data = self.get_tcgplayer_pricing_data_for_product(card.product_id)
|
||||||
|
if pricing_data:
|
||||||
|
self.save_tcgplayer_pricing_data(card.product_id, pricing_data)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# this one contains nearly everything, use it first
|
||||||
|
# what does score mean? - totally ignore score, it seems related to price and changes based on what is on the page. probably some psy op shit to get you to buy expensive stuff, not useful for us
|
||||||
|
# can i get volatility from here?
|
||||||
|
# no historical data here
|
||||||
|
"""
|
||||||
|
curl 'https://mp-search-api.tcgplayer.com/v2/product/615745/details?mpfev=3279' \
|
||||||
|
-H 'accept: application/json, text/plain, */*' \
|
||||||
|
-H 'accept-language: en-US,en;q=0.8' \
|
||||||
|
-b 'tcgpartner=PK=TRADECARDS&M=1; valid=set=true; product-display-settings=sort=price+shipping&size=10; OAuthLoginSessionId=63b1a89d-1ac2-43f7-8e79-55a9ca5e761d; __RequestVerificationToken_L2FkbWlu0=Lw1sfWh823UeJ7zRux0b1ZTI4Vg4i_dFt97a55aQpf-qBURVuwWDCJyuCxSwgLNLe9nPlfDSc1AMV5nyqhY4Q4jurxs1; spDisabledUIFeatures=orders; SellerProximity=ZipCode=&MaxSellerDistance=1000&IsActive=false; tcg-uuid=613192dc-ecf6-481a-bec1-afdee8686db7; LastSeller=e576ed4c; __RequestVerificationToken=VFv72VLK6McJVzthg8O-41p7BNkdoW2jQAlDAu-ylO39qfzCddRi2-7bWiH4qloc8Vo_ZftOAAa5OhXL3OByFHIdlwY1; TCGAuthTicket_Production=270B0566400C905C51DEAD644E3CDBD634ECBCCC796B1F77717F461EE104FCE101CFAD2A8458319330A0931018A99214D4EA5601E7551E25E2069ACA550BB71775C0A04F30724E2C4E262CB167EAC2C2EB05D15F9EA08363FC6455B94654F1F110CF079E24201C3B8CEF26762423D8CAA71DDF7B; ASP.NET_SessionId=5ycv15jf0mon3l5adodmkog5; StoreSaveForLater_PRODUCTION=SFLK=a167bf88521f4d0fbeb7497a7ed74629&Ignore=false; TCG_VisitorKey=81fe992f-9a12-4926-a417-7815c4f94edd; setting=CD=US&M=1; SearchSortSettings=M=1&ProductSortOption=MinPrice&ProductSortDesc=True&PriceSortOption=Shipping&ProductResultDisplay=grid; tcg_analytics_previousPageData=%7B%22title%22%3A%22Seller%20Feedback%22%2C%22href%22%3A%22https%3A%2F%2Fshop.tcgplayer.com%2Fsellerfeedback%2Fbe27fef9%22%7D; fileDownloadToken=1740499419709; StoreCart_PRODUCTION=CK=b4f8aff616974a12a6b2811129b81ee2&Ignore=false; tracking-preferences={%22version%22:1%2C%22destinations%22:{%22Actions%20Amplitude%22:false%2C%22AdWords%22:false%2C%22Google%20AdWords%20New%22:false%2C%22Google%20Enhanced%20Conversions%22:false%2C%22Google%20Tag%20Manager%22:false%2C%22Impact%20Partnership%20Cloud%22:false%2C%22Optimizely%22:false}%2C%22custom%22:{%22advertising%22:false%2C%22functional%22:false%2C%22marketingAndAnalytics%22:false}}; tcg-segment-session=1740595460137%257C1740595481177' \
|
||||||
|
-H 'origin: https://www.tcgplayer.com' \
|
||||||
|
-H 'priority: u=1, i' \
|
||||||
|
-H 'referer: https://www.tcgplayer.com/' \
|
||||||
|
-H 'sec-ch-ua: "Not(A:Brand";v="99", "Brave";v="133", "Chromium";v="133"' \
|
||||||
|
-H 'sec-ch-ua-mobile: ?0' \
|
||||||
|
-H 'sec-ch-ua-platform: "macOS"' \
|
||||||
|
-H 'sec-fetch-dest: empty' \
|
||||||
|
-H 'sec-fetch-mode: cors' \
|
||||||
|
-H 'sec-fetch-site: same-site' \
|
||||||
|
-H 'sec-gpc: 1' \
|
||||||
|
-H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36'
|
||||||
|
"""
|
||||||
|
|
||||||
|
# get volatility also
|
||||||
|
"""
|
||||||
|
curl 'https://mpgateway.tcgplayer.com/v1/pricepoints/marketprice/skus/8395586/volatility?mpfev=3279' \
|
||||||
|
-H 'accept: application/json, text/plain, */*' \
|
||||||
|
-H 'accept-language: en-US,en;q=0.8' \
|
||||||
|
-b 'tcgpartner=PK=TRADECARDS&M=1; valid=set=true; product-display-settings=sort=price+shipping&size=10; OAuthLoginSessionId=63b1a89d-1ac2-43f7-8e79-55a9ca5e761d; __RequestVerificationToken_L2FkbWlu0=Lw1sfWh823UeJ7zRux0b1ZTI4Vg4i_dFt97a55aQpf-qBURVuwWDCJyuCxSwgLNLe9nPlfDSc1AMV5nyqhY4Q4jurxs1; spDisabledUIFeatures=orders; SellerProximity=ZipCode=&MaxSellerDistance=1000&IsActive=false; tcg-uuid=613192dc-ecf6-481a-bec1-afdee8686db7; LastSeller=e576ed4c; __RequestVerificationToken=VFv72VLK6McJVzthg8O-41p7BNkdoW2jQAlDAu-ylO39qfzCddRi2-7bWiH4qloc8Vo_ZftOAAa5OhXL3OByFHIdlwY1; TCGAuthTicket_Production=270B0566400C905C51DEAD644E3CDBD634ECBCCC796B1F77717F461EE104FCE101CFAD2A8458319330A0931018A99214D4EA5601E7551E25E2069ACA550BB71775C0A04F30724E2C4E262CB167EAC2C2EB05D15F9EA08363FC6455B94654F1F110CF079E24201C3B8CEF26762423D8CAA71DDF7B; ASP.NET_SessionId=5ycv15jf0mon3l5adodmkog5; StoreSaveForLater_PRODUCTION=SFLK=a167bf88521f4d0fbeb7497a7ed74629&Ignore=false; TCG_VisitorKey=81fe992f-9a12-4926-a417-7815c4f94edd; setting=CD=US&M=1; tcg_analytics_previousPageData=%7B%22title%22%3A%22Seller%20Feedback%22%2C%22href%22%3A%22https%3A%2F%2Fshop.tcgplayer.com%2Fsellerfeedback%2Fbe27fef9%22%7D; fileDownloadToken=1740499419709; StoreCart_PRODUCTION=CK=b4f8aff616974a12a6b2811129b81ee2&Ignore=false; tracking-preferences={%22version%22:1%2C%22destinations%22:{%22Actions%20Amplitude%22:false%2C%22AdWords%22:false%2C%22Google%20AdWords%20New%22:false%2C%22Google%20Enhanced%20Conversions%22:false%2C%22Google%20Tag%20Manager%22:false%2C%22Impact%20Partnership%20Cloud%22:false%2C%22Optimizely%22:false}%2C%22custom%22:{%22advertising%22:false%2C%22functional%22:false%2C%22marketingAndAnalytics%22:false}}; SearchCriteria=M=1&WantVerifiedSellers=False&WantDirect=False&WantSellersInCart=False; SearchSortSettings=M=1&ProductSortOption=Sales&ProductSortDesc=False&PriceSortOption=Shipping&ProductResultDisplay=grid; tcg-segment-session=1740597680921%257C1740598418227' \
|
||||||
|
-H 'origin: https://www.tcgplayer.com' \
|
||||||
|
-H 'priority: u=1, i' \
|
||||||
|
-H 'referer: https://www.tcgplayer.com/' \
|
||||||
|
-H 'sec-ch-ua: "Not(A:Brand";v="99", "Brave";v="133", "Chromium";v="133"' \
|
||||||
|
-H 'sec-ch-ua-mobile: ?0' \
|
||||||
|
-H 'sec-ch-ua-platform: "macOS"' \
|
||||||
|
-H 'sec-fetch-dest: empty' \
|
||||||
|
-H 'sec-fetch-mode: cors' \
|
||||||
|
-H 'sec-fetch-site: same-site' \
|
||||||
|
-H 'sec-gpc: 1' \
|
||||||
|
-H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36'
|
||||||
|
"""
|
||||||
|
|
||||||
|
# handle historical data later
|
||||||
|
|
||||||
|
|
||||||
|
# detailed range quarter - detailed pricing info for the last quarter. seems simple
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""
|
||||||
|
# listings - lots of stuff here
|
||||||
|
"""
|
||||||
|
QUANTITY OVERVIEW
|
||||||
|
|
||||||
|
curl 'https://mp-search-api.tcgplayer.com/v1/product/615745/listings?mpfev=3279' \
|
||||||
|
-H 'accept: application/json, text/plain, */*' \
|
||||||
|
-H 'accept-language: en-US,en;q=0.8' \
|
||||||
|
-H 'content-type: application/json' \
|
||||||
|
-b 'tcgpartner=PK=TRADECARDS&M=1; valid=set=true; product-display-settings=sort=price+shipping&size=10; OAuthLoginSessionId=63b1a89d-1ac2-43f7-8e79-55a9ca5e761d; __RequestVerificationToken_L2FkbWlu0=Lw1sfWh823UeJ7zRux0b1ZTI4Vg4i_dFt97a55aQpf-qBURVuwWDCJyuCxSwgLNLe9nPlfDSc1AMV5nyqhY4Q4jurxs1; spDisabledUIFeatures=orders; SellerProximity=ZipCode=&MaxSellerDistance=1000&IsActive=false; tcg-uuid=613192dc-ecf6-481a-bec1-afdee8686db7; LastSeller=e576ed4c; __RequestVerificationToken=VFv72VLK6McJVzthg8O-41p7BNkdoW2jQAlDAu-ylO39qfzCddRi2-7bWiH4qloc8Vo_ZftOAAa5OhXL3OByFHIdlwY1; TCGAuthTicket_Production=270B0566400C905C51DEAD644E3CDBD634ECBCCC796B1F77717F461EE104FCE101CFAD2A8458319330A0931018A99214D4EA5601E7551E25E2069ACA550BB71775C0A04F30724E2C4E262CB167EAC2C2EB05D15F9EA08363FC6455B94654F1F110CF079E24201C3B8CEF26762423D8CAA71DDF7B; ASP.NET_SessionId=5ycv15jf0mon3l5adodmkog5; StoreSaveForLater_PRODUCTION=SFLK=a167bf88521f4d0fbeb7497a7ed74629&Ignore=false; TCG_VisitorKey=81fe992f-9a12-4926-a417-7815c4f94edd; setting=CD=US&M=1; SearchSortSettings=M=1&ProductSortOption=MinPrice&ProductSortDesc=True&PriceSortOption=Shipping&ProductResultDisplay=grid; tcg_analytics_previousPageData=%7B%22title%22%3A%22Seller%20Feedback%22%2C%22href%22%3A%22https%3A%2F%2Fshop.tcgplayer.com%2Fsellerfeedback%2Fbe27fef9%22%7D; fileDownloadToken=1740499419709; StoreCart_PRODUCTION=CK=b4f8aff616974a12a6b2811129b81ee2&Ignore=false; tracking-preferences={%22version%22:1%2C%22destinations%22:{%22Actions%20Amplitude%22:false%2C%22AdWords%22:false%2C%22Google%20AdWords%20New%22:false%2C%22Google%20Enhanced%20Conversions%22:false%2C%22Google%20Tag%20Manager%22:false%2C%22Impact%20Partnership%20Cloud%22:false%2C%22Optimizely%22:false}%2C%22custom%22:{%22advertising%22:false%2C%22functional%22:false%2C%22marketingAndAnalytics%22:false}}; tcg-segment-session=1740595460137%257C1740595481430' \
|
||||||
|
-H 'origin: https://www.tcgplayer.com' \
|
||||||
|
-H 'priority: u=1, i' \
|
||||||
|
-H 'referer: https://www.tcgplayer.com/' \
|
||||||
|
-H 'sec-ch-ua: "Not(A:Brand";v="99", "Brave";v="133", "Chromium";v="133"' \
|
||||||
|
-H 'sec-ch-ua-mobile: ?0' \
|
||||||
|
-H 'sec-ch-ua-platform: "macOS"' \
|
||||||
|
-H 'sec-fetch-dest: empty' \
|
||||||
|
-H 'sec-fetch-mode: cors' \
|
||||||
|
-H 'sec-fetch-site: same-site' \
|
||||||
|
-H 'sec-gpc: 1' \
|
||||||
|
-H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36' \
|
||||||
|
--data-raw '{"filters":{"term":{"sellerStatus":"Live","channelId":0,"language":["English"],"direct-seller":true,"directProduct":true,"listingType":"standard"},"range":{"quantity":{"gte":1},"direct-inventory":{"gte":1}},"exclude":{"channelExclusion":0,"listingType":"custom"}},"from":0,"size":1,"context":{"shippingCountry":"US","cart":{}},"sort":{"field":"price+shipping","order":"asc"}}'
|
||||||
|
|
||||||
|
|
||||||
|
AGGREGATION AND SOME SPECIFIC DATA IDK THIS MIGHT BE A GOOD ONE
|
||||||
|
|
||||||
|
curl 'https://mp-search-api.tcgplayer.com/v1/product/615745/listings?mpfev=3279' \
|
||||||
|
-H 'accept: application/json, text/plain, */*' \
|
||||||
|
-H 'accept-language: en-US,en;q=0.8' \
|
||||||
|
-H 'content-type: application/json' \
|
||||||
|
-b 'tcgpartner=PK=TRADECARDS&M=1; valid=set=true; product-display-settings=sort=price+shipping&size=10; OAuthLoginSessionId=63b1a89d-1ac2-43f7-8e79-55a9ca5e761d; __RequestVerificationToken_L2FkbWlu0=Lw1sfWh823UeJ7zRux0b1ZTI4Vg4i_dFt97a55aQpf-qBURVuwWDCJyuCxSwgLNLe9nPlfDSc1AMV5nyqhY4Q4jurxs1; spDisabledUIFeatures=orders; SellerProximity=ZipCode=&MaxSellerDistance=1000&IsActive=false; tcg-uuid=613192dc-ecf6-481a-bec1-afdee8686db7; LastSeller=e576ed4c; __RequestVerificationToken=VFv72VLK6McJVzthg8O-41p7BNkdoW2jQAlDAu-ylO39qfzCddRi2-7bWiH4qloc8Vo_ZftOAAa5OhXL3OByFHIdlwY1; TCGAuthTicket_Production=270B0566400C905C51DEAD644E3CDBD634ECBCCC796B1F77717F461EE104FCE101CFAD2A8458319330A0931018A99214D4EA5601E7551E25E2069ACA550BB71775C0A04F30724E2C4E262CB167EAC2C2EB05D15F9EA08363FC6455B94654F1F110CF079E24201C3B8CEF26762423D8CAA71DDF7B; ASP.NET_SessionId=5ycv15jf0mon3l5adodmkog5; StoreSaveForLater_PRODUCTION=SFLK=a167bf88521f4d0fbeb7497a7ed74629&Ignore=false; TCG_VisitorKey=81fe992f-9a12-4926-a417-7815c4f94edd; setting=CD=US&M=1; SearchSortSettings=M=1&ProductSortOption=MinPrice&ProductSortDesc=True&PriceSortOption=Shipping&ProductResultDisplay=grid; tcg_analytics_previousPageData=%7B%22title%22%3A%22Seller%20Feedback%22%2C%22href%22%3A%22https%3A%2F%2Fshop.tcgplayer.com%2Fsellerfeedback%2Fbe27fef9%22%7D; fileDownloadToken=1740499419709; StoreCart_PRODUCTION=CK=b4f8aff616974a12a6b2811129b81ee2&Ignore=false; tracking-preferences={%22version%22:1%2C%22destinations%22:{%22Actions%20Amplitude%22:false%2C%22AdWords%22:false%2C%22Google%20AdWords%20New%22:false%2C%22Google%20Enhanced%20Conversions%22:false%2C%22Google%20Tag%20Manager%22:false%2C%22Impact%20Partnership%20Cloud%22:false%2C%22Optimizely%22:false}%2C%22custom%22:{%22advertising%22:false%2C%22functional%22:false%2C%22marketingAndAnalytics%22:false}}; tcg-segment-session=1740595460137%257C1740595481430' \
|
||||||
|
-H 'origin: https://www.tcgplayer.com' \
|
||||||
|
-H 'priority: u=1, i' \
|
||||||
|
-H 'referer: https://www.tcgplayer.com/' \
|
||||||
|
-H 'sec-ch-ua: "Not(A:Brand";v="99", "Brave";v="133", "Chromium";v="133"' \
|
||||||
|
-H 'sec-ch-ua-mobile: ?0' \
|
||||||
|
-H 'sec-ch-ua-platform: "macOS"' \
|
||||||
|
-H 'sec-fetch-dest: empty' \
|
||||||
|
-H 'sec-fetch-mode: cors' \
|
||||||
|
-H 'sec-fetch-site: same-site' \
|
||||||
|
-H 'sec-gpc: 1' \
|
||||||
|
-H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36' \
|
||||||
|
--data-raw '{"filters":{"term":{"sellerStatus":"Live","channelId":0,"language":["English"]},"range":{"quantity":{"gte":1}},"exclude":{"channelExclusion":0}},"from":0,"size":10,"sort":{"field":"price+shipping","order":"asc"},"context":{"shippingCountry":"US","cart":{}},"aggregations":["listingType"]}'
|
||||||
|
|
||||||
|
|
||||||
|
AGGREGATION OF RANDOM SHIT IDK
|
||||||
|
|
||||||
|
curl 'https://mp-search-api.tcgplayer.com/v1/product/615745/listings?mpfev=3279' \
|
||||||
|
-H 'accept: application/json, text/plain, */*' \
|
||||||
|
-H 'accept-language: en-US,en;q=0.8' \
|
||||||
|
-H 'content-type: application/json' \
|
||||||
|
-b 'tcgpartner=PK=TRADECARDS&M=1; valid=set=true; product-display-settings=sort=price+shipping&size=10; OAuthLoginSessionId=63b1a89d-1ac2-43f7-8e79-55a9ca5e761d; __RequestVerificationToken_L2FkbWlu0=Lw1sfWh823UeJ7zRux0b1ZTI4Vg4i_dFt97a55aQpf-qBURVuwWDCJyuCxSwgLNLe9nPlfDSc1AMV5nyqhY4Q4jurxs1; spDisabledUIFeatures=orders; SellerProximity=ZipCode=&MaxSellerDistance=1000&IsActive=false; tcg-uuid=613192dc-ecf6-481a-bec1-afdee8686db7; LastSeller=e576ed4c; __RequestVerificationToken=VFv72VLK6McJVzthg8O-41p7BNkdoW2jQAlDAu-ylO39qfzCddRi2-7bWiH4qloc8Vo_ZftOAAa5OhXL3OByFHIdlwY1; TCGAuthTicket_Production=270B0566400C905C51DEAD644E3CDBD634ECBCCC796B1F77717F461EE104FCE101CFAD2A8458319330A0931018A99214D4EA5601E7551E25E2069ACA550BB71775C0A04F30724E2C4E262CB167EAC2C2EB05D15F9EA08363FC6455B94654F1F110CF079E24201C3B8CEF26762423D8CAA71DDF7B; ASP.NET_SessionId=5ycv15jf0mon3l5adodmkog5; StoreSaveForLater_PRODUCTION=SFLK=a167bf88521f4d0fbeb7497a7ed74629&Ignore=false; TCG_VisitorKey=81fe992f-9a12-4926-a417-7815c4f94edd; setting=CD=US&M=1; SearchSortSettings=M=1&ProductSortOption=MinPrice&ProductSortDesc=True&PriceSortOption=Shipping&ProductResultDisplay=grid; tcg_analytics_previousPageData=%7B%22title%22%3A%22Seller%20Feedback%22%2C%22href%22%3A%22https%3A%2F%2Fshop.tcgplayer.com%2Fsellerfeedback%2Fbe27fef9%22%7D; fileDownloadToken=1740499419709; StoreCart_PRODUCTION=CK=b4f8aff616974a12a6b2811129b81ee2&Ignore=false; tracking-preferences={%22version%22:1%2C%22destinations%22:{%22Actions%20Amplitude%22:false%2C%22AdWords%22:false%2C%22Google%20AdWords%20New%22:false%2C%22Google%20Enhanced%20Conversions%22:false%2C%22Google%20Tag%20Manager%22:false%2C%22Impact%20Partnership%20Cloud%22:false%2C%22Optimizely%22:false}%2C%22custom%22:{%22advertising%22:false%2C%22functional%22:false%2C%22marketingAndAnalytics%22:false}}; tcg-segment-session=1740595460137%257C1740595481430' \
|
||||||
|
-H 'origin: https://www.tcgplayer.com' \
|
||||||
|
-H 'priority: u=1, i' \
|
||||||
|
-H 'referer: https://www.tcgplayer.com/' \
|
||||||
|
-H 'sec-ch-ua: "Not(A:Brand";v="99", "Brave";v="133", "Chromium";v="133"' \
|
||||||
|
-H 'sec-ch-ua-mobile: ?0' \
|
||||||
|
-H 'sec-ch-ua-platform: "macOS"' \
|
||||||
|
-H 'sec-fetch-dest: empty' \
|
||||||
|
-H 'sec-fetch-mode: cors' \
|
||||||
|
-H 'sec-fetch-site: same-site' \
|
||||||
|
-H 'sec-gpc: 1' \
|
||||||
|
-H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36' \
|
||||||
|
--data-raw '{"filters":{"term":{"condition":["Near Mint","Lightly Played","Moderately Played","Heavily Played","Damaged"],"printing":["Foil"],"language":["English"],"sellerStatus":"Live"},"range":{"quantity":{"gte":1}},"exclude":{"channelExclusion":0}},"context":{"shippingCountry":"US","cart":{}},"aggregations":["seller-key"],"size":0}'
|
||||||
|
|
||||||
|
|
||||||
|
VOLATILITY
|
||||||
|
|
||||||
|
curl 'https://mpgateway.tcgplayer.com/v1/pricepoints/marketprice/skus/8547894/volatility?mpfev=3279' \
|
||||||
|
-H 'accept: application/json, text/plain, */*' \
|
||||||
|
-H 'accept-language: en-US,en;q=0.8' \
|
||||||
|
-b 'tcgpartner=PK=TRADECARDS&M=1; valid=set=true; product-display-settings=sort=price+shipping&size=10; OAuthLoginSessionId=63b1a89d-1ac2-43f7-8e79-55a9ca5e761d; __RequestVerificationToken_L2FkbWlu0=Lw1sfWh823UeJ7zRux0b1ZTI4Vg4i_dFt97a55aQpf-qBURVuwWDCJyuCxSwgLNLe9nPlfDSc1AMV5nyqhY4Q4jurxs1; spDisabledUIFeatures=orders; SellerProximity=ZipCode=&MaxSellerDistance=1000&IsActive=false; tcg-uuid=613192dc-ecf6-481a-bec1-afdee8686db7; LastSeller=e576ed4c; __RequestVerificationToken=VFv72VLK6McJVzthg8O-41p7BNkdoW2jQAlDAu-ylO39qfzCddRi2-7bWiH4qloc8Vo_ZftOAAa5OhXL3OByFHIdlwY1; TCGAuthTicket_Production=270B0566400C905C51DEAD644E3CDBD634ECBCCC796B1F77717F461EE104FCE101CFAD2A8458319330A0931018A99214D4EA5601E7551E25E2069ACA550BB71775C0A04F30724E2C4E262CB167EAC2C2EB05D15F9EA08363FC6455B94654F1F110CF079E24201C3B8CEF26762423D8CAA71DDF7B; ASP.NET_SessionId=5ycv15jf0mon3l5adodmkog5; StoreSaveForLater_PRODUCTION=SFLK=a167bf88521f4d0fbeb7497a7ed74629&Ignore=false; TCG_VisitorKey=81fe992f-9a12-4926-a417-7815c4f94edd; setting=CD=US&M=1; SearchSortSettings=M=1&ProductSortOption=MinPrice&ProductSortDesc=True&PriceSortOption=Shipping&ProductResultDisplay=grid; tcg_analytics_previousPageData=%7B%22title%22%3A%22Seller%20Feedback%22%2C%22href%22%3A%22https%3A%2F%2Fshop.tcgplayer.com%2Fsellerfeedback%2Fbe27fef9%22%7D; fileDownloadToken=1740499419709; StoreCart_PRODUCTION=CK=b4f8aff616974a12a6b2811129b81ee2&Ignore=false; tracking-preferences={%22version%22:1%2C%22destinations%22:{%22Actions%20Amplitude%22:false%2C%22AdWords%22:false%2C%22Google%20AdWords%20New%22:false%2C%22Google%20Enhanced%20Conversions%22:false%2C%22Google%20Tag%20Manager%22:false%2C%22Impact%20Partnership%20Cloud%22:false%2C%22Optimizely%22:false}%2C%22custom%22:{%22advertising%22:false%2C%22functional%22:false%2C%22marketingAndAnalytics%22:false}}; tcg-segment-session=1740595460137%257C1740595481430' \
|
||||||
|
-H 'origin: https://www.tcgplayer.com' \
|
||||||
|
-H 'priority: u=1, i' \
|
||||||
|
-H 'referer: https://www.tcgplayer.com/' \
|
||||||
|
-H 'sec-ch-ua: "Not(A:Brand";v="99", "Brave";v="133", "Chromium";v="133"' \
|
||||||
|
-H 'sec-ch-ua-mobile: ?0' \
|
||||||
|
-H 'sec-ch-ua-platform: "macOS"' \
|
||||||
|
-H 'sec-fetch-dest: empty' \
|
||||||
|
-H 'sec-fetch-mode: cors' \
|
||||||
|
-H 'sec-fetch-site: same-site' \
|
||||||
|
-H 'sec-gpc: 1' \
|
||||||
|
-H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36'
|
||||||
|
"""
|
@ -71,6 +71,23 @@ class RequestsUtil:
|
|||||||
self.browser_type = browser_type
|
self.browser_type = browser_type
|
||||||
self.docker_util = DockerUtil()
|
self.docker_util = DockerUtil()
|
||||||
self.previous_request_time = datetime.now()
|
self.previous_request_time = datetime.now()
|
||||||
|
|
||||||
|
def get_session(self, cookies: Dict = None) -> requests.Session:
|
||||||
|
"""Create a session with the specified cookies"""
|
||||||
|
session = requests.Session()
|
||||||
|
if cookies:
|
||||||
|
session.cookies.update(cookies)
|
||||||
|
return session
|
||||||
|
|
||||||
|
def bare_request(self, url: str, method: str, cookies: dict, data=None) -> requests.Response:
|
||||||
|
"""Send a request without any additional processing"""
|
||||||
|
try:
|
||||||
|
response = requests.request(method, url, cookies=cookies, data=data)
|
||||||
|
response.raise_for_status()
|
||||||
|
return response
|
||||||
|
except requests.RequestException as e:
|
||||||
|
logger.error(f"Request failed: {str(e)}")
|
||||||
|
return None
|
||||||
|
|
||||||
def get_tcgplayer_cookies_from_file(self) -> Dict:
|
def get_tcgplayer_cookies_from_file(self) -> Dict:
|
||||||
# check if cookies file exists
|
# check if cookies file exists
|
||||||
|
16
requests.md
16
requests.md
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user