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

Ежедневная доля цен Уведомления с использованием Python, SQL и Africas Talking – Первая часть

На горячем сухом днеме мы сидели, обсуждая инвестиционные проспекты во время этой пандемии, мы поняли, что они … Теги с Python, учебником, PostgreSQL.

На горячем сухом днеме мы сидели обсуждаем инвестиционные проспекты во время этой пандемии, мы поняли, что они ограничены. Однако мы продолжали возвращаться к акциям в качестве твердой среды для изучения. Это представило хорошую идею проекта, а также хорошую возможность узнать больше о разработках финансового сектора. Здесь, в Кении, главный борс – это биржа найробийской бирже (NSE), есть около 60+ компаний, перечисленных на бирже по состоянию на 2021. NSE действует с понедельника с пятницы с 9.00 до 3,00 вечера, кроме праздников. Основная цель этой статьи состоит в том, чтобы разработать веб-скребок и скрипт уведомления, чтобы уведомить нас, когда определенный тикер достигает определенной цены или альтернативно выше определенного порогового значения цен.

Раньше проработав веб-соскобные проекты, я исследовал обширный список библиотек и каркасов и других инструментов. Вы можете проверить мою запись на Новости скребка Отказ У меня был немного опыта с использованием Скапировка И казалось, что идеально подходит для этого проекта. STRAPY – это веб-структура SCRAPING, поэтому она делает предположения о том, как обрабатывать определенные аспекты, начиная от структуры папки до собственного CLI и хранения данных.

Это делает его отличным для структурирования крупных проектов или даже нескольких скребков в одном проекте. Тем не менее, он также имеет крутущую кривую обучения, но углубленную документацию и довольно большое сообщество более чем компенсирует это. Для хранения данных Обычно файл JSON должен быть адекватен, но база данных гарантирует, что она будет легко сохраняться и запросить данные позже. Мы будем использовать PostgreSQL, в основном потому, что я использовал уже в других проектах и приятно пользоваться нашим потребностям.

Предварительные условия перед началом работы

Следовать по этому посту и введите те же функции. Вам нужно понадобиться несколько вещей:

  • Python и Pip (в настоящее время я использую 3.9.2) Любая версия выше 3.5 должна работать.
  • AfricaS говорят аккаунт Отказ

    • API ключ и имя пользователя из вашей учетной записи. Создайте приложение и обратите внимание на ключ API. Как только вы получили вышеупомянутые:

      • Создайте новый каталог и измените его.

    • Создайте новую виртуальную среду для проекта или активируйте предыдущую.

    • Использование Python Package Manager (PIP), установка: BasineSoup4, Scraphy, африканский Python SDK, библиотека Python-Dotenv, Библиотеки SQLachemy и Psycopg2.

    • Сохраните установленные библиотеки в файле требований.

Как уже упоминалось выше, мы используем PostgreSQL, поскольку наша база данных выбора, следовательно, нам нужна библиотека для интерфейса с базой данных, PSYCOPG2 является хорошим вариантом, хотя есть другие. Хотя не нужно, мы будем использовать SQLalchemy как наше соотношение объектов Mapper (ORM). Это позволяет нам использовать объекты Python (классы, функции), чтобы сделать транзакции вместо RAW SQL.

  • Установите базу данных PostgreSQL, чтобы сохранить все наши данные SCRAPED. В зависимости от того, на каком платформе вы введите код, вы можете сделать это в результате вашей системы. Лично я использую Докер Как легко управлять контейнерами и предотвращает загромождение моей системы. Это Статья Это потрясающий ресурс о том, как получить PostgreSQL и PGADMIN4, установленный в качестве контейнеров.

В качестве альтернативы, проверьте готовый код на Гадость

Пауки везде 🕷️🕸️.

Скапли работает на концепции пауков, мы определяем наши собственные пользовательские пауки для ползания и соскрести данных. SCRAPY имеет свои команды, которые делают создание проекта и Spider (S) быстро и легко. Теперь мы создадим Scraphy Project, генерируйте паук с необходимым кодом для котеля с помощью CLI.

scrapy startproject nse_scraper 

Запуск команды StartProject создаст папку со структурой, изложенной ниже. Существует верхняя папка с именем проекта (NSE_SCRAPE), который содержит конфигурацию SCRAPY и подпапку с тем же именем, содержащим фактический кодрез.

python-projects $ tree nse_scraper
nse_scraper
├── nse_scraper
│   ├── __init__.py
│   ├── items.py
│   ├── middlewares.py
│   ├── pipelines.py
│   ├── settings.py
│   └── spiders
│       └── __init__.py
└── scrapy.cfg

