Рубрики
Без рубрики

Сэкономьте деньги и разочарование на Amazon, используя функции Azure

Amazon – это услуга, известная для удобства и эффективности. Тем не менее, часто расстраивает покупать P … Tagged with Azure, Python, SQL, учебник.

Amazon – это услуга, известная для удобства и эффективности. Тем не менее, часто разочаровывает покупать продукт на Amazon, только чтобы узнать, что другой сайт продает его за меньшие деньги, или что товар был продан со скидкой вскоре после этого. Эта дилемма противоречит преимуществам онлайн -покупок, упомянутых выше, с небольшой эффективностью и удобством для вручную искать лучшие цены.

Недавно я узнал о Microsoft Azure и нашел идеальный способ сэкономить себе много денег и разочарования! Благодаря серверным вычислениям автоматизация задачи наблюдения за ценой продукта становится веселой и доступной проблемой. Результатом, который я придумал, был цены, веб -приложение, которое принимает ссылку на Amazon и некоторую базовую информацию и отправляет сообщение, если элемент колеблется в цене. В этом уроке я проведу вас, как создать собственное приложение цены, чтобы лучше понять Azure, а также колебания цен на Amazon.

Примечание. Прежде чем начать, не забудьте отреагировать и прокомментировать эту статью! Я хотел бы любые отзывы, чтобы улучшить ваш опыт.

Обзор

Блок -схема:

Интерфейс программы является основной формой с полями для ссылки на продукт, номер телефона, количество дней, которые пользователь хочет получить уведомления, и базовый процент скидки для элемента. Веб -сайт запускает функцию Azure HTTP Trigger, которая вводит данные в базу данных SQL. Затем триггер таймера проверяет цену элемента каждый день, обновляет базу данных и отправляет пользователю текстовое сообщение в трех условиях. Пользователь уведомляется, если цена предмета увеличивается с момента, когда пользователь подает форму, цена уменьшается до желаемого значения дисконтирования, или будет достигнут период уведомления.

Берегись! К концу этого урока у вас будет:

  • Установленный код Visual Studio
  • Создал учетную запись и подписку Microsoft Azure (бесплатная пробная версия)
  • Создал учетную запись Twilio (бесплатная пробная версия)
  • Создал учетную запись GitHub

Шаг 1: Настройка оснований:

Создайте учетную запись Azure:

Чтобы что -либо сделать, нам нужен доступ к Azure! Это означает Создание учетной записи и подписки . Как упоминалось выше, Azure поставляется с бесплатным кредитом в 200 долларов, который достаточно для наших целей. Если у вас уже есть учетная запись, то создайте Новая подписка или Ресурсная группа Чтобы хранить все, что связано с вашим проектом.

Установите VSCODE

Мы используем Python в качестве языка для наших функций, и, к сожалению, портал Azure очень ограничен с точки зрения использования с Python, чем с другими стеками выполнения. Из -за этой проблемы мы используем код Visual Studio в качестве альтернативы. VSCODE имеет большую интеграцию с Azure, так как Microsoft была создана как редактор, так и платформа. Мы будем использовать портал Azure для административных конфигураций (в основном с созданием ресурсов и управлением удаленным приложением), но VSCODE – это то, где будет выполнена большая часть работы.

Итак, с учетом этой информации, пришло время загрузить VSCODE! Это относительно простой процесс; Все, что вам нужно сделать, это искать «Скачать vscode» или следить за Эта ссылка Анкет После установки редактора я бы Высоко Recamend Подписание в GitHub через редактор. Хотя этот учебник не включает в себя что -либо контролирующее версию, это самое разумное решение отслеживать ваш код, чтобы вы могли:

  1. Вернитесь к своей последней рабочей версии
  2. Сохранить свой код на случай Ваша подписка отключена

Создайте базу данных

Поскольку цены – это веб -приложение, чрезвычайно важно найти способ отслеживать данные для всех пользователей. Это еще один экземпляр, когда использование Azure является преимуществом: мы можем размещать базу данных с минимальными накладными затратами и легкой настройкой.

Первым шагом является создание фактической базы данных на портале Azure. Однако важно отметить, что есть много вариантов На лазуре для баз данных. Обязательно выполните поиск «баз данных SQL» в верхней строке поиска.

После выбора «баз данных SQL» нажмите + в верхнем левом углу экрана:

Наконец, заполните основную информацию для вашей базы данных. Если у вас еще нет, создайте подписку, затем назовите свою базу данных и выберите сервер, ближайший к вашему местоположению. Самое главное , убедитесь, что правильно настроить свою базу данных (ссылка в разделе Compute+Storage в форме), чтобы она потребовала наименьшую сумму денег, которую вы можете потратить. Поскольку этот проект, скорее всего, будет для личного использования, вы можете минимизировать спецификации базы данных, чтобы сократить расходы до пары долларов:

Через пару минут база данных должна быть создана, и вы будете приведены к странице, аналогичной этой

База данных почти настроена, сохраните один важный шаг; Настройка аутентификации SQL. Если вы перейдете в редактор запросов из левого меню, вам будет предложено создать имя пользователя и пароль для доступа и изменения базы данных.

Обязательно помните свое имя пользователя и пароль для базы данных !! Вы будете использовать его в будущих шагах.

Подключите базу данных к VSCODE:

Чтобы регулярно получить доступ к базе данных, легко использовать расширение в VSCODE и иметь его прямо вместе с остальной частью нашего приложения. На левой панели в VSCODE перейдите на значок, напоминающий несколько блоков, и ищите «базы данных Azure». Оттуда нажмите «Установить» и дождитесь появления расширения в виде значка на панели.

