Аз съм запален наблюдател на NBA. За да си дам извинение да хапна малко пица, често поръчвам от най-близката верига Dominos Pizza винаги, когато се провежда мач на първокласен мач на Селтикс (любимия ми отбор). Изневиделица ми хрумна: защо да не впрегна уменията си в Python, за да напиша скрипт, който автоматично да дава поръчки вместо мен, когато Селтикс играят мач, предаван по национална телевизия.

Първата задача беше да разбера кога сценарият наистина трябва да поръча пица. Обикновено обичам да поръчвам пица само когато Селтикс играят с добър отбор и тези мачове обикновено се предават по национална телевизия (което означава, че се показват по национална мрежа като ESPN, TNT или ABC). Така че просто трябва да накарам сценария да поръча пица, когато има мач на Селтикс по един от тези три канала.

За да получим тези данни, обикновено се нуждаем от API. След кратка борба разбрах, че няма безплатни спортни API, които да предоставят данните, които ми трябват. Така че реших, че уеб скрапингът е единственият вариант, особено след като не го правех за комерсиални цели.

Добре, сега към частта за кодиране. Как ще направя самото остъргване, ще попитате? Е, ще използвам Python и инструмент, известен като Selenium. Selenium ви позволява да автоматизирате сърфирането в мрежата чрез код. Реших, че мога да го използвам, за да взема данни от страницата с графика на НБА на Селтикс (https://www.nba.com/celtics/schedule). Ще продължа останалата част от статията с предположението, че използвате Chrome и сте конфигурирали правилно Selenium Web Driver. Отворете любимия си редактор, създайте нов python файл.

Добавете следното към вашия python файл:

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
WINDOW_SIZE = "1920,1080" chrome_options = Options()chrome_options.add_argument("--headless")chrome_options.add_argument("--window-size=%s" % WINDOW_SIZE)chrome_options.add_argument('log-level=3')driver = webdriver.Chrome(options=chrome_options)

Първо импортираме зависимости от Selenium. Обикновено Selenium отваря нов екземпляр на Chrome и изпълнява автоматизацията по този начин. Но аз искам скриптът да го прави във фонов режим, безшумно. И така, използвам няколко трика, за да накарам Chrome да отвори невидим прозорец, което е нещото, което виждате след импортирането.

Сега да се върнем към изстъргването на сайта. Елементът за бърза проверка ни дава информацията, от която се нуждаем. Всяка игра е обвита в свой собствен div елемент и има атрибут, наречен data-shortdate. Това е датата, на която се играе играта. Искаме обаче да поръчаме пица в дните, когато се играе мач И мачът се предава по национална телевизия. И така, намираме div, където data-shortdate е текущата дата, а атрибутът data-tv е или „ESPN“, „ABC“, или „TNT“.

За да получите играта, която се играе на текущата дата:

import datetime
import time
now = datetime.datetime.now()
day = now.strftime("%d")
if (int(day) < 10):
   day = day.replace("0", "")
currentDate = now.strftime("%b ") + day

Сега трябва да проверим дали играта ще бъде излъчена по национална телевизия:

driver.get("https://www.nba.com/" + credentials.favoriteTeam + "/schedule")
tvProvs = driver.find_element_by_css_selector("div[data-shortdate='" + currentDate + "'").get_attribute('data-tv')
pizzaShouldBeOrdered = (("ESPN" in tvProvs) or ("ABC" in tvProvs) or ("TNT" in tvProvs))

Все още не се притеснявайте за частта credentials.favoriteTeam. Ще се спра на това по-късно.

Сега имаме нужда от начин да поръчаме пица. Сигурен съм, че някои вериги имат API за правене на поръчки, но попаднах на страхотна библиотека на Python в GitHub, която е проста обвивка на Python за извършване на поръчки на Dominos. Нарича се PizzaPI и прави пускането на поръчки 10 пъти по-лесно.

За да правим поръчки, трябва да имаме правилния импорт (конфигурационен файл обикновено не е необходим, но прави този урок по-лесен):

from pizzapi import *
import config as credentials

А също и същинската част за поръчка на пица:

customer = Customer(credentials.credentials['firstName'], credentials.credentials['lastName'],
                                        credentials.credentials['email'], credentials.credentials['phone'])
                    address = Address(credentials.credentials['addressLine'], credentials.credentials['city'],
                                      credentials.credentials['state'], credentials.credentials['zip'])
                    store = address.closest_store()
                    order = Order(store, customer, address)
                    for item in credentials.items:
                        order.add_item(item)
                    card = PaymentObject(
                        credentials.card['number'], credentials.card['expiration'], credentials.card['cvv'], credentials.card['zip'])
                    orderText = order.place(card)

Както можете да видите, библиотеката прави пускането на поръчки лесно. Внимателно стартирайте този код обаче, тъй като той всъщност ще направи поръчка. За целите на тестването препоръчвам да използвате order.pay_with(card), тъй като прави всичко, което pay прави, освен че не поставя истинската поръчка. Страхотно е за тестови цели. Вашият файл config.py трябва да бъде настроен нещо подобно, персонализирайки информацията както искате:

credentials = {'firstName': 'Denzel', 'lastName': 'Washington', 'phone': '8337283933', 'email': '[email protected]', 'addressLine': '69 Flower Ln.', 'city': 'Los Angeles', 'state': 'CA', 'zip': '12345'}
items = ['B8PCPT', '12SCREEN']
card = {'number': '4100123422343234', 'expiration': '0115', 'cvv': '0115', 'zip': '90210'}
favoriteTeam = 'celtics'

Почти сме готови, но има само един проблем. Когато поръчваме чрез библиотеката, тя прави поръчката веднага. Искаме процесът да бъде автоматизиран и пицата да идва веднага в началото на играта. За да автоматизираме лесно целия процес, просто ще завъртаме проверката на графика на всеки три часа само в случай, че графикът се промени и ще проверяваме на всеки петнадесет минути дали е добра идея да направите поръчката:

dayLastOrdered = ''
while True:
   now = datetime.datetime.now()
   driver.get("https://www.nba.com/" + credentials.favoriteTeam + "/schedule")
   while True:
      #check if current date is same as day last ordered var. if not, order the pizza
      time.sleep(900)
time.sleep(10800)

За да проверим дали времето е подходящо за поръчка, трябва да получим прогнозното време, необходимо за приготвяне на пицата, и да видим дали времето, в което се намираме, оправдава поръчката. Така че трябва да вземем предвид текущото време, времето, необходимо за приготвяне на пицата, и времето, в което е играта. Времето, необходимо за подготовка на поръчката, е достъпно с метода order.pay_with(), а времето за игра е достъпно чрез селектора за данни и време. Също така приемаме, че игрите са вечер, за да направим всичко малко по-лесно (обикновено е така). Намираме времето до играта в часове:

timeOfGamehelper = driver.find_element_by_css_selector(
 "div[data-shortdate='" + currentDate + "'").get_attribute('data-time')
 # assume game is in the evening
 indexOfTimeSeperator = timeOfGamehelper.find(':')
 timeOfGame = int(
 (str(timeOfGamehelper)[:indexOfTimeSeperator])) + 12
 if(str(timeOfGamehelper)[indexOfTimeSeperator + 1:] != '00'):
 timeOfGame += int(str(timeOfGamehelper)
 [indexOfTimeSeperator + 1:]) / 60
 currentHour = int(now.hour) + int(now.minute) / 60
 #time until game in hours used to decide whether to order yet
 timeUntilGame = timeOfGame - currentHour

След това намираме очакваното време за изпълнение на нашата поръчка в часове:

orderText = order.pay_with(card)
timeEstimate = (orderText['Order']['EstimatedWaitMinutes'])
hoursLeft = int((str(timeEstimate)[
timeEstimate.find('-') + 1:])) / 60

След това правим поръчката, ако времето е подходящо:

if(timeUntilGame < hoursLeft):
   order.place(card)
   dayLastOrdered = currentDate

Благодаря ви, че прочетохте. Пълният код е наличен в моя GitHub.