2 directories, 7 files

NB: Я не хочу идти в слишком много подробностей о Scrapy, потому что есть много учебных пособий для инструмента в Интернете, и, поскольку я обычно использую запросы с LXML для создания (очень простых) погреблений данных. Многие люди предпочитают использовать BeautifulSoup или другие библиотеки сканирования данных более высокого уровня, поэтому не стесняйтесь идти на это. Я выбрал SCRAPY в этом конкретном случае, потому что он создает красивый эшафот при работе с потерей и базами данных, но это может быть полностью выполнено с нуля.

cd nse_scraper 
scrapy genspider afx_scraper https://afx.kwayisi.org/nseke/
Created spider 'afx_scraper' using template 'basic' in module:
nse_scraper.spiders.afx_scraper

Вы можете выбрать не использовать генератор и писать самих файлов SCHAPY, но для простоты я использую бойную табличку, которая поставляется с SCRAPY. Теперь перейдите к папке проекта верхнего уровня и создайте паук ( AFX_SCREPER ) Использование Genspider Отказ В моем случае я буду ползать данные из [AFX] (AFX.kwayisi.org} о ценах NSE Chare. Есть главный NSE сайт или даже Сайт mystocks Однако оба требуют подписки на то, чтобы получить цитаты акций в реальном времени. Поскольку этот проект предназначен для скребка DIY с минимальными затратами, AFX был самым жизнеспособным вариантом. Как бонус, они структурируют свои данные в таблице и регулярно обновляют цены. Как видно ниже:

Если мы посмотрим на структуру файла снова внутри пауки Папка, новый файл afx_scraper.py был создан.

python-projects/nse_scraper $ tree
.
├── nse_scraper 
│   ├── __init__.py
│   ├── items.py
│   ├── middlewares.py
│   ├── pipelines.py
│   ├── settings.py
│   └── spiders
│       ├── __init__.py
│       └── afx_scraper.py 
└── scrapy.cfg

Содержание afx_scraper.py Требуется ли минимальный код, необходимый для работы с ползуными данными.

#afx_scraper.py 
import scrapy
class AfxScraperSpider(scrapy.Spider):
    name = 'afx_scraper'
    allowed_domains = ['https://afx.kwayisi.org']
    start_urls = ['https://afx.kwayisi.org/nseke/']

    def parse(self, response):
        pass

Настройка скребка

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

Скапельчик позволяет двумя способами выбрать элементы в HTML-документе:

  1. Использование селекторов CSS
  2. Используя xpath.

Мы начнем с использованием селектора CSS как его простым. Мы назначаем ряд Переменная к коду, ссылающуюся на ряд данных. Благодаря характеру того, как отображаются отдельные данные (аналогичные теги HTML), нам нужно использовать XPath для извлечения данных.

#afx_scraper.py
 def parse(self, response):
        print("Processing: " + response.url)
        # Extract data using css selectors
        row = response.css('table tbody tr ')
        # use XPath and regular expressions to extract stock name and price
        raw_ticker_symbol = row.xpath('td[1]').re('[A-Z].*')
        raw_stock_name = row.xpath('td[2]').re('[A-Z].*')
        raw_stock_price = row.xpath('td[4]').re('[0-9].*')

        # create a function to remove html tags from the returned list
        print(raw_ticker_symbol)

Для каждого ряда выше мы используем XPath для извлечения необходимых элементов. Результатом является комбинированный список данных, в том числе данные из верхней таблицы, включая лучшие Gainers и неудачников. Inforder, чтобы отфильтровать то, что нам не нужно, мы используем Регулярные выражения Отказ В случае Raw_ticker_symbol и Raw_Stock_price нам нужны только алфавитные буквы, поэтому мы проходим вместе [A-Z]. * правила для нашего регенератора. Что касается наших ценовых данных, нам нужны целые числа, которые мы проходим [0-9]. * Как наше правило Regex.

Жуткие скалы 🐜.

Теперь скребок готов к выполнению и извлечению элементов. Запустите гусенику и убедитесь, что он действительно возвращает предметы, которые вы ожидаете. Нет вывода, которые еще не хранят предметы, но журнал говорит мне, что было 66 элементов, которые на самом деле имели символ, имя и определенную цену («item_scraped_count»: 66,). Обратите внимание, что я устанавливаю Loglevel для информации, чтобы предотвратить перегрузку информации в консоли.