Затем нам нужно фактически связать нашу учетную запись Azure с VSCODE. Нажмите Ctrl + Shift + P, чтобы открыть панель команд, затем введите «SQL» и выберите «MS SQL: Connect». Вам может быть предложено сообщение, сообщающее вам, чтобы вы вошли в свои учетные данные Microsoft, со ссылкой на внешний сайт. В этом случае войдите и авторизуйте все разрешения.

Затем перейдите к расширению на левой панели и наведите «соединения». Нажмите на появившийся символ «+», и появится меню, похожее на панель команд.

При запросе ссылки на базу данных перейдите в базу данных в портале Azure, и имя сервера находится в обзоре правого правого.

После ввода имени сервера нажмите Enter, чтобы продолжить или поместить имя БД, если хотите. Затем выберите «SQL Login» и используйте свое имя пользователя и пароль для входа в систему SQL для аутентификации. Следуйте следующей паре подсказок, связанных с входом в систему SQL, затем вам следует подключить с именем вашей базы данных и зеленой точкой, отображаемой при открытии расширения:

Шаг 2: Создание столбцов для базы данных

Наконец, время начать использовать нашу базу данных! Нажмите на свою базу данных, затем перейдите в каталог «таблицы». Обратите внимание на один набор данных, в котором говорится DBO: Это тот набор, от которого мы разветвляем, чтобы создать нашу таблицу. Чтобы создать таблицу, мы собираемся создать SQL -запросы, указанные на столбцах и их датах. Щелкните правой кнопкой мыши имя базы данных и выберите «Новый запрос», и должен появиться новый документ. В этом документе скопируйте и вставьте следующий запрос:

CREATE TABLE dbo.ScrapedData (
    phonenumber FLOAT(100),
    baseline_percentage FLOAT(100),
    duration FLOAT(100),
    original_price FLOAT(100),
    current_price FLOAT(100),
    link VARCHAR(400)
)

Стоимость дата несколько произвольных сохранения для ссылки и столбцов номера телефона. Поскольку номер телефона будет иметь не менее 10 цифр, лучше придерживаться высокой емкости. То же самое касается ссылки; Многие URL -адреса чрезвычайно длинные и на самом деле вышли бы на максимум 255 символов.

Шаг 3: Создание функций

Теперь пришло время настроить наши функции. Мы будем создавать триггер HTTP, который имеет URL в качестве конечной точки, а также триггер таймера, который независимо отключается после указанного интервала времени. Как упоминалось ранее Python не так легко интегрирован с порталом Azure, как другие стеки выполнения, поэтому мы будем использовать расширения VSCode для программирования нашего приложения.

Пройти через Это руководство по быстрому началу , который должен занять около тридцати минут, чтобы узнать, как настроить функцию Python в VSCODE. Как и в случае с управлением версиями, мы не будем явно переходить на развертывание вашего приложения с локальной машины в удаленную версию, но эту информацию можно найти в руководстве. После того, как вы следовали инструкциям, вы должны были создать Http -запуска функция. Обязательно сделайте папку для всего вашего кода для этого приложения.

Создание триггера таймера

Чтобы создать триггер таймера, выполните те же шаги, описанные в руководстве, но измените тип от HTTP на таймер в панели меню. Если вам предложено для интервала времени N-Crontab для при том, что функция запускается, используйте Эта ссылка Чтобы генерировать желаемый интервал.

Настройка Twilio

Twilio – это потрясающая служба, которую мы будем использовать для отправки SMS -сообщений пользователям. Поскольку нам понадобится учетная запись Twilio, чтобы получить доступ к его API в наших функциях, мы будем настраивать ее прямо сейчас. Перейти к Веб -сайт Twilio и создать новую учетную запись. Если у вас уже есть учетная запись, не стесняйтесь использовать ее, пока у вас есть выставление счетов. В противном случае создайте новую учетную запись, чтобы получить бесплатную пробную версию.

Важное примечание: отсутствие выставления счетов с вашей учетной записью ограничит вас отправкой сообщений только на авторизованные номера вручную. Это ограничит ваше приложение людям, которых вы лично знаете.

Шаг 4: Внедрение данных в базу данных

Наконец -то пришло время начать кодирование! Мы начнем с программирования нашего HTTP -триггера, который будет обрабатывать запрос пользователя через Webform, и создаст строку в базе данных с использованием предоставленной информации.

Перейдите на самый верхний значок на левой панели VSCODE, нажмите «Добавить папку» в рабочее пространство и перейдите к тому, где вы сохранили свои функции. Затем в вашей функции перейдите вложенную папку с тем же именем, что и основной каталог, и Open init.py.

Когда вы откроете свой файл init.py, у вас будет шаблон для создания основного HTTP -триггера. Здесь будет жить весь наш код для триггера.

Подключение к базе данных:

Теперь, когда у нас есть наша функция, готовая к работе, нам нужно настроить соединение с нашей базой данных. Первый шаг – установить очень важный пакет Python в pyodbc . Перейти к Эта ссылка Для команды PIP и установите ее на Справочник вашего триггера в терминале. После завершения установки убедитесь, что Импорт pyodbc На вершине init.py.

PYODBC, пожалуй, самая сложная зависимость во всем этом проекте, но суть заключается в том, что он позволяет вам подключаться к базе данных и записывать запросы SQL в Python. Ниже приведен код, инициализирующий экземпляр подключения, который мы будем использовать в нашей функции:

import pyodbc

