La dernière fois, j'ai implémenté une fonction pour acquérir des informations sur le cours des actions, mais en fait, "C'est vrai, j'ai utilisé l'application boursière sans demander au BOT. Ce n'est qu'une question de temps avant que le tsukkomi ne dise: «C'est plus pratique», et j'utilise probablement une certaine application de gestion de portefeuille quotidiennement, alors j'aimerais que BOT identifie le cours de l'action. Il n'en vient pas à avoir le besoin. Je suis donc retourné au point de départ, je me suis demandé quel type de fonction je souhaitais et j'ai recherché une fonction plus pratique.
"Oui, ce serait pratique si nous pouvions saisir les dates de règlement (prévues) d'un seul coup."
Si vous possédez beaucoup d'actions, il est assez difficile de suivre quotidiennement le calendrier des actions individuelles, les résultats financiers semestriels, les résultats financiers trimestriels, etc. Ce serait pratique s'il y avait une fonction qui puisse collecter et visualiser cela de manière centralisée, et surtout, je pense que ce serait bien si c'était la manière originale de BOT.
Donc, cette fois, je voudrais faire une technique de grattage extrêmement avancée visant à renforcer les compétences pour extraire et analyser les informations du site Web.
Lorsque vous entrez le code d'activation comprenant le code de la marque à partir de l'application LINE (①), le BOT accède au site Web, examine la dernière date d'annonce des résultats financiers (prévue) et répond (②). Il est également supposé que les actions individuelles seront spécifiées ou qu'un portefeuille sera défini à l'avance et plusieurs actions seront acquises à la fois.
Nous utiliserons un site appelé Stock Forecast. Lorsque vous ouvrez la page des actions individuelles, la dernière date d'annonce des résultats financiers (prévue) est affichée à la position indiquée dans la figure ci-dessous. Si les résultats financiers ont été annoncés, ce fait sera affiché avec la date. Si son annonce est prévue à l'avenir, elle sera affichée avec la date comme date d'annonce prévue. La source HTML correspondant à cette position est la classe «" header_main "» commençant à la ligne 222. Vous pouvez voir que toutes les informations que vous souhaitez sont incluses en dessous.
Par conséquent, il semble que cette mission puisse être réalisée en comprenant les compétences des trois processus suivants.
<div class =" header_main ">
Donc, si vous écrivez d'abord la conclusion
Pour 1. demandes
2. Pour "Belle soupe"
3. Pour «re»
Un codage très intelligent est réalisé en utilisant chacun d'eux.
La requête ayant déjà été packagée lors de la mise en œuvre du chatbot, les détails sont omis. L'exemple de codage est le suivant
(Exemple d'exécution de script)Comment utiliser les demandes
(botenv2) [botenv2]$ python
Python 3.6.7 (default, Dec 5 2018, 15:02:16)
>>> import requests
#Obtenir la source HTML
>>> r = requests.get('https://kabuyoho.ifis.co.jp/index.php?action=tp1&sa=report_top&bcode=4689')
#Confirmation de contenu
>>> print(r.headers)
{'Cache-Control': 'max-age=1', 'Content-Encoding': 'gzip', 'Content-Type': 'text/html; charset=UTF-8', (réduction)
>>> print(r.encoding)
UTF-8
>>> print(r.content)
(réduction)
Avec ce sentiment, aucune explication particulière n'est nécessaire. Puisque toute la partie du corps est stockée dans r.content, elle sera bouillie ou cuite par la suite. Vous pouvez utiliser la balise HTML cible comme clé pour extraire des informations.
Il semble que ce soit un package célèbre et implémente déjà divers analyseurs. Lorsque je l'ai présenté, j'ai pu atteindre 90% de l'objectif cette fois. .. Comparé à l'époque où je jouais avec le langage C, je me sens comme un monde à part.
Belle installation de soupe
(botenv2) [botenv2]$ pip install BeautifulSoup4
(Exemple d'exécution de script@A continué)Comment utiliser Beautiful Soup
>>> from bs4 import BeautifulSoup
#Analyse de la partie Body avec un analyseur
>>> soup = BeautifulSoup(r.content, "html.parser")
Essayez d'afficher la classe header_main.
python
>>> print(soup.find("div", class_="header_main"))
Résultat d'exécution
<div class="header_main">
<div class="stock_code left">4689</div>
<div class="stock_name left">Participations Z</div>
<div class="block_update right">
<div class="title left">
Revenus annoncés
</div>
<div class="settle left">
2Q
</div>
<div class="date left">
2019/11/01
</div>
<div class="float_end"></div>
</div>
<div class="float_end"></div>
</div>
C'est incroyable. C'est trop pratique d'arrêter de trembler.
Il ne reste plus qu'à supprimer les chaînes de caractères inutiles. Étant donné que les balises HTML ne sont pas requises, utilisez la méthode text.
(Exemple d'exécution de script@A continué)Extraction de texte
>>> s = soup.find("div", class_="header_main").text
>>> print(s)
4689
Participations Z
Revenus annoncés
2Q
2019/11/01
>>>
Les balises ont été effacées, mais il reste encore beaucoup de lacunes mystérieuses. Je ne savais pas s'il s'agissait d'un espace ou d'un méta-personnage, alors j'en suis tombé amoureux pendant un moment. Dans un tel cas, la substance peut être vue en l'affichant sous forme d'octet.
(référence)Confirmation du code de caractère
>>> s.encode()
b'\n4689\n\xef\xbc\xba\xe3\x83\x9b\xe3\x83\xbc\xe3\x83\xab\xe3\x83\x87\xe3\x82\xa3\xe3\x83\xb3\xe3\x82\xb0\xe3\x82\xb9\n\n\n\t\t\t\t\t\t\t\t\t\xe6\xb1\xba\xe7\xae\x97\xe7\x99\xba\xe8\xa1\xa8\xe6\xb8\x88\n\t\t\t\t\t\t\t\n\n\t\t\t\t\t\t\t\t\t2Q\n\t\t\t\t\t\t\t\n\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t2019/11/01\n\t\t\t\t\t\t\t\t\t\t\t\t\n\n\n\n'
Le but est de supprimer / n et / t. Remplaçons-le sans pitié par une virgule et enterrons-le.
(Exemple d'exécution de script@A continué)Suppression de méta-caractères
>>> import re
>>> s = re.sub(r'[\n\t]+', ',', s)
>>> print(s)
,4689,Participations Z,Revenus annoncés,2Q,2019/11/01,
Si vous supprimez les virgules inquiétantes avant et après la fin
(Exemple d'exécution de script@A continué)
>>> s = re.sub(r'(^,)|(,$)','', s)
>>> print(s)
4689,Participations Z,Revenus annoncés,2Q,2019/11/01
Ça fait du bien. Il semble qu'il puisse être converti en CSV ou en dataframe tel quel.
À propos, lorsqu'un code de marque qui n'existe pas est acquis, le code de caractère suivant reste après le traitement ci-dessus.
Pour code stock inexistant
>>> print(s)
b'\xc2\xa0'
Ce \ xc2 \ xa0
signifie NO-BREAK SPACE en Unicode et correspond à & nbsp
en HTML.
Si ce code de caractère est inclus, il interférera avec le traitement ultérieur, il est donc souhaitable de le supprimer si possible.
(Cela semble être un problème courant lors du scraping de pages Web.)
(Référence) [Python3] Que faire si vous rencontrez [\ xa0] pendant le scraping
&suppression de nbsp
s = re.sub(r'[\xc2\xa0]','', s)
Voici une version fonctionnalisée du processus ci-dessus.
getSettledata.py
import requests
from bs4 import BeautifulSoup
import re
import logging
logger = logging.getLogger('getSettledata')
source = 'https://kabuyoho.ifis.co.jp/index.php?action=tp1&sa=report_top&bcode='
#Fonction d'acquisition de la date de règlement(4689 si l'argument est vide(ZHD)Se référer aux données de)
def get_settleInfo(code="4689"):
#Rampant
try:
logger.debug('read web data cord = ' + code) #logging
r = requests.get(source + code)
except:
logger.debug('read web data ---> Exception Error') #logging
return None, 'Exception error: access failed'
#Grattage
soup = BeautifulSoup(r.content, "html.parser")
settleInfo = soup.find("div", class_="header_main").text
settleInfo = re.sub(r'[\n\t]+', ',', settleInfo) #Suppression des méta caractères
settleInfo = re.sub(r'(^,)|(,$)','', settleInfo) #Supprimer les virgules au début et à la fin des lignes
settleInfo = re.sub(r'[\xc2\xa0]','', settleInfo) # (\xc2\xa0)Faire face au problème
logger.debug('settleInfo result = ' + settleInfo) #logging
if not settleInfo:
settleInfo = 'Il n'y a pas de telle marque ~'
return settleInfo
if __name__ == '__main__':
print(get_settleInfo())
Pour le programme principal, ajoutez un traitement de branche conditionnel en identifiant le code d'activation comme d'habitude.
Si vous créez votre propre portfolio dans SETTLEVIEW_LIST_CORD
à l'avance, vous serez éligible pour l'acquisition par lots.
chatbot.py(★ Ajout)##La partie fonction existante est omise car elle n'est pas modifiée.
# -*- Coding: utf-8 -*-
from django.views.decorators.csrf import csrf_exempt
from django.http import HttpResponse
from django.shortcuts import render
from datetime import datetime
from time import sleep
import requests
import json
import base64
import logging
import os
import random
import log.logconfig
from utils import tools
import re
from .getStockdata import get_chart
from .getSettledata import get_settleInfo
logger = logging.getLogger('commonLogging')
LINE_ENDPOINT = 'https://api.line.me/v2/bot/message/reply'
LINE_ACCESS_TOKEN = ''
###
###Omis
###
SETTLEVIEW_KEY = ['Règlement','settle'] #★ Ajout
SETTLEVIEW_LIST_KEY = ['Liste des résultats financiers'] #★ Ajout
SETTLEVIEW_LIST_CORD = ['4689','3938','4755','1435','3244','3048'] #★ Ajout
@csrf_exempt
def line_handler(request):
#exception
if not request.method == 'POST':
return HttpResponse(status=200)
logger.debug('line_handler message incoming') #logging
out_log = tools.outputLog_line_request(request) #logging
request_json = json.loads(request.body.decode('utf-8'))
for event in request_json['events']:
reply_token = event['replyToken']
message_type = event['message']['type']
user_id = event['source']['userId']
#whitelist
if not user_id == LINE_ALLOW_USER:
logger.warning('invalid userID:' + user_id) #logging
return HttpResponse(status=200)
#action
if message_type == 'text':
if:
###
###Omis
###
elif any(s in event['message']['text'] for s in SETTLEVIEW_KEY): #★ Ajout
action_data(reply_token,'settleview',event['message']['text']) #★ Ajout
else:
###
###Omis
###
return HttpResponse(status=200)
def action_res(reply_token,command,):
###
###Omis
###
def action_data(reply_token,command,value):
#Graphique boursier
###
###Omis
###
#######################################################★ Ajout d'ici
#L'information financière
elif command == 'settleview':
logger.debug('get_settleInfo on') #logging
#Acquisition collective de titres en portefeuille
if any(s in value for s in SETTLEVIEW_LIST_KEY):
logger.debug('get_settleInfo LIST') #logging
results = []
for cord in SETTLEVIEW_LIST_CORD:
results.append(get_settleInfo(cord))
logger.debug('get_settleInfo LIST ---> ' + '\n'.join(results)) #logging
response_text(reply_token,'\n'.join(results))
#Acquisition de stocks individuels
else:
cord = re.search('[0-9]+$', value)
logger.debug('get_settleInfo cord = ' + cord.group()) #logging
result = get_settleInfo(cord.group())
if result[0] is not None:
response_text(reply_token,result)
else:
response_text(reply_token,result[1])
#######################################################★ Ajout jusqu'à ici
def response_image(reply_token,orgUrl,preUrl,text):
###
###Omis
###
def response_text(reply_token,text):
payload = {
"replyToken": reply_token,
"messages":[
{
"type": 'text',
"text": text
}
]
}
line_post(payload)
def line_post(payload):
url = LINE_ENDPOINT
header = {
"Content-Type": "application/json",
"Authorization": "Bearer " + LINE_ACCESS_TOKEN
}
requests.post(url, headers=header, data=json.dumps(payload))
out_log = tools.outputLog_line_response(payload) #logging
logger.debug('line_handler message -->reply') #logging
def ulocal_chatting(event):
###
###Omis
###
Ceci termine.
line_Lancer le bot
(botenv2) [line_bot]$ gunicorn --bind 127.0.0.1:8000 line_bot.wsgi:application
Si vous déposez un message selon le format de l'application LINE, le résultat sera renvoyé.
Si vous voulez tout obtenir en même temps, entrez «Liste des états financiers».
Il faut environ 1 seconde pour mesurer 6 marques en série. J'ai été impressionné par le fait qu'il a été traité plus rapidement que je ne l'avais imaginé, mais au cas où cela me dérangerait trop, je l'utiliserai modérément pour ne pas y accéder fréquemment. Jusqu'à ici pour cette fois.