2021-04-30 23:02:09 [scrapy.utils.log] INFO: Scrapy 2.4.1 started (bot: nse_scraper)
2021-04-30 23:02:09 [scrapy.utils.log] INFO: Versions: lxml 4.6.3.0, libxml2 2.9.10, cssselect 1.1.0, parsel 1.6.0, w3lib 1.22.0, Twisted 21.2.0, Python 3.9.4 (default, Apr 20 2021, 15:51:38) - [GCC 10.2.0], pyOpenSSL 20.0.1 (OpenSSL 1.1.1k  25 Mar 2021), cryptography 3.4.7, Platform Linux-5.11.14-147-tkg-bmq-x86_64-with-glibc2.33
2021-04-30 23:02:09 [scrapy.crawler] INFO: Overridden settings:
{'BOT_NAME': 'nse_scraper',
 'EDITOR': '/usr/bin/micro',
 'LOG_LEVEL': 'INFO',
 'NEWSPIDER_MODULE': 'nse_scraper.spiders',
 'SPIDER_MODULES': ['nse_scraper.spiders']}
2021-04-30 23:02:09 [scrapy.extensions.telnet] INFO: Telnet Password: 616b228b56a699b0
2021-04-30 23:02:09 [scrapy.middleware] INFO: Enabled extensions:
['scrapy.extensions.corestats.CoreStats',
 'scrapy.extensions.telnet.TelnetConsole',
 'scrapy.extensions.memusage.MemoryUsage',
 'scrapy.extensions.feedexport.FeedExporter',
 'scrapy.extensions.logstats.LogStats']
2021-04-30 23:02:09 [scrapy.middleware] INFO: Enabled downloader middlewares:
['scrapy.downloadermiddlewares.httpauth.HttpAuthMiddleware',
 'scrapy.downloadermiddlewares.downloadtimeout.DownloadTimeoutMiddleware',
 'scrapy.downloadermiddlewares.defaultheaders.DefaultHeadersMiddleware',
 'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware',
 'scrapy.downloadermiddlewares.retry.RetryMiddleware',
 'scrapy.downloadermiddlewares.redirect.MetaRefreshMiddleware',
 'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware',
 'scrapy.downloadermiddlewares.redirect.RedirectMiddleware',
 'scrapy.downloadermiddlewares.cookies.CookiesMiddleware',
 'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware',
 'scrapy.downloadermiddlewares.stats.DownloaderStats']
2021-04-30 23:02:09 [scrapy.middleware] INFO: Enabled spider middlewares:
['scrapy.spidermiddlewares.httperror.HttpErrorMiddleware',
 'scrapy.spidermiddlewares.offsite.OffsiteMiddleware',
 'scrapy.spidermiddlewares.referer.RefererMiddleware',
 'scrapy.spidermiddlewares.urllength.UrlLengthMiddleware',
 'scrapy.spidermiddlewares.depth.DepthMiddleware']
2021-04-30 23:02:09 [scrapy.middleware] INFO: Enabled item pipelines:
['nse_scraper.pipelines.NseScraperPipeline']
2021-04-30 23:02:09 [scrapy.core.engine] INFO: Spider opened
2021-04-30 23:02:09 [scrapy.extensions.logstats] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min)
2021-04-30 23:02:09 [py.warnings] WARNING: /home/zoo/.pyenv/versions/stock-price-scraper/lib/python3.9/site-packages/scrapy/spidermiddlewares/offsite.py:65: URLWarning: allowed_domains accepts only domains, not URLs. Ignoring URL entry https://afx.kwayisi.org in allowed_domains.
  warnings.warn(message, URLWarning)

2021-04-30 23:02:09 [scrapy.extensions.telnet] INFO: Telnet console listening on 127.0.0.1:6023
Processing: https://afx.kwayisi.org/nseke/
2021-04-30 23:02:17 [scrapy.core.engine] INFO: Closing spider (finished)
2021-04-30 23:02:17 [scrapy.extensions.feedexport] INFO: Stored json feed (66 items) in: test.json
2021-04-30 23:02:17 [scrapy.statscollectors] INFO: Dumping Scrapy stats:
{'downloader/request_bytes': 443,
 'downloader/request_count': 2,
 'downloader/request_method_count/GET': 2,
 'downloader/response_bytes': 9754,
 'downloader/response_count': 2,
 'downloader/response_status_count/200': 1,
 'downloader/response_status_count/301': 1,
 'elapsed_time_seconds': 7.457298,
 'finish_reason': 'finished',
 'finish_time': datetime.datetime(2021, 4, 30, 20, 2, 17, 77130),
 'item_scraped_count': 66,
 'log_count/INFO': 11,
 'log_count/WARNING': 1,
 'memusage/max': 75550720,
 'memusage/startup': 75550720,
 'response_received_count': 1,
 'scheduler/dequeued': 2,
 'scheduler/dequeued/memory': 2,
 'scheduler/enqueued': 2,
 'scheduler/enqueued/memory': 2,
 'start_time': datetime.datetime(2021, 4, 30, 20, 2, 9, 619832)}