server = ''
database = ''
username = ''
password = ''
cnxn = pyodbc.connect('DRIVER={ODBC Driver 17 for SQL Server};SERVER='+server+';DATABASE='+database+';UID='+username+';PWD='+ password)
cursor = cnxn.cursor()

Переменная DBCNXN – это фактическое соединение с базой данных, а «курсор» – это объект, который позволяет выполнять действия через это соединение, чтобы фактически запросить базу данных. Перед запуском убедитесь, что ваша локальная среда находится в базе данных. Убедитесь, что все они проверяют расширение базы данных и убедитесь, что в базе данных есть зеленая точка вместо красной)

Важное примечание : Поскольку мы программируем это локально, база данных доступна через различные IP -адреса, поскольку различные порты используются каждый раз, когда мы запускаем функцию. Оставая, это огромная проблема, так как наша база данных требует ручной авторизации IP -адресов, чтобы пройти через брандмауэр. Поэтому нам нужно настроить настройки брандмауэра, чтобы все IP -адреса разрешали запросить базу данных, поскольку мы никогда не знаем, какой IP -адрес наш локальный сервер будет получить доступ к конечной точке базы данных. Перейдите в базу данных на портале Azure и выберите обзор на левой вкладке. На правом Menubar выберите «Установить брандмауэр сервера»:

Вы будете перенаправлены на страницу под названием «Настройки брандмауэра», где вы можете дать диапазон авторизованных IP -адресов. Перейдите на «Имя правила» и введите описание, например, «All All», затем вставьте 0.0.0.0 Как стартовый IP и 255.255.255.255 IP -адресов, которые допускают каждую возможную комбинацию в IPv6.

Теперь пришло время проверить код. Ваш код должен выглядеть так на данный момент:

import logging

import azure.functions as func
import pyodbc

server = ''
database = ''
username = ''
password = ''
cnxn = pyodbc.connect('DRIVER={ODBC Driver 17 for SQL Server};SERVER='+server+';DATABASE='+database+';UID='+username+';PWD='+ password)
cursor = cnxn.cursor()

def main(req: func.HttpRequest) -> func.HttpResponse:
    logging.info('Python HTTP trigger function processed a request.')

    name = req.params.get('name')
    if not name:
        try:
            req_body = req.get_json()
        except ValueError:
            pass
        else:
            name = req_body.get('name')

    if name:
        return func.HttpResponse(f"Hello, {name}. This HTTP triggered function executed successfully.")
    else:
        return func.HttpResponse(
             "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response.",
             status_code=200
        )

Вернуться к Эта ссылка выполнить вашу функцию. В вашем терминале вы, скорее всего, увидите ошибки соединения, но эти проблемы слишком широко различаются, чтобы охватить в этом учебнике. PYODBC может быть очень разочаровывающим, поэтому я советую сделать Google и StackOverflow вашими лучшими друзьями! Ищите любую ошибку, которая возникает, и следуйте проверенным инструкциям, пока что -то не придержится. Если вы испытываете какие -либо проблемы с Pylint, обратитесь к Эта ссылка Чтобы удалить и создать новую виртуальную среду для вашей функции. Вы готовы идти, как только ваш терминал выглядит похоже на следующее:

Указание параметров запроса:

Окончательно! Наши технологии настроены, и мы можем запросить нашу базу данных с помощью Python. Вот быстрое напоминание о том, что должен делать этот триггер:

  1. Возьмите параметры запроса, отправленные пользователем через веб -форму
  2. Использование предоставленного URL
  3. Обновите базу данных новой строкой со всеми параметрами запроса вместе с исходной ценой и текущей ценой для предмета (которые эквивалентны)

Чтобы принять различные параметры запроса, нам нужно отредактировать function.json Файл, чтобы включить всю информацию о форме. Это должно быть прямо под файлом init.py в вашем каталоге. Отредактируйте и сохраните свой файл так, чтобы он соответствовал следующему JSON:

{
    "scriptFile": "__init__.py",
    "bindings": [
      {
        "authLevel": "anonymous",
        "type": "httpTrigger",
        "direction": "in",
        "name": "req",
        "phonenumber": "req",
        "url": "req",
        "baseline_percentage": "req",
        "duration": "req",
        "methods": [
          "get",
          "post"
        ]
      },
      {
        "type": "http",
        "direction": "out",
        "name": "$return",
        "url": "$return",
        "phonenumber": "req"
      }
    ]
  }

Цены на рассылку с помощью URL:

Теперь, когда мы можем получить доступ ко всей информации, которую вводит пользователь, нам также необходимо получить информацию о цене. Пользователь вводит только URL, поэтому наша задача – каким -то образом получить цену с ней.

Мы будем использовать еще одну великую библиотеку Python под названием Красивый суп , что позволяет нам провести сеть или проанализировать HTML с сайта. Ветка настолько важна для этого проекта, что цены были названы в честь него! Поскольку рассеяние веб -сайтов так важно, выбор самый простой и наиболее эффективный способ соскоба цены является приоритетом. Красивый суп очень удобен для начинающих, и это самая большая причина, по которой мы используем функции Python.

Чтобы установить красивый суп, позвоните пип установить BS4 В вашем терминале или ищите «Красивый суп». После установки убедитесь, что импортируйте пакет:

from bs4 import BeautifulSoup

Вам также нужно будет загрузить и импортировать Запросы упаковка:

import requests

Как только все будет импортировано, скопируйте и вставьте следующую функцию в init.py:

