Raspador da web da bolsa de valores de Nairóbi
Kennedy Mwaura19 min read • Published May 09, 2023 • Updated Apr 02, 2024
APLICATIVO COMPLETO
Avalie esse Tutorial
Procurando construir um web scraper usando Python e MongoDB para a Bolsa de Valores de Nairobi? Nosso tutorial abrangente fornece um guia passo a passo sobre como configurar o ambiente de desenvolvimento, criar um spider Scrapy, analisar o site e armazenar os dados no MongoDB.
Também abordamos práticas recomendadas para trabalhar com MongoDB e dicas para solucionar problemas comuns. Além disso, dê uma olhada no uso do MongoDB Atlas para Atlas Charts. Por fim, ative as notificações de texto usando a API Africas Talking (sinta-se à vontade para mudar para o seu provedor preferido). Obtenha todo o código no Github e simplifique seu fluxo de trabalho hoje mesmo!
Foi verificado que os pré-requisitos abaixo funcionam no Linux. A implementação em outros sistemas operacionais pode ser diferente. Por gentileza, verifique as instruções de instalação.
- O que é web scraping?
- Layout do projeto
- Configuração do projeto
- Iniciando um projeto Scrapy
- Criando uma aranha
- Executando o scraper
- Ativando alertas de texto
- Dados no MongoDB Atlas
- Gráficos no MongoDB Atlas
- CI/CD com ações do GitHub
- Conclusão
Web scraping é o processo de extração de dados de sites. É uma forma de mineração de dados, que automatiza a recuperação de dados da web. A raspagem da Web é uma técnica para acessar e extrair automaticamente grandes quantidades de informações de um site ou plataforma, o que pode economizar muito tempo e esforço. Você pode salvar esses dados localmente em seu computador ou em um banco de dados na nuvem.
O Scrapy é um framework de rastreamento da web gratuito e de código aberto escrito em Python. Ele extrai os dados de que você precisa de sites de forma rápida, simples, mas extensível. Ele pode ser usado para uma ampla variedade de fins, desde coleta de dados até monitoramento e testes automatizados.
MongoDB Atlas é uma plataforma de banco de dados em nuvem totalmente gerenciada que hospeda seus dados na AWS, GCPou Azure. É um banco de dados totalmente gerenciado como serviço (DBaaS) que fornece uma infraestrutura de banco de dados altamente disponível, distribuída globalmente e escalável. Leia nosso tutorial para começar a usar uma instância gratuita do MongoDB Atlas.
Você também pode acessarDocs para saber mais sobre como limitar o acesso ao seu cluster a IP especificados. Esta etapa aumenta a segurança seguindo as melhores práticas.
Abaixo está um diagrama que fornece uma visão geral de alto nível do projeto.
O diagrama acima mostra como o projeto é executado, bem como a estrutura geral. Vamos detalhá-lo:
- Como o Scrapy é uma estrutura completa, nós o usamos para extrair e limpar os dados.
- Os dados são enviados para o MongoDB Atlas para armazenamento.
- A partir daqui, podemos conectá-lo facilmente ao MongoDB Charts para visualizações.
- Empacotamos nosso raspador da web usando o Docker para facilitar a implantação na nuvem.
- O código é hospedado no GitHub e criamos um pipeline CI/CD usando ações do GitHub.
- Por fim, temos um script de notificação de texto que é executado quando as condições definidas são atendidas.
Vamos configurar nosso projeto. Primeiro, criaremos um novo diretório para nosso projeto. Abra seu terminal e navegue até o diretório onde deseja criar o projeto. Em seguida, execute o seguinte comando para criar um novo diretório e alterá-lo.
1 mkdir nse-stock-scraper && cd nse-stock-scraper
Em seguida, criaremos um ambiente virtual para nosso projeto. Isso nos ajudará a isolar as dependências do nosso projeto do resto do nosso sistema. Execute o comando a seguir para criar um ambiente virtual. Estamos usando o módulo Ppython
venv
incorporado para criar o ambiente virtual. Ative o ambiente virtual executando o script activate
no diretóriobin
.1 python3 -m venv venv 2 source venv/bin/activate
Agora, instalaremos as dependências necessárias. Usaremos o
pip
para instalar as dependências. Execute o seguinte comando para instalar as dependências necessárias:1 pip install scrapy pymongo[srv] dnspython python-dotenv beautifulsoup4 2 pip freeze > requirements.txt
Scrapy é um framework completo. Assim, tem uma visão opinativa sobre a estrutura de seus projetos. Ele vem com uma ferramenta CLI para começar rapidamente. Agora, iniciaremos um novo projeto Scrapy. Execute o seguinte comando.
1 scrapy startproject nse_scraper .
Isso criará um novo diretório com o nome
nse_scraper
e alguns arquivos. O diretórionse_scraper
é o pacote Python real para nosso projeto. Os arquivos são os seguintes:items.py
- Esse arquivo contém a definição dos itens que serão extraídos.middlewares.py
— Este arquivo contém a definição dos middlewares que usaremos.pipelines.py
— Contém a definição dos pipelines que usaremos.settings.py
— Contém a definição das configurações que usaremos.spiders
— Este diretório contém os spiders que usaremos.scrapy.cfg
— Este arquivo contém a configuração do projeto.
Uma aranha é uma classe que define como um determinado site será raspado. Ela deve subclassificar
scrapy.Spider
e definir as solicitações iniciais a serem feitas — e, opcionalmente, como seguir os links nas páginas e analisar o conteúdo da página baixada para extrair dados.Vamos criar uma aranha para raspar o siteafx. Execute o comando a seguir para criar um spider. Mude para a pasta
nse_scraper
que está dentro da nossa pasta raiz.1 cd nse_scraper 2 scrapy genspider afx_scraper afx.kwayisi.org
Isso criará um novo arquivo
afx_scraper.py
no diretóriospiders
. Abra o arquivo e substitua o conteúdo pelo seguinte código:1 from scrapy.settings.default_settings import CLOSESPIDER_PAGECOUNT, DEPTH_LIMIT 2 from scrapy.spiders import CrawlSpider, Rule 3 from bs4 import BeautifulSoup 4 from scrapy.linkextractors import LinkExtractor 5 6 7 class AfxScraperSpider(CrawlSpider): 8 name = 'afx_scraper' 9 allowed_domains = ['afx.kwayisi.org'] 10 start_urls = ['https://afx.kwayisi.org/nse/'] 11 user_agent = 'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.157 Safari/537.36' 12 custom_settings = { 13 DEPTH_LIMIT: 1, 14 CLOSESPIDER_PAGECOUNT: 1 15 } 16 17 rules = ( 18 Rule(LinkExtractor(deny='.html', ), callback='parse_item', follow=False), 19 Rule(callback='parse_item'), 20 ) 21 22 def parse_item(self, response, **kwargs): 23 print("Processing: " + response.url) 24 # Extract data using css selectors 25 row = response.css('table tbody tr ') 26 # use XPath and regular expressions to extract stock name and price 27 raw_ticker_symbol = row.xpath('td[1]').re('[A-Z].*') 28 raw_stock_name = row.xpath('td[2]').re('[A-Z].*') 29 raw_stock_price = row.xpath('td[4]').re('[0-9].*') 30 raw_stock_change = row.xpath('td[5]').re('[0-9].*') 31 32 # create a function to remove html tags from the returned list 33 def clean_stock_symbol(raw_symbol): 34 clean_symbol = BeautifulSoup(raw_symbol, "lxml").text 35 clean_symbol = clean_symbol.split('>') 36 if len(clean_symbol) > 1: 37 return clean_symbol[1] 38 else: 39 return None 40 41 def clean_stock_name(raw_name): 42 clean_name = BeautifulSoup(raw_name, "lxml").text 43 clean_name = clean_name.split('>') 44 if len(clean_name[0]) > 2: 45 return clean_name[0] 46 else: 47 return None 48 49 def clean_stock_price(raw_price): 50 clean_price = BeautifulSoup(raw_price, "lxml").text 51 return clean_price 52 53 # Use list comprehension to unpack required values 54 stock_name = [clean_stock_name(r_name) for r_name in raw_stock_name] 55 stock_price = [clean_stock_price(r_price) for r_price in raw_stock_price] 56 ticker_symbol = [clean_stock_symbol(r_symbol) for r_symbol in raw_ticker_symbol] 57 stock_change = [clean_stock_price(raw_change) for raw_change in raw_stock_change] 58 if ticker_symbol is not None: 59 cleaned_data = zip(ticker_symbol, stock_name, stock_price) 60 for item in cleaned_data: 61 scraped_data= { 62 'ticker_symbol': item[0], 63 'stock_name': item[1], 64 'stock_price': item[2], 65 'stock_change': stock_change } 66 # yield info to scrapy 67 yield scraped_data
Vamos decompor o código acima. Primeiro, importamos os módulos e classes necessários. Em nosso caso, usaremos _CrawlSpider _and Rule de scrapy.spiders e LinkExtractor de scrapy.linkextractors. Também usaremos o prettySoup da bs4 para limpar os dados raspados.
A classe
AfxScraperSpider
herda da CrawlSpider, que é uma subclasse da Spider. A classe Spider é o núcleo do Scrapy. Ela define como um determinado site (ou um grupo de sites) será extraído. Ela contém uma lista inicial de URLs para download e regras para seguir links nas páginas e extrair dados delas. Neste caso, usaremos o CrawlSpider para rastrear o site e seguir os links para a próxima página.O atributo name define o nome do spider. Esse nome deve ser exclusivo dentro de um projeto — ou seja, você não pode definir o mesmo nome para spiders diferentes. Ele será usado para identificar o spider quando você o executar na linha de comando.
O atributo allowed_domains é uma lista de domínios que esse spider tem permissão para rastrear. Se não for especificado, nenhuma restrição de domínio será aplicada. Isso é útil se você quiser restringir o rastreamento a um domínio específico (ou subdomínio) enquanto raspa vários domínios no mesmo projeto. Você também pode usá-lo para evitar o rastreamento do mesmo domínio várias vezes ao usar vários spiders.
O atributo start_urls é uma lista de URL de onde o spider começará a rastrear. Quando nenhum start_URLs for definido, as URL iniciais serão lidas do arquivo sitemap.xml (se existir) do primeiro domínio na lista allow_domains. Se você não quiser começar a partir de um mapa do site, poderá definir uma URL inicial neste atributo. Este atributo é opcional e pode ser omitido.
O atributo user_agent é usado para definir o agente do usuário para o spider. Isso é útil quando você deseja fazer o scraping de um site que bloqueia spiders que não têm um agente de usuário. Neste caso, usaremos um agente de usuário para o Chrome. Também podemos definir o agente de usuário no arquivo settings.py. Isso é fundamental para dar ao site de destino a ilusão de que somos um navegador real.
O atributo custom_settings é usado para definir configurações personalizadas para o indexador. Nesse caso,definiremos DEPTH_LIMIT como 1 e Closespider_PAGECOUNT como 1. O atributo DEPTH_LIMIT limita a profundidade máxima que será permitido rastrear para qualquer site. Profundidade refere-se ao número de página(s) que a indexação pode rastrear. O atributo CLOSESPIDER_PAGECOUNT é usado para fechar a aranha após rastrear o número especificado de páginas.
O atributo rules define as regras para a aranha. Usaremos a classe Rule para definir as regras para extrair links de uma página e processá-los com um retorno de chamada ou segui-los e copiá-los usando outra aranha.
A classe Rule usa um objeto LinkExtractor como primeiro argumento. A classe LinkExtractor é usada para extrair links de páginas da web. Ele pode extrair links que correspondam a expressões regulares específicas ou que usem atributos específicos, como href ou src.
O argumento deny é usado para negar a extração de links que correspondam à expressão regular especificada. O argumento callback specifies é usado para especificar a função de callback a ser chamada na resposta dos links extraídos.
O argumento follow especifica se os links extraídos devem ser seguidos ou não. Usaremos o argumento callback para especificar a função de callback a ser chamada na resposta dos links extraídos. Também usaremos o argumentofollow para especificar se os links extraídos devem ser seguidos ou não.
Em seguida, definimos uma função
parse_item
que recebe a resposta como um argumento. A funçãoparse_item
é usada para analisar a resposta e extrair os dados necessários. Usaremos o métodoxpath
para extrair os dados necessários. O métodoxpath
extrai dados utilizando expressões xPath.Obtemos expressões xpath inspecionando o site de destino. Básicamente, clicamos com o botão direito do mouse no elemento do qual queremos extrair dados e clicamos em
inspect
. Isso abrirá as ferramentas para desenvolvedores. Em seguida, clicamos no botão copy
e selecionamos copy xpath
. Cole a expressão xpath no métodoxpath
.O método
re
extrai dados utilizando expressões regulares. Em seguida, usamos as funçõesclean_stock_symbol
, clean_stock_name
e clean_stock_price
para limpar os dados extraídos. Utilize a funçãozip
para combinar os dados extraídos em uma única lista. Em seguida, use um loopfor
para iterar a lista e fornecer os dados ao Scrapy.As funções clean_stock_symbol, clean_stock_name e clean_stock_price são usadas para limpar os dados extraídos. A função clean_stock_symbol usa o símbolo bruto como argumento. A classeBeautifulSoup limpa o símbolo bruto. Em seguida, ela usa o método split para dividir o símbolo limpo em uma lista. Uma instrução if verifica se o comprimento da lista é maior que 1. Se for, ele retorna o segundo item da lista. Se não for, ele retorna None.
A função clean_stock_name usa o nome bruto como argumento. Ele usa a classe prettySoup para limpar o nome bruto. Em seguida, ele usa o método split para dividir o nome limpo em uma lista. Novamente, uma instrução if verificará se o tamanho da lista é maior que 1. Se for, ela retornará o primeiro item da lista. Se não for, retornará Nenhum. A função limpeza_estoque_price usa o preço bruto como argumento. Em seguida, ele usa a classe BeautifulSoup para limpar o preço bruto e retornar o preço limpo.
A funçãoclean_stock_change usa a alteração bruta como argumento. Ela usa a classe BeautifulSoup para limpar a alteração bruta e retornar os dados limpos.
Dentro da raiz do nosso projeto, temos o arquivo
items.py
. Um item é um contêiner que será carregado com os dados extraídos. Funciona de forma semelhante a um dicionário com recursos adicionais, como declarar seus campos e personalizar sua exportação. Usaremos a classe Item para criar nossos itens. A classe Item é a classe base para todos os itens. Ele fornece os mecanismos gerais para lidar com dados de páginas raspadas. É uma classe abstrata e não pode ser instanciada diretamente. Usaremos a classe Field para criar nossos campos.Adicione o seguinte código ao arquivo nse_scraper/items.py :
1 from scrapy.item import Item, Field 2 3 4 class NseScraperItem(Item): 5 # define the fields for your item here like: 6 ticker_symbol = Field() 7 stock_name = Field() 8 stock_price = Field() 9 stock_change = Field()
A classe NseScraperItem cria nosso item. Os campos ticker_symbol, stock_name, stock_price e stock_change armazenam o símbolo do ticker, o nome da ação, o preço da ação e a alteração da ação, respectivamente. Leia mais sobre os itens aqui.
Dentro da raiz do nosso projeto, temos o arquivo
pipelines.py
. Um pipeline é um componente que processa os itens extraídos dos spiders. Ele pode limpar, validar e armazenar os dados extraídos em um banco de dados. Usaremos a classe Pipeline para criar nossos pipelines. A classe Pipeline é a classe base para todos os pipelines. Ela fornece os métodos e as propriedades gerais que o pipeline usará.Adicione o seguinte código ao arquivo
pipelines.py
:1 # pipelines.py 2 # useful for handling different item types with a single interface 3 import pymongo 4 from scrapy.exceptions import DropItem 5 6 from .items import NseScraperItem 7 8 9 class NseScraperPipeline: 10 collection = "stock_data" 11 12 def __init__(self, mongodb_uri, mongo_db): 13 self.db = None 14 self.client = None 15 self.mongodb_uri = mongodb_uri 16 self.mongo_db = mongo_db 17 if not self.mongodb_uri: 18 raise ValueError("MongoDB URI not set") 19 if not self.mongo_db: 20 raise ValueError("Mongo DB not set") 21 22 @classmethod 23 def from_crawler(cls, crawler): 24 return cls( 25 mongodb_uri=crawler.settings.get("MONGODB_URI"), 26 mongo_db=crawler.settings.get('MONGO_DATABASE', 'nse_data') 27 ) 28 29 def open_spider(self, spider): 30 self.client = pymongo.MongoClient(self.mongodb_uri) 31 self.db = self.client[self.mongo_db] 32 33 def close_spider(self, spider): 34 self.client.close() 35 36 def clean_stock_data(self,item): 37 if item['ticker_symbol'] is None: 38 raise DropItem('Missing ticker symbol in %s' % item) 39 elif item['stock_name'] == 'None': 40 raise DropItem('Missing stock name in %s' % item) 41 elif item['stock_price'] == 'None': 42 raise DropItem('Missing stock price in %s' % item) 43 else: 44 return item 45 46 def process_item(self, item, spider): 47 """ 48 process item and store to database 49 """ 50 51 clean_stock_data = self.clean_stock_data(item) 52 data = dict(NseScraperItem(clean_stock_data)) 53 print(data) 54 # print(self.db[self.collection].insert_one(data).inserted_id) 55 self.db[self.collection].insert_one(data) 56 57 return item
Primeiro, importamos o módulopymongo. Em seguida, importamos a classe DropItem do móduloscrapy.Exceptions. Em seguida, importe a classeNseScraperItem do módulo de itens.
A classeNseScraperPipeline cria nosso pipeline. A variável dacollection armazena o nome da collection que usaremos. O métodoinit inicializa o pipeline. Ele aceita mongodb_uri e mongo_db como argumentos. Em seguida, ele usa uma declaração if para verificar se mongodb_uri está definido. Caso contrário, gera um ValueError. Em seguida, ele usa uma declaração if para verificar se mongo_db está definido. Caso contrário, gera um ValueError.
O método from_crawler cria uma instância do pipeline. Ele usa o rastreador como argumento. Em seguida, ele retorna uma instância do pipeline. O método open_spider abre o spider. Ele usa a spider como argumento. Em seguida, ele cria uma instância MongoClient e a armazena na variável do cliente. Ele utiliza a instância do cliente para se conectar ao banco de dados e o armazena na variável db.
O método close_spider fecha a aranha. Toma a aranha como argumento. Em seguida, ele fecha a instância do cliente. O método clean_stock_data limpa os dados extraídos. Ele usa o item como argumento. Em seguida, ele usa uma instrução if para verificar se o ticker_symbol é None. Se for, ele gera um DropItem. Em seguida, ele usa uma instrução if para verificar se o stock_name é None. Se for, ele gera um DropItem. Em seguida, ele usa uma instrução if para verificar se o stock_price é None. Se for, ele gera um DropItem. Se nenhuma das instruções if for verdadeira, ele retornará o item.
O métodoprocess_item processa os dados extraídos. Ele usa o item e a aranha como argumentos. Em seguida, ele usa o métodoclean_stock_data para limpar os dados copiados. Ele usa a função dict para converter o item em um dicionário. Em seguida, ele imprime os dados no console. Em seguida, ele usa a instância de banco de dados para inserir os dados no banco de dados. Ele devolve o item.
Dentro da raiz do nosso projeto, temos o arquivo
settings.py
. Este arquivo é usado para armazenar as configurações do nosso projeto. Adicione o seguinte código ao arquivosettings.py
:1 # settings.py 2 import os 3 from dotenv import load_dotenv 4 5 load_dotenv() 6 BOT_NAME = 'nse_scraper' 7 8 SPIDER_MODULES = ['nse_scraper.spiders'] 9 NEWSPIDER_MODULE = 'nse_scraper.spiders' 10 11 # MONGODB SETTINGS 12 MONGODB_URI = os.getenv("MONGODB_URI") 13 MONGO_DATABASE = os.getenv("MONGO_DATABASE") 14 15 ITEM_PIPELINES = { 16 'nse_scraper.pipelines.NseScraperPipeline': 300, 17 } 18 LOG_LEVEL = "INFO" 19 20 # USER_AGENT = 'nse_scraper (+http://www.yourdomain.com)' 21 22 # Obey robots.txt rules 23 ROBOTSTXT_OBEY = False 24 25 26 # Override the default request headers: 27 DEFAULT_REQUEST_HEADERS = { 28 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 29 'Accept-Language': 'en', 30 } 31 32 # Enable and configure HTTP caching (disabled by default) 33 # See https://docs.scrapy.org/en/latest/topics/downloader-middleware.html#httpcache-middleware-settings 34 HTTPCACHE_ENABLED = True 35 HTTPCACHE_EXPIRATION_SECS = 360 36 HTTPCACHE_DIR = 'httpcache' 37 # HTTPCACHE_IGNORE_HTTP_CODES = [] 38 HTTPCACHE_STORAGE = 'scrapy.extensions.httpcache.FilesystemCacheStorage'
Primeiro, importamos os módulos
os
e load_dotenv
. Chamamos então a funçãoload_dotenv
. Não utiliza argumentos. Esta função carrega as variáveis de ambiente do arquivo.env
.nse_scraper.spiders
. Anexamos a variávelMONGODB_URI
e a definimos para a variável de ambiente MONGODB_URI
. Em seguida, criamos a variávelMONGODB_DATABASE
e a definimos para a variável de ambienteMONGO_DATABASE
.Depois, criamos a variável
ITEM_PIPELINES
e a configuramos como nse_scraper.pipelines.NseScraperPipeline
. Em seguida, criamos a variávelLOG_LEVEL
e a configuramos como INFO
. A variávelDEFAULT_REQUEST_HEADERS
está definida como um dicionário. A seguir, criamos a variávelHTTPCACHE_ENABLED
e a configuramos como True
.Altere a variável
HTTPCACHE_EXPIRATION_SECS
e defina-a para360
. Crie a variável HTTPCACHE_DIR
e defina-a comohttpcache
. Finalmente, crie a variável HTTPCACHE_STORAGE
e defina-a parascrapy.extensions.httpcache.FilesystemCacheStorage
.A estrutura do projeto é a seguinte:
1 ├ nse_stock_scraper 2 ├ nse_scraper 3 ├── __init__.py 4 │ ├── items.py 5 │ ├── middlewares.py 6 │ ├── pipelines.py 7 │ ├── settings.py 8 ├─ stock_notification.py 9 │ └── spiders 10 │ ├── __init__.py 11 │ └── afx_scraper.py 12 ├── README.md 13 ├── LICENSE 14 ├── requirements.txt 15 └── scrapy.cfg 16 ├── .gitignore 17 ├── .env
Para executar o scraper, precisaremos abrir um terminal e navegar até o diretório do projeto. Em seguida, precisaremos ativar o ambiente virtual, se ele ainda não estiver ativado. Podemos fazer isso executando o seguinte comando:
1 source venv/bin/activate
Crie um arquivo
.env
na raiz do projeto (em /nse_scraper/). Adicione o seguinte código ao arquivo.env
:1 MONGODB_URI=mongodb+srv:// 2 MONGODB_DATABASE= 3 at_username= 4 at_api_key= 5 mobile_number=
Adicione seu URI do MongoDB, o nome do banco de dados, o nome de usuário do Africas Talking, a chave da API e o número do celular ao
.env
arquivo do seu URI do MongoDB. Você pode usar a camada gratuita do MongoDB Atlas. Obtenha seu URI no connect
painel do Atlas, no botão . Ele deve ter a seguinte aparência:1 mongodb+srv://<username>:<password>@<cluster-name>.mongodb.net/<database-name>?retryWrites=true&w=majority
Precisamos executar o seguinte comando para executar o scraper enquanto estiver na pasta do projeto:
(/nse_scraper/):
1 scrapy crawl afx_scraper
Instale o módulo
africastalking
executando o seguinte comando no terminal:1 pip install africastalking
Crie um novo arquivo chamado
stock_notification.py
no diretório nse_scraper
. Adicione o seguinte código ao arquivo
stock_notification.py :
1 # stock_notification.py 2 import africastalking as at 3 import os 4 from dotenv import load_dotenv 5 import pymongo 6 7 load_dotenv() 8 9 at_username = os.getenv("at_username") 10 at_api_key = os.getenv("at_api_key") 11 mobile_number = os.getenv("mobile_number") 12 mongo_uri = os.getenv("MONGODB_URI") 13 14 # Initialize the Africas sdk py passing the api key and username from the .env file 15 at.initialize(at_username, at_api_key) 16 sms = at.SMS 17 account = at.Application 18 19 20 ticker_data = [] 21 22 # Create a function to send a message containing the stock ticker and price 23 def stock_notification(message: str, number: int): 24 try: 25 response = sms.send(message, [number]) 26 print(account.fetch_application_data()) 27 print(response) 28 except Exception as e: 29 print(f" Houston we have a problem: {e}") 30 31 # create a function to query mongodb for the stock price of Safaricom 32 def stock_query(): 33 client = pymongo.MongoClient(mongo_uri) 34 db = client["nse_data"] 35 collection = db["stock_data"] 36 # print(collection.find_one()) 37 ticker_data = collection.find_one({"ticker": "BAT"}) 38 print(ticker_data) 39 stock_name = ticker_data["name"] 40 stock_price = ticker_data["price"] 41 sms_data = { "stock_name": stock_name, "stock_price": stock_price } 42 print(sms_data) 43 44 message = f"Hello the current stock price of {stock_name} is {stock_price}" 45 # check if Safaricom share price is more than Kes 39 and send a notification. 46 if int(float(stock_price)) >= 38: 47 # Call the function passing the message and mobile_number as a arguments 48 print(message) 49 stock_notification(message, mobile_number) 50 else: 51 print("No notification sent") 52 53 client.close() 54 55 return sms_data 56 57 stock_query()
O código acima importa o módulo
africastalking
. Importe os módulosos
e load_dotenv
. Passamos a chamar a funçãoload_dotenv
. Não são necessários argumentos. Essa função carrega as variáveis de ambiente do arquivo.env
.- Criamos a variável
at_username
e a definimos como a variável de ambienteat_username
. Em seguida, criamos a variávelat_api_key
e a definimos como a variável de ambienteat_api_key
. Crie a variávelmobile_number
e defina-a como a variável de ambientemobile_number
. E crie a variávelmongo_uri
e defina-a como a variável de ambienteMONGODB_URI
. - Inicializamos o módulo
africastalking
passando as variáveisat_username
eat_api_key
como argumentos. Crie a variávelsms
e defina-a comoat.SMS
. Crie a variávelaccount
e defina-a comoat.Application
. - Crie a variável
ticker_data
e defina-a como uma lista vazia. Crie a funçãostock_notification
. São necessários dois argumentos:message
enumber
. Em seguida, tentamos enviar a mensagem para o número e imprimir a resposta. Procure por exceções e exiba-as. - Criamos a função
stock_query
. Em seguida, criamos a variávelclient
e a definimos para um objetopymongo.MongoClient
. Crie a variáveldb
e defina-a para o banco de dadosnse_data
. Em seguida, crie a variávelcollection
e defina-a para a coleçãostock_data
e crie a variávelticker_data
e defina-a para o métodocollection.find_one
. Aceita um dicionário como argumento.
A variável
stock_name
é definida como a chavename
no dicionárioticker_data
. Crie a variávelstock_price
e defina-a como a chaveprice
no dicionárioticker_data
. Crie a variávelsms_data
e defina-a como um dicionário. Ele contém as variáveis stock_name
e stock_price
.A variável
message
é definida como uma string contendo o nome e o preço da ação. Verificamos se o preço da ação é maior ou igual a 38. Se for, chamamos a funçãostock_notification
e passamos as variáveismessage
e mobile_number
como argumentos. Caso contrário, imprimimos uma mensagem no console.Feche a conexão com o banco de dados e retorne a variável
sms_data
. Chame a funçãostock_query
.Precisamos adicionar o seguinte código ao
afx_scraper.py
arquivo :1 # afx_scraper.py 2 from nse_scraper.stock_notification import stock_query 3 4 # ... 5 6 # Add the following code to the end of the file 7 stock_query()
Se tudo estiver configurado corretamente, você deverá ver algo assim:
Precisamos criar um novo cluster no MongoDB Atlas. Podemos fazer isso por:
- Clicar no botão
Build a Cluster
. - Selecionando a opção
Shared Clusters
. - Selecionando a opção
Free Tier
. - Selecionando a opção
Cloud Provider & Region
. - Selecionando a opção
AWS
. (Selecionei a opção AWS Cape Town.) - Selecionando a opção
Cluster Name
. - Dando um nome ao cluster. (Podemos chamá-lo de
nse_data
.)
Vamos configurar um usuário para acessar o cluster seguindo as etapas abaixo:
- Selecione a opção
Database Access
. - Clique na opção
Add New User
. - Dê ao usuário um nome de usuário. (Eu usei
nse_user.)
. - Dê ao usuário uma senha. (Eu usei
nse_password
). - Selecione a opção
Network Access
. - Selecione a opção
Add IP Address
. - Selecione a opção
Allow Access from Anywhere
. - Selecione a opção
Cluster
. Em seguida, será necessário selecionar a opçãoCreate Cluster
.
Clique na opção
Collections
e depois no botão+ Create Database
. Dê um nome ao banco de dados. Podemos chamá-lo nse_data
. Clique no botão+ Create Collection
. Dê um nome à collection. Podemos chamar isso stock_data
. Se tudo estiver configurado corretamente, você deverá ver algo assim:Se você vir uma collection vazia, execute novamente o projeto no terminal para preencher os valores no MongoDB. Em caso de erro, leia a saída do terminal. Problemas comuns podem ser:
- O IP aAddress não foi adicionado no painel.
- Falta de credenciais ou credenciais incorretas em seuarquivo.env.
- Um erro de sSyntax no seu código.
- Verifique sua conexão com a Internet.
- A lFalta de permissões apropriadas para seu usuário.
Go ver como visualizar métricas relacionadas ao(s) nosso(s) banco(s) de dados.
- Clique na opção **
Metrics
. - Clique no botão
+ Add Metric
. - Selecione a opção
Database
. - Selecione a opção
nse_data
. - Selecione a opção
Collection
. - Selecione a opção
stock_data
. - Selecione a opção
Metric
. - Selecione a opção
Documents
. - Selecione a opção
Time Range
. - Selecione a opção
Last 24 Hours
. - Selecione a opção
Granularity
. - Selecione a opção
1 Hour
. - Clique no botão
Add Metric
.
Se tudo estiver configurado corretamente, ficará assim:
MongoDB Atlas oferece Atlas Charts que podem ser utilizados para visualizar os dados no banco de dados. Clique na opção
Charts
. Em seguida, clique no botão + Add Chart
. Selecione a opçãoDatabase
. Abaixo está uma captura de tela de exemplos de Atlas Charts para dados NSE:Execute o seguinte comando no seu terminal para inicializar um repositório git:
1 git init
Crie um arquivo
.gitignore
. Podemos fazer isso executando o seguinte comando em nosso terminal:1 touch .gitignore
Vamos adicionar o arquivo .env ao arquivo .gitignore. Adicione o seguinte código ao arquivo
.gitignore
:1 # .gitignore 2 .env
Adicione os arquivos à área de preparação executando o seguinte comando em nosso terminal:
1 git add .
Confirme os arquivos no repositório executando o seguinte comando em nosso terminal:
1 git commit -m "Initial commit"
Crie um novo repositório no GitHub clicando no ícone
+
no canto superior direito da página e selecionando New repository
. Dê um nome ao repositório. Podemos chamá-lo nse-stock-scraper
. Selecione Public
como a visibilidade do repositório. Selecione Add a README file
e Add .gitignore
, e selecione Python
no menu suspenso. Clique no botãoCreate repository
.Adicione o repositório remoto ao nosso repositório local executando o seguinte comando em seu terminal:
1 git remote add origin
Envie os arquivos para o repositório remoto executando o seguinte comando no seu terminal:
1 git push -u origin master
Crie uma nova pasta —
.github
— e uma pastaworkflows
dentro, no diretório raiz do projeto. Podemos fazer isso executando o seguinte comando em nosso terminal. Dentro do workflows file
, precisaremos criar um novo arquivo chamado scraper-test.yml
. Podemos fazer isso executando o seguinte comando em nosso terminal:1 touch .github/workflows/scraper-test.yml
Dentro do
scraper-test.yml arquivo, precisaremos adicionar o seguinte código:
1 name: Scraper test with MongoDB 2 3 on: [push] 4 5 jobs: 6 build: 7 8 runs-on: ubuntu-latest 9 strategy: 10 matrix: 11 python-version: [3.8, 3.9, "3.10"] 12 mongodb-version: ['4.4', '5.0', '6.0'] 13 14 steps: 15 - uses: actions/checkout@v2 16 - name: Set up Python ${{ matrix.python-version }} 17 uses: actions/setup-python@v1 18 with: 19 python-version: ${{ matrix.python-version }} 20 - name: Set up MongoDB ${{ matrix.mongodb-version }} 21 uses: supercharge/mongodb-github-action@1.8.0 22 with: 23 mongodb-version: ${{ matrix.mongodb-version }} 24 - name: Install dependencies 25 run: | 26 python -m pip install --upgrade pip 27 pip install -r requirements.txt 28 - name: Lint with flake8 29 run: | 30 pip install flake8 31 # stop the build if there are Python syntax errors or undefined names 32 flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics 33 # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide 34 flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics 35 - name: scraper-test 36 run: | 37 cd nse_scraper 38 export MONGODB_URI=mongodb://localhost:27017 39 export MONGO_DATABASE=nse_data 40 scrapy crawl afx_scraper -a output_format=csv -a output_file=afx.csv 41 scrapy crawl afx_scraper -a output_format=json -a output_file=afx.json
Vamos detalhar o código acima. Criamos um novo fluxo de trabalho chamado
Scraper test with MongoDB
. Em seguida, definimos o eventoon
como push
. Crie um novo trabalho chamado build
. Defina o runs-on
para ubuntu-latest
. Defina o strategy
como uma matriz. Ele contém as variáveispython-version
e mongodb-version
. Defina o python-version
para 3.8
, 3.9
e 3.10
. Defina o mongodb-version
como 4.4
, 5.0
e 6.0
.Crie uma nova etapa chamada
Checkout
. Configure o uses
para actions/checkout@v2
. Crie uma nova etapa chamada Set up Python ${{ matrix.python-version }}
e defina uses
como actions/setup-python@v1
. Defina python-version
como ${{ matrix.python-version }}
. Crie uma nova etapa chamada Set up MongoDB ${{ matrix.mongodb-version }}
. Isso configura diferentes versões do Python e versões do MongoDB para teste.A etapa
Install dependencies
instala as dependências. Crie uma nova etapa chamada Lint with flake8
. Esta etapa liga o código. Crie uma nova etapa chamada scraper-test
. Esta etapa executa o raspador e o testa.Confirme as alterações no repositório executando o seguinte comando no seu terminal:
1 git add . 2 git commit -m "Add GitHub Actions" 3 git push
Go para a aba
Actions
no seu repositório. Você deve ver algo assim:Neste tutorial, construímos um raspador de preço de ações usando Python e Scrapy. Em seguida, usamos o MongoDB para armazenar os dados raspados. Usamos o Africas Talking para enviar notificações por SMS. Por fim, implementamos um pipeline de CI/CD usando o GitHub Actions.
Existem melhorias definitivas que podem ser feitas neste projeto. Por exemplo, podemos adicionar mais bolsas de valores. Também podemos adicionar mais canais de notificação. Este projeto deve servir como um bom ponto de partida.
Graças para ler até agora., espero que você tenha obtido informações ou expiração para seu próximo projeto com o MongoDB Atlas. Sinta-se à vontade para comentar abaixo ou entrar em contato para solicitar novas melhorias. Gostaríamos muito de ouvir você! Este projeto é oOpen sSource e está disponível no GitHub —, clone ou fork it !, Estou ansioso para ver o que você cria.