2021-04-30 23:02:17 [scrapy.core.engine] INFO: Spider closed (finished)

Давайте очистите данные!

Данные, которые мы получаем, не используемая в своем текущем формате, так как он содержит теги HTML, классы, атрибуты и т. Д. Таким образом, нам нужно его очистить.

#afx_scraper.py
#import BeautifulSoup at the top of the file
from bs4 import BeautifulSoup

# create a function to remove html tags from the returned list 
def clean_stock_name(raw_name):
            clean_name = BeautifulSoup(raw_name, "lxml").text
            clean_name = clean_name.split('>')
            return clean_name[1]

def clean_stock_price(raw_price):
    clean_price = BeautifulSoup(raw_price, "lxml").text
    return clean_price

 # Use list comprehension to unpack required values 
stock_name = [clean_stock_name(r_name) for r_name in raw_stock_name]
stock_price = [clean_stock_price(r_price) for r_price in raw_stock_price]
stock_symbol = [clean_stock_name(r_symbol) for r_symbol in raw_ticker_symbol]
# using list slicing to remove the unnecessary data
stock_symbol = stock_symbol[6:]
cleaned_data = zip(stock_symbol, stock_name, stock_price)
for item in cleaned_data:
    scraped_data = {
        'ticker': item[0],
        'name': item[1],
        'price': item[2],
    }
    # yield info to scrapy
    yield scraped_data

Сначала импортируем библиотеку BeautifulSoup из пакета BS4. Это даст нам более легкое время очистки данных. Первая функция Clean_Stock_name () принимает значение raw_name. Затем мы называем конструктор Beautifulsoup, пройдете нашу ценность как Аргумент, то уточняем lxml как наш парсер. Для получения дополнительной информации о том, насколько красивый суп работает и разные парсеры, Проверьте документацию Отказ Затем мы указываем, что мы хотим только текст и назначить его нашему Clean_name переменная. Убирая имя, у нас все еще имели дополнительные символы, которые нам не нужно, поэтому мы называем .расколоть () Метод и вернуть необходимую строку.

Вторая функция Clean_Stock_name () В значительной степени повторяет процесс, указанный выше с единственной настройкой, это нам не нужен дополнительный этап добавления метода разделения струн.

Затем мы называем функции на каждом значении Raw_ticker_symbol С RAW_NAME и Raw_Stock_price . Мы приступаем к назначению результата для соответствующим образом именованных переменных: Stock_symbol , Stock_Price и Stock_Name Отказ Символ сток возвращает дополнительные символы, чем нам нужно, поэтому мы выполняем список, чтобы получить правильную длину символов и назначить его переменной. Мы используем функцию ZIP для создания списка всех полученных данных. Наконец мы создаем словарь SCRAPED_DATA и назначить соответствующие ключевые ключи к значению очищенных данных. Используя доходность Ключевое слово Наш анализ функции теперь генератор может возвращать значения при необходимости. Это особенно имеет решающее значение для производительности при полсе нескольких страниц.

Давайте храним все данные!

Прежде всего я определяю схему элемента, что я ползю в items.py. Там нет модных схемы пока нет Но это, очевидно, может быть улучшено в будущем, когда будут получены более предметы, и фактические значения данных имеют значение.

# items.py
# See documentation in:
# https://docs.scrapy.org/en/latest/topics/items.html


from scrapy.item import Item, Field


class NseScraperItem(Item):
    # define the fields for your item here like:
    stock_name = Field()
    stock_price = Field()
    stock_symbol = Field()

Hiddrwares.py остается нетронутым для проекта. Важный бит для хранения данных в базе данных находится внутри моделей .py. Как описано, прежде чем я использую SQLalchemy для подключения к базе данных PostgreSQL. Детали базы данных хранятся в settings.py (См. Ниже) и используются для создания двигателя SQLALCHEMY. Я определяю модель элементов с тремя полями и использую create_items_table для создания таблицы.

# nse_scraper/nse_scraper/models.py
from sqlalchemy import Column, Float, Integer, String, create_engine
from sqlalchemy.engine.base import Engine
from scrapy.utils.project import get_project_settings
from sqlalchemy.ext.declarative import declarative_base