def scrape_price(URL: str):
    headers = ({'User-Agent':
            'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36',
            'Accept-Language': 'en-US, en;q=0.5'})

    page = requests.get(URL, headers=headers) #response.text gets page and html
    soup = BeautifulSoup(page.text, 'html.parser')

    price_tag = soup.find(id='priceblock_ourprice')
    if price_tag is None:
        price_tag = soup.find(id='priceblock_dealprice')
    if price_tag is None:
        price_tag = soup.find('data-asin-price')
    if price_tag is None:
        price_tag = soup.find(id='price_inside_buybox')
    if price_tag is None:
        price_tag = soup.find(class_='p13n-sc-price')
    if price_tag is None:
        return "incompatible"
    price = price_tag.get_text()
    #gets the lowest price if a range is listed
    if('-' in price):
        price = price[:price.index('-')]

    #turn into number
    price = price.replace('$', '')
    price = float(price)
    return price

Обратите внимание на переменную страницы, которая получает фактическую страницу, используя параметр URL. Суп – это красивый суп -объект, который берет страницу, и анализирует его HTML. Метод Soup.find () Выбирает отдельный элемент через атрибут (в данном случае идентификатор элемента).

Отнесее отметить, что функция не будет работать для некоторых сайтов. Amazon – это огромная платформа с различными макетами для сделок и скидок и т. Д., Затрудняя узнать, какой атрибут выбирать и обеспечить, чтобы ваша программа не будет работать в каждом случае. Чтобы убедиться, что программа не работает, мы проверяем несколько элементов по цене, а затем возвращаем «несовместимый», если она не найдена. В противном случае значение очищается только в числовые символы, подчиняется значению поплавки и возвращается.

Обновление базы данных:

Теперь у нас есть вся информация, необходимая для создания строки в базе данных для запроса пользователя. Наряду с параметрами запроса, мы введем введу цену на WebStraped, один раз, один раз в качестве первоначальной ценой (цена продукта на момент обработки запроса), и один раз в качестве текущей цены (которая будет обновляться каждый раз, когда программа чеки на колебания цен).

Мы собираемся использовать переменные PYODBC, которые мы создали в предыдущих шагах для запроса в базу данных и вставить все значения:

def update_database(url:str, phonenumber:int, baseline_percentage:float, duration:float, original_price:float, current_price:float, item_name: str):
    row_str = ""
    cursor.execute("INSERT INTO dbo.ScrapedData VALUES (?,?,?,?,?,?,?)", int(phonenumber),float(baseline_percentage), float(duration), float(scrape_price(url)), float(scrape_price(url)), url, item_name) 

На самом деле это весь код, который вам нужен, чтобы вставить новую строку! Тем не менее, мы хотим убедиться, что пользователь может отслеживать один и тот же URL только один раз. Для этого мы собираемся создать еще один запрос, чтобы удалить ряд, если PhonNeman и Link существуют в другом месте. После этого обязательно позвоните ‘ cnxn.commit () ‘Чтобы фактически изменить содержание базы данных:

cnxn.commit()
    cursor.execute('''WITH removing AS (
    SELECT 
        phonenumber, 
        baseline_percentage, 
        duration, 
        original_price,
        current_price, 
        link,
        ROW_NUMBER() OVER (
            PARTITION BY 
                phonenumber,  
                link
            ORDER BY 
                phonenumber, 
                link
        ) row_num
        FROM 
            dbo.ScrapedData
        )
        DELETE FROM removing
        WHERE row_num > 1;''')

    cnxn.commit()

При тестировании нашей функции было бы удобно увидеть строки в таблице, не запуская запрос, нажимая кнопку в VSCODE или портале Azure. Для этого добавьте еще один запрос в функцию, которая выбирает все строки. Затем храните каждую строку в строке и верните значение:

 cnxn.commit()
    cursor.execute("SELECT * FROM [PriceScraper].[dbo].[ScrapedData]")
    row = cursor.fetchone()
    while row:
        row_str += row.__repr__() + "\n"
        row = cursor.fetchone()
    return row_str

Ваша полная функция должна выглядеть так:

def update_database(url:str, phonenumber:int, baseline_percentage:float, duration:float, original_price:float, current_price:float, item_name: str):
    row_str = ""
    cursor.execute("INSERT INTO dbo.ScrapedData VALUES (?,?,?,?,?,?,?)", int(phonenumber),float(baseline_percentage), float(duration), float(scrape_price(url)), float(scrape_price(url)), url, item_name) 

    cnxn.commit()
    cursor.execute('''WITH removing AS (
    SELECT 
        phonenumber, 
        baseline_percentage, 
        duration, 
        original_price,
        current_price, 
        link,
        ROW_NUMBER() OVER (
            PARTITION BY 
                phonenumber,  
                link
            ORDER BY 
                phonenumber, 
                link
        ) row_num
        FROM 
            dbo.ScrapedData
        )
        DELETE FROM removing
        WHERE row_num > 1;''')

    cnxn.commit()
    cursor.execute("SELECT * FROM [PriceScraper].[dbo].[ScrapedData]")
    row = cursor.fetchone()
    while row:
        row_str += row.__repr__() + "\n"
        row = cursor.fetchone()
    return row_str

Вызов наши функции и запросы на обработку:

Теперь, когда наши функции написаны, нам нужно их использовать! Все функции требуют параметров, поэтому первое, что нам нужно сделать, – это настроить способ доступа к параметрам запроса. В шаблоне для запуска HTTP, Главный Функция отвечает за получение этой информации:

import logging

import azure.functions as func


def main(req: func.HttpRequest) -> func.HttpResponse:
    logging.info('Python HTTP trigger function processed a request.')

    name = req.params.get('name')
    if not name:
        try:
            req_body = req.get_json()
        except ValueError:
            pass
        else:
            name = req_body.get('name')

    if name:
        return func.HttpResponse(f"Hello, {name}. This HTTP triggered function executed successfully.")
    else:
        return func.HttpResponse(
             "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response.",
             status_code=200
        )

Обратите внимание на переменную имя , который назначен как свойство req , или запрос на функцию. Используйте имя Как ссылка на получение наших параметров:

def main(req: func.HttpRequest) -> func.HttpResponse:
    logging.info('Python HTTP trigger function processed a request.')

    phonenumber = req.params.get('phonenumber')
    url = req.params.get('url')
    baseline_percentage = req.params.get('baseline_percentage')
    duration = req.params.get('duration')
    name = req.params.get('name')

Следующее, что нам нужно сделать, это проверить, отправляется ли каждый параметр, особенно URL. Если все параметры отправлены, мы можем вернуть ответ на запрос, который вызывает Update_Database (). В этом вызове функции Update_Database () дают параметры формы, а также исходную цену и (эквивалентную) текущую цену. Возвращение функционального вызова в качестве ответа выведет возвращаемое значение update_database (), которое мы установили, чтобы быть строками, содержащимися в базе данных.

all_vals = {"phone": phonenumber, "url": url, "percent": baseline_percentage, "dur": duration, "name": name}
    null_valls = ""

    if not phonenumber:
        null_valls += "phonenumber, "
    if not url:
        null_valls += "url, "
    if not baseline_percentage:
        null_valls += "baseline_percentage, "
    if not duration:
        null_valls += "duration, "
    if not name:
        null_valls += "name" 

    if url and scrape_price(url) == "incompatible":
        return func.HttpResponse(
             f'url unsupported',
             status_code=200
        )
    if url and phonenumber and baseline_percentage and duration and name:
        return func.HttpResponse(update_database(url, phonenumber, baseline_percentage, duration, scrape_price(url), scrape_price(url), name))

    else:
        return func.HttpResponse(
             null_valls,
             status_code=200
        )

И с этим наш HTTP -триггер завершен! Чтобы проверить триггер, запустите его в терминале, скопируйте и вставьте предоставленный URL -адрес. Я настоятельно рекомендую использовать Postman API Client с этим URL как конечной точки. Оттуда вы можете ввести параметры теста и посмотреть, правильным ли вывод. Другая альтернатива, конечно, – это тест в веб -браузере, но ввод параметров в строку поиска чрезвычайно разочаровывает и неэффективен.

Шаг 5: Проверка колебаний и отправки уведомлений

Шаг 4 заключался в том, чтобы дать нашим ценам некоторые данные, чтобы уйти от. Теперь мы работаем над поиском и обновлением этих данных! Благодаря программированию триггера таймера, мы будем:

  1. Проверьте, нужно ли нам уведомить какие -нибудь пользователей
  2. Отправить уведомления
  3. Обновите базу данных с текущими ценами для каждого элемента

Вот блок -схема, в которой изложена логика для нашего кода:

Настройка зависимостей:

Вам понадобится каждая зависимость, используемая в предыдущем триггере, а также некоторые новые пакеты. Перейдите к init.py в своей функции триггера таймера, затем скопируйте и вставьте каждый импорт. Вернитесь к инструкциям и убедитесь, что вы загрузили и импортировали:

  • Запросы
  • pyodbc
  • Красивый суп

После импорта PYODBC обязательно скопируйте следующий код, причем каждая переменная является Глобал :

server = ''
database = ''
username = ''
password = ''
cnxn = pyodbc.connect('DRIVER={ODBC Driver 17 for SQL Server};SERVER='+server+';DATABASE='+database+';UID='+username+';PWD='+ password)
cursor = cnxn.cursor()

Вам также нужно скачать Twilio Пакет и импортируйте следующее для отправки SMS -сообщений:

import os 
from twilio.rest import Client

Получение информации о базе данных:

Мы снова будем использовать объект курсора PYODBC для запроса в базу данных и создать параллельные списки для каждого столбца. Эти списки, которые затем могут быть использованы для проверки того, кого нужно уведомлять, и составить сообщение с соответствующей информацией.