Base = declarative_base()


def db_connect() -> Engine:
    """
    Creates database connection using database settings from settings.py.
    Returns sqlalchemy engine instance
    """
    return create_engine(get_project_settings().get("DATABASE"))


def create_items_table(engine: Engine):
    """
    Create the Items table
    """
    Base.metadata.create_all(engine)


class StockData(Base):
    """
    Defines the items model
    """

    __tablename__ = "stock_data"

    id = Column("id", Integer, primary_key=True, autoincrement=True)
    stock_ticker = Column("stock_ticker", String)
    stock_name = Column("stock_name", String)
    stock_price = Column("stock_price", Float)

Внутри трубопроводы .py Паук подключен к базе данных. Когда трубопровод запущен, он будет инициализировать базу данных и создать двигатель, создать таблицу и настроить сеанс SQLALCHMEY. process_item Функция является частью кода по умолчанию и выполняется для каждого приведенного элемента в скребке. В этом случае это означает, что он будет срабатывать каждый раз, когда запас извлекается тикером, именем и ценой. Не забудьте всегда совершать () при добавлении (или удалении) предметов в таблицу.

# nse_scraper/nse_scraper/pipelines.py
# Define your item pipelines here

# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html


# useful for handling different item types with a single interface
from sqlalchemy.orm import sessionmaker

from nse_scraper.models import StockData, create_items_table, db_connect


class NseScraperPipeline:
    def __init__(self):
        """
        Initializes database connection and sessionmaker.
        Creates stock_data table
        """
        engine = db_connect()
        create_items_table(engine)
        self.Session = sessionmaker(bind=engine)

    def process_item(self, item, spider):
        """
        process item and store to database
        """
        session = self.Session()
        stock_data = StockData()
        stock_data.stock_name = item["name"]
        stock_data.stock_price = float(item["price"].replace(',', ''))
        stock_data.stock_ticker = item["ticker"]
        try:
            session.add(stock_data)
            session.commit()
            # query again
            obj = session.query(StockData).first()
            # print(obj.stock_ticker)
        except Exception as e:
            session.rollback()
            print(f"we have a problem, houston {e}")
            raise
        finally:
            session.close()
        return item

Наконец, settings.py недолго и содержит информацию для гусеничного. Единственными элементами, которые я добавил, это база данных и переменные log_level. Вы могли бы выбрать добавлять ваши данные безопасности в этом файле, но я бы порекомендовал держать их в секрете и хранить их в другом месте. Я использовал .env Файл для хранения моих учетных данных, затем использовал Python-Dotenv Библиотека для их получения. Примечание: .env.env. должно быть в той же папке, что и settings.py Файл или укажите путь к файлу в скобках.

# nse_scraper/nse_scraper/settings.py 
# Scrapy settings for nse_scraper project

# For simplicity, this file contains only settings considered important or
# commonly used. You can find more settings consulting the documentation:
#
#     https://docs.scrapy.org/en/latest/topics/settings.html
#     https://docs.scrapy.org/en/latest/topics/downloader-middleware.html
#     https://docs.scrapy.org/en/latest/topics/spider-middleware.html

import os
from dotenv import load_dotenv

load_dotenv()
BOT_NAME = 'nse_scraper'

SPIDER_MODULES = ['nse_scraper.spiders']
NEWSPIDER_MODULE = 'nse_scraper.spiders'

# POSTGRES SETTINGS
host = os.getenv("POSTGRES_HOST")
port = os.getenv("POSTGRES_PORT")
username = os.getenv("POSTGRES_USER")
password = os.getenv("POSTGRES_PASS")
database = os.getenv("POSTGRES_DB")
drivername = "postgresql"
DATABASE = f"{drivername}://{username}:{password}@{host}:{port}/{database}"

# Configure item pipelines
# See https://docs.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
    'nse_scraper.pipelines.NseScraperPipeline': 300,
}
LOG_LEVEL = "INFO"
# Crawl responsibly by identifying yourself (and your website) on the user-agent
# USER_AGENT = 'nse_scraper (+http://www.yourdomain.com)'

# Obey robots.txt rules
ROBOTSTXT_OBEY = False

Ваш скребок теперь готов к его запуску:

scrapy crawl afx_scraper 

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

scrapy crawl afx_scraper -o stock.json

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

Если у вас есть какие-либо вопросы или комментарии. Дайте мне знать в комментариях, или на Twitter Отказ

Оригинал: “https://dev.to/ken_mwaura1/daily-share-price-notifications-using-python-sql-and-africas-talking-part-one-17p”