def get_Database_Information():
    phonenumbers = []
    names = []
    links = []
    original_prices = []
    current_prices = []
    percentages = []
    durations = []

    cursor.execute('SELECT phonenumber,link,original_price FROM dbo.ScrapedData;')
    row = cursor.fetchone()
    row_str = ""
    while row:
        row_str += row.__repr__() + "\n"
        row = cursor.fetchone()

    # get all of each type of data
    cursor.execute('SELECT phonenumber FROM dbo.ScrapedData;')
    row = cursor.fetchone() 
    while row:
        row_str = row.__repr__()
        row_str = row_str.replace('(','')
        row_str = row_str.replace(')','')
        row_str = row_str.replace(',','')
        row_str = float(row_str)
        phonenumbers.append(row_str)
        row = cursor.fetchone()

    cursor.execute('SELECT item_name FROM dbo.ScrapedData;')
    row = cursor.fetchone()
    while row:
        row_str = row.__repr__()
        row_str = row_str.replace('(','')
        row_str = row_str.replace(')','')
        row_str = row_str.replace(',','')
        names.append(row)
        row = cursor.fetchone()

    cursor.execute('SELECT link FROM dbo.ScrapedData;')
    row = cursor.fetchone()
    while row:
        row_str = row.__repr__()
        row_str = row_str.replace('(','')
        row_str = row_str.replace(')','')
        row_str = row_str.replace(',','')
        links.append(row)
        row = cursor.fetchone()

    cursor.execute('SELECT original_price FROM dbo.ScrapedData;')
    row = cursor.fetchone()
    while row:
        row_str = row.__repr__()
        row_str = row_str.replace('(','')
        row_str = row_str.replace(')','')
        row_str = row_str.replace(',','')
        row_str = row_str.replace(' ', '')
        row_str = float(row_str)
        original_prices.append(row_str)
        row = cursor.fetchone()

    cursor.execute('SELECT current_price FROM dbo.ScrapedData;')
    row = cursor.fetchone()
    while row:
        row_str = row.__repr__()
        row_str = row_str.replace('(','')
        row_str = row_str.replace(')','')
        row_str = row_str.replace(',','')
        row_str = row_str.replace(' ', '')
        row_str = float(row_str)
        current_prices.append(row_str)
        row = cursor.fetchone()

    cursor.execute('SELECT baseline_percentage FROM dbo.ScrapedData;')
    row = cursor.fetchone()
    while row:
        row_str = row.__repr__()
        row_str = row_str.replace('(','')
        row_str = row_str.replace(')','')
        row_str = row_str.replace(',','')
        row_str = row_str.replace(' ', '')
        row_str = float(row_str)
        percentages.append(row_str)
        row = cursor.fetchone()

    cursor.execute('SELECT duration FROM dbo.ScrapedData;')
    row = cursor.fetchone()
    while row:
        row_str = row.__repr__()
        row_str = row_str.replace('(','')
        row_str = row_str.replace(')','')
        row_str = row_str.replace(',','')
        row_str = row_str.replace(' ', '')
        row_str = float(row_str)
        durations.append(row_str)
        row = cursor.fetchone()

    return phonenumbers, names, links, original_prices, current_prices, percentages, durations

В конце функция возвращает все списки.

Поиск, кто должен быть уведомлен:

Есть три случая, когда пользователь будет уведомлен о ценах:

  1. Цена увеличилась
  2. Цена соответствует идеальной цене пользователя
  3. Продолжительность, которую пользователь хотел уведомлений, была достигнута

Мы будем писать методы для каждого из этих случаев. Параметры для каждого метода разные, но все они требуют списков, выведенных нашим методом get_database_information ():

#if price increases from current
def price_increased_from_current(current_prices, links, phonenumbers, names):
    increased_notifs = []

    for i in range(len(current_prices)):
        updated_current_price = scrape_price(links[i], current_prices[i])
        if updated_current_price > current_prices[i]:
            increased_notifs.append([phonenumbers[i], names[i], current_prices[i], updated_current_price, links[i]])
    return increased_notifs
    #use to send to phone number 'price for  has increased from  to 

#if price is within baseline percentage
def price_increased_from_baseline(original_prices, links, phonenumbers, percentages, current_prices, names):
    baseline_notifs = []

    for i in range(len(original_prices)):
        original_price = original_prices[i]
        baseline_price = original_price - (original_price * percentages[i] * .01)
        updated_current_price = scrape_price(links[i], current_prices[i])
        current_price = current_prices[i]
        if current_price <= baseline_price:
            baseline_notifs.append([phonenumbers[i], names[i], updated_current_price, links[i]])
    return baseline_notifs

#if duration reached
def duration_reached(phonenumbers, durations, names, links, current_prices):
    duration_notifs = []

    today = datetime.date.today()

    for i in range(len(durations)):
        updated_current_price = scrape_price(links[i], current_prices[i])
        if(durations[i] == 0):
            duration_notifs.append([phonenumbers[i], names[i], updated_current_price, links[i]])

    return duration_notifs

Каждая функция выводит список контактов со всей информацией, необходимой для составления сообщения уведомления для каждого пользователя.

Примечание. Триггер таймера также использует функцию scrape_price (), которую вы можете скопировать и вставить снизу:

def scrape_price(URL: str, current_price):
    headers = ({'User-Agent':
            'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36',
            'Accept-Language': 'en-US, en;q=0.5'})

    '''
    URL = str(URL)
    if 'dp/' in URL:
        url_short = URL[URL.index('dp/'):]
        if '?' in url_short:
            url_short = url_short[: URL.index('?')]
    '''


    page = requests.get(URL[0], headers=headers) #response.text gets page and html
    soup = BeautifulSoup(page.text, 'html.parser')

    price_tag = soup.find(id='priceblock_ourprice')
    if price_tag is None:
        price_tag = soup.find(id='priceblock_dealprice')
    if price_tag is None:
        price_tag = soup.find('data-asin-price')
    if price_tag is None:
        price_tag = soup.find(id='price_inside_buybox')
    if price_tag is None:
        price_tag = soup.find(class_='p13n-sc-price')
    if price_tag is None:
        return current_price
    price = price_tag.get_text()
    #gets the lowest price if a range is listed
    if('-' in price):
        price = price[:price.index('-')]

    #turn into number
    price = price.replace('$', '')
    price = float(price)
    return price

Отправка сообщений:

Теперь пришло время создать нашу систему напоминаний! Фактическая функция отправки сообщения очень проста:

def send_message(phonenumber, message): 
    # Your Account Sid and Auth Token from twilio.com/console
    # and set the environment variables. See http://twil.io/secure
    account_sid = ''
    auth_token = ''
    client = Client(account_sid, auth_token)
    to_str = '+1{}'.format(phonenumber)
    message = client.messages \
                    .create(
                        body=message,
                        from_='',
                        to= to_str[0:len(to_str)-1]
                    )

    print(message.sid)

Обязательно введите свою учетную запись SID, Token Auth и номер телефона в функцию. Эту информацию можно найти через Twilio Console , но убедитесь, что Никогда не копируйте и вставьте их в код. Если вам случится толкать этот код с GitHub, любой может получить доступ к вашей информации. Кроме того, Twilio экраны, чтобы проверить, была ли ваша информация размещена в Интернете, и автоматически отключает эти учетные данные Анкет Вместо этого установите эти учетные данные как переменные среды Анкет

После того, как вспомогательная функция работает, мы перейдем к созданию более крупного метода, который итерация проходит через каждый из списков контактов, изготовленных из Pright_incred_from_current (), price_incred_from_baseline () и duration_reached (). Каждый список контактов содержит суб -списки с PhonNembumber, название продукта, исходную цену, текущую цену и URL и проходит в отдельном цикле. Один за один, сообщения отправляются каждому пользователю в списке для каждого типа уведомления:

def send_messages(increased_from_current, increased_from_baseline, reached_durations):

    for i in increased_from_current:
        phonenumber = i[0]
        name = i[1]
        old_price = i[2]
        current_price = i[3]
        link = i[4]
        send_message(phonenumber, "Product {} has increased in price from ${} to ${}\nLink to product: {}".format(name[0], old_price, current_price, link[0]))

    for i in increased_from_baseline:
        phonenumber = i[0]
        name = i[1]
        current_price = i[2]
        link = i[3]
        send_message(phonenumber, "Product {} meets your ideal discount percentage at ${}!\nLink to product: {}".format(name[0], current_price, link[0]))

    for i in reached_durations:
        phonenumber = i[0]
        name = i[1]
        current_price = i[2]
        link = i[3]
        send_message(phonenumber, "The set duration for {} is complete! The current price for this product is ${}\nLink to product: {}".format(name[0], current_price, link[0]))

Обновление текущей цены:

Чтобы отслеживать колебания, ценам должен иметь возможность обновить текущую цену до последнего времени, когда она была проверена. Мы делаем это в самом конце программы, чтобы мы не сравнивали текущую цену с самим собой при проверке уведомлений.

   def get_current_prices(phonenumbers, prices, urls):

        for i in range len(urls):
            if(scrape_price(urls[i]) != prices[i]):
                cursor.execute('UPDATE dbo.ScrapedData SET current_price = {} WHERE phonenumber LIKE \'%{}%\' AND link LIKE \'%{}%\';'.format(scrape_price(url[i]), phonenumbers[i], urls[i]))
                cursor.commit()

Все, что требует этой функции, – это номера телефонов, цены и URL -адреса в базе данных. URL -адрес используется для сокраски текущей цены с сайта, а затем сравнивается с ценой, указанной в базе данных для продукта. В запросе ряд изменяется, если цена соскоба отличается от. Перечисленная цена. Именно здесь появляются фонарики: мы указали на триггере HTTP, что фоненовичный игрок может соответствовать данному URL только один раз. Поэтому, в запросе, мы обязательно изменили строку как с этим фенюенсом, так и ссылкой, чтобы не изменить текущую цену для всех пользователей.

Связывая все вместе:

Теперь мы будем называть все методы, которые мы только что создали в основной функции. Чтобы вызвать любой другой метод, нам сначала нужно создать список для каждого столбца в базе данных, используя get_database_information ():

def main(mytimer: func.TimerRequest) -> None:
    phonenumbers = get_Database_Information()[0]
    names = get_Database_Information()[1]
    links = get_Database_Information()[2]
    original_prices = get_Database_Information()[3]
    current_prices = get_Database_Information()[4]
    percentages = get_Database_Information()[5]
    durations = get_Database_Information()[6]

Используя эти списки, мы составим наши списки контактов, позвонив по цене agoot_incred_from_current (), pright_incread_from_baseline () и duration_reached ():

increased_from_current = price_increased_from_current(current_prices, links, phonenumbers, names)
    increased_from_baseline = price_increased_from_baseline(original_prices, links, phonenumbers, percentages, current_prices, names)
    reached_durations = duration_reached(phonenumbers, durations, names, links, current_prices)

Наконец, мы отправим уведомления и обновим базу данных:

    send_messages(increased_from_current, increased_from_baseline, reached_durations)
    get_current_prices(phonenumbers, prices, urls)

    utc_timestamp = datetime.datetime.utcnow().replace(
        tzinfo=datetime.timezone.utc).isoformat()

    if mytimer.past_due:
        logging.info('The timer is past due!')

    logging.info('DATA: %s', get_Database_Information())

Примечание. Обязательно сохраните код котла! Это то, что гарантирует, что ваша функция запускается с правильными интервалами.

Вот как будут выглядеть сообщения, отправляемые через функцию:

Шаг 6: Разработка фронта

Мы так близки к завершению цены! Все, что осталось, – это создание интерфейса для пользователей, чтобы фактически получить доступ к задней части. Мы разработаем очень простую форму, используя HTML, CSS и JavaScript. Однако, поскольку мы хотим, чтобы наш цены были общедоступными, мы создадим статический веб -приложение Azure, чтобы развернуть его в Интернете.

Настройка учетной записи GitHub:

Первый шаг к созданию статического веб -приложения – это иметь учетную запись GitHub. GitHub является хозяином для репозитории кодирования, что означает, что он используется для отслеживания различных версий проекта и для различных сотрудников и задач. Azure Static WebApps требует, чтобы вы развернули свой код в GitHub, а затем разверните его снова в экземпляре WebApp, расположенном на портале Azure. Эта ссылка IS обеспечивает отличную прогулку по тому, как настроить GitHub и Azure Static WebApp, а затем подключить оба к VSCODE.

После изучения того, как создать и развернуть веб -приложение, откройте страницу index.html в локальном веб -приложении и вставьте следующий код:



    
    
    
    
    Amazon Webscraper


    

Price Scraper

Phone number:

Product Name

Product URL:

Baseline discount percentage:

Notification time frame (days):




Это имеет весь код для фронта, но несколько примитивно выглядит. Вы можете либо уколоть этот код самостоятельно, либо обратиться к мой стиль :

И там у тебя это есть! У нас есть красивый фронт для цены.

Данные формы обработки:

GIF выше – это демонстрация готового продукта, причем данные фактически отправляются на наш триггер HTTP. Нам необходимо проверить пользовательский ввод, чтобы они размещали рабочие ссылки и правильный тип данных. Это лучше всего сделать в JavaScript, который оптимизирован для совместимости с HTML. В каталоге для ваших листов в стиле и HTML создайте файл с именем script.js. Затем скопируйте и вставьте следующий код:

function validatePhonenumber(phonenumber)
{
    var numberType = typeof phonenumber;
    var regExp = /[a-zA-Z]/g; //any letters

    if( numberType != "number")
    {
        var noHyphens = phonenumber.replaceAll('-', '');
        console.log(noHyphens + ", length: " + noHyphens.length);
        if((noHyphens).length != 10 || (regExp.test(phonenumber)))
            return null;

        return noHyphens
    }


    return phonenumber;


}

function cleanDuration(duration)
{
    console.log("dur peeps");
    const currentDate = new Date();
    var stopDate = new Date();

    stopDate.setDate(currentDate.getDay() + duration);

    return duration;
}


function cleanPercentage(percentage)
{
    if(percentage > 100)
        percentage = percentage % 100;

    return percentage;
}


function validateURL(url)
{
    var request = new XMLHttpRequest();  
    request.open('GET', url, true);
    request.onreadystatechange = function()
    {
        if (request.readyState === 4)
        {
            if (request.status === 404) 
            {  
                return null;
            }  
        }
    };

    return url;
}

Приведенный выше код проверяет и очищает пользовательский ввод для работы с нашим кодом функции. Например, функция ValidatePhonEnumber () гарантирует, что дефисы стерты с ввода, а длина проверяется как 9 – 10 цифр. Основной функцией для этого сценария является jsonifydata (). Первая половина функции получает вход формы при нажатии кнопки «Отправить»:

function jsonifyData(event)
{
    event.preventDefault();
    var phonenumber = document.getElementById('phonenumber').value;
    var product_name = document.getElementById('product_name').value;
    var url = document.getElementById('url').value;
    var percentage = document.getElementById('percentage').value;
    var duration = document.getElementById('duration').value;

    if(validateURL(url) == null)
    {
        $('#popup').html("INVALID URL");
        return;
    }  
    if(validatePhonenumber(phonenumber) == null)
    {
        document.getElementById("popup").innerHTML = "INVALID PHONENUMBER";
        //$('#popup').html("INVALID PHONENUMBER");
        return;
    }  

Если данные недопустимы, процесс завершается, и функция отправляет пользователю сообщение об ошибке. В противном случае очищенные данные отправляются в виде параметров для запроса XHTTP, который публикует параметры из формы на триггер:

var params = 'url=' + url + '&phonenumber=' + validatePhonenumber(phonenumber) + '&duration=' + duration + '&baseline_percentage=' + percentage + '&name=' + product_name.replaceAll(' ', '-');
    var http = new XMLHttpRequest();
    //TRIGGER LINKS: 'https://pricescrapertester.azurewebsites.net/api/SQLFormCompleter?'   'http://localhost:7071/api/SQLFormCompleter'
    var trigger_url = '';

    http.open('POST', trigger_url+params, true);

    http.setRequestHeader('Content-type', 'application/multipart/form-data');

    http.onreadystatechange = function() {//Call a function when the state changes.
        if(http.readyState == 4 && http.status == 200) {
            alert(http.responseText);
        }
    }
    console.log("POST: " + trigger_url+params);
    console.log(params);

    document.getElementById("popup").innerHTML = "FORM STATUS: SUCCESS";

Обязательно используйте Ваш собственный триггер URL -адреса , который можно найти в «Обзоре» для каждого приложения функции. Как только работает script.js, переведите свой статический веб -приложение.

Шаг 7: Празднование !!

Поздравляю! Вы создали веб -приложение с нуля и, что более важно, сэкономили себе много денег и разочарования, когда дело доходит до покупок на Amazon. Это нелегкий подвиг, но я надеюсь, что вам понравилось кодировать этот проект так же, как и я. Если вы хотите ссылаться на мой код, вот ссылка на мой репозиторий GitHub для проекта. Спасибо за прочтение этой статьи, и, пожалуйста, оставьте любые отзывы или вопросы в комментариях.

Специальная благодарность:

Я также хотел бы поблагодарить BitProject и Microsoft, которые вдохновили меня создать этот проект! Я благодарен за то, что узнал о облачных вычислениях и лазуре таким забавным и увлекательным образом.

Оригинал: “https://dev.to/bitproject/save-money-and-frustration-on-amazon-with-azure-functions-1chl”