2017年10月17日

VALUの別の情報もつぶやいてみる(Python+MySQL+Tweepy)

下記のエントリでVALUの時価総額ランキングを呟くようにしました。
下記のエントリーでVALUの情報をスクレイピングしてMySQLに格納しました。...
今回は、時価総額だけでなく追加の情報もつぶやくようにしました。その内容についてのエントリーです。

追加のつぶやき

今回は、騰落率と新規公募時の時価総額のランキングをつぶやくようにしました。
下記をつぶやくまでに、幾つか注意した点を明記しておきます。

追加でスクレイピングした情報

今まではVALUのページの左側の情報のみを入手していましたが、今回は右側の情報も取得しました。

20171016_valu_0.png

また、今回は、右のように前日終値がついていないものは新規公募扱いとしてフラグを立てています。

20171016_valu_1.png

該当箇所のコードは下記となります。
    # em tagで順番に 現在値:時価総数:発行VA数:VALUER数となる
current_value = float(re.sub(",","",soup.select(".news_valu_left em")[0].string))
ag_market_value = float(re.sub(",","",soup.select(".news_valu_left em")[1].string))
value_issue = float(re.sub(",","",soup.select(".news_valu_left em")[2].string))
numerical_value = float(re.sub(",","",soup.select(".news_valu_left em")[3].string))

if soup.select(".news_valu_right em")[0].string == "---":
# 新規メンバーの場合
last_close_value = float(0)
last_first_value = float(re.sub(",","",soup.select(".news_valu_right em")[1].string))
last_top_value = float(0)
trunover = float(re.sub(",","",soup.select(".news_valu_right em")[3].string))
yearly_high = float(0)
yearly_low = float(0)
diff_rate = 0
newid = 1
else :
last_close_value = float(re.sub(",","",soup.select(".news_valu_right em")[0].string))
last_first_value = float(re.sub(",","",soup.select(".news_valu_right em")[1].string))
last_top_value = float(re.sub(",","",soup.select(".news_valu_right em")[2].string))
trunover = float(re.sub(",","",soup.select(".news_valu_right em")[3].string))
yearly_high = float(re.sub(",","",soup.select(".news_valu_right em")[4].string))
yearly_low = float(re.sub(",","",soup.select(".news_valu_right em")[5].string))
diff_rate = 100 * (current_value - last_close_value) / last_close_value
newid = 0

連投制限

Twitterでは全く同じ発言をつぶやこうとするとErrorが発生します(20回に1回ぐらいなら良いのかもしれませんが)。そのため前日と全く同じデータ・ランキングの場合にはTweetすることが出来ません。そのため、ランダムな数字を文末に挿入することにしました。該当箇所のコードは下記となります。
    for row in cur.fetchall():
tweet = tweet_prev + str(rank)+"位:"+row[1]+"("+row[0]+"):"+str(row[index])+str(unit)+"\n"
#140文字以上になる場合は、140文字以内になるように分割してリストに追加
if len(tweet) > 130:
tweet_list.append(tweet_prev+"\n"+str(random.randint(1,100)))
tweet_prev = str(rank)+"位:"+row[1]+"("+row[0]+"):"+str(row[index])+str(unit)+"\n"
tweet = ""
else:
tweet_prev = tweet
では、これらのことに注意して修正した全コードに続きます。

メインコード

DBへの格納

#!/virtual/kumanabe/.pyenv/versions/anaconda3-4.4.0/bin/python

import MySQLdb
import re
import urllib.request as urlreq
from bs4 import BeautifulSoup
import requests

# 文字コード(UTF-8)関連
import io,sys
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8')

def make_category_list():
category_list = []
# 念のため最大LOOP数を指定しておく
maxpage = 10000

# カテゴリーページにアクセス
for num in range(1,maxpage):
res = requests.get("https://valu.is/users/categories?page="+str(num))
soup = BeautifulSoup(res.text, "lxml")

# Linkの箇所をselect
links = soup.select(".valuer_category a[href]")

# もし最終ページだったらforループから抜ける
if links == []:
break

for a in links:
# Linkタグのhref属性の箇所を抜き出す
href = a.attrs['href']
# Link先の最後の数字がカテゴリーナンバー
hrefOB = re.search(r"\/([0-9]+)",href)
# カテゴリー名の取得
vc_name = re.sub("[\t\s\n]+","",a.text)
vc_name = re.sub("\(.*\)","",vc_name)
if hrefOB:
category = [hrefOB.group(1),vc_name]
category_list.append(category)
return(category_list)

#各カテゴリーごとにDBへの情報書き込みを実施する
def insert_data(numberOfCat, nameOfCat, cur):
# 一時的に保存するページのリスト
# 重複はないと思うけれど重複チェックのために使用
linkData = []
# 念のため最大LOOP数を指定しておく
maxpage = 10000

# カテゴリーから各IDのリンク先を取得
for num in range(1,maxpage):
res = requests.get("https://valu.is/users/categories/"+str(numberOfCat)+"?type=0&page="+str(num))
soup = BeautifulSoup(res.text, "lxml")

# Linkの箇所をselect
links = soup.select(".ranking_info_box a[href]")

# もし最終ページだったらforループから抜ける
if links == []:
break

for a in links:
# Linkタグのhref属性の箇所を抜き出す
href = a.attrs['href']
# 各IDのリンクを引数に渡してDBへの書き込みを実施する
if not re.search(r"users",href):
if not href in linkData:
linkData.append(href)
insert_data_link(href,nameOfCat,cur)

# Linkから各IDのVALU個別情報を取得してDBに書き込む
def insert_data_link(link,nameOfCat,cur):
res = requests.get(link+"/data")
soup = BeautifulSoup(res.text, "lxml")

# IDはURLから抜き出し
nameid = re.search(r"https:\/\/valu.is\/(.*)",link).group(1)
icon_url = re.search(r"url\((.*)\)",str(soup.select_one(".user_icon"))).group(1)
namestr = re.sub("[\t\s\n]+","",soup.select_one(".user_introduction").text)

# em tagで順番に 現在値:時価総数:発行VA数:VALUER数となる
current_value = float(re.sub(",","",soup.select(".news_valu_left em")[0].string))
ag_market_value = float(re.sub(",","",soup.select(".news_valu_left em")[1].string))
value_issue = float(re.sub(",","",soup.select(".news_valu_left em")[2].string))
numerical_value = float(re.sub(",","",soup.select(".news_valu_left em")[3].string))

if soup.select(".news_valu_right em")[0].string == "---":
# 新規メンバーの場合
last_close_value = float(0)
last_first_value = float(re.sub(",","",soup.select(".news_valu_right em")[1].string))
last_top_value = float(0)
trunover = float(re.sub(",","",soup.select(".news_valu_right em")[3].string))
yearly_high = float(0)
yearly_low = float(0)
diff_rate = 0
newid = 1
else :
last_close_value = float(re.sub(",","",soup.select(".news_valu_right em")[0].string))
last_first_value = float(re.sub(",","",soup.select(".news_valu_right em")[1].string))
last_top_value = float(re.sub(",","",soup.select(".news_valu_right em")[2].string))
trunover = float(re.sub(",","",soup.select(".news_valu_right em")[3].string))
yearly_high = float(re.sub(",","",soup.select(".news_valu_right em")[4].string))
yearly_low = float(re.sub(",","",soup.select(".news_valu_right em")[5].string))
diff_rate = 100 * (current_value - last_close_value) / last_close_value
newid = 0

data = (nameid,)
cur.execute("SELECT EXISTS( SELECT * FROM items WHERE id = %s )",data)
if cur.fetchone()[0]==0:
# データが存在しない場合には新規で書き込み
data = (nameid,namestr,icon_url,nameOfCat,current_value,ag_market_value,value_issue,numerical_value, \
last_close_value,last_first_value,last_top_value,trunover,yearly_high,yearly_low,diff_rate,newid)
cur.execute("INSERT INTO items(id,name,icon,category,current_value,total_value,value_issue,valuer, \
last_close_value,last_first_value,last_top_value,trunover,yearly_high,yearly_low,diff_rate,newid) \
VALUES(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)",data)
else:
# データが存在する場合には上書き
data = (namestr,icon_url,nameOfCat,current_value,ag_market_value,value_issue,numerical_value,\
last_close_value,last_first_value,last_top_value,trunover,yearly_high,yearly_low,diff_rate,newid,\
nameid)
cur.execute("UPDATE items SET name = %s, icon = %s, category = %s, current_value = %s, total_value = %s, value_issue = %s, valuer = %s, \
last_close_value = %s, last_first_value = %s, last_top_value = %s, trunover = %s, yearly_high = %s, yearly_low = %s, diff_rate = %s, newid = %s \
WHERE id = %s",data)
cur.execute("COMMIT")

def main():
conn = MySQLdb.connect(
user='kumanabe_valu',
passwd='xxxxxxxxxx',
host='localhost',
db='kumanabe_valu',
charset="utf8")
cur = conn.cursor()

if len(sys.argv) == 2:
if sys.argv[1] == "init":
# 初回起動時のみ実施
print("----- Initialize DB -----")
cur.execute('DROP TABLE items')
cur.execute('''
CREATE TABLE items (
id TEXT,
name TEXT,
icon TEXT,
category TEXT,
current_value FLOAT,
total_value FLOAT,
value_issue FLOAT,
valuer FLOAT,
last_close_value FLOAT,
last_first_value FLOAT,
last_top_value FLOAT,
trunover FLOAT,
yearly_high FLOAT,
yearly_low FLOAT,
diff_rate FLOAT,
newid FLOAT
)
''')

# カテゴリーのリストを取得
category_list = make_category_list()
for number,name in category_list:
# Debug用にカテゴリー名は表示する
print(name)
insert_data(number,name,cur)

if __name__ == "__main__":
main()

Tweet部分

#!/virtual/kumanabe/.pyenv/versions/anaconda3-4.4.0/bin/python

import MySQLdb
import tweepy
import random
from datetime import datetime

# 文字コード(UTF-8)関連
import io,sys
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8')

#Twitter認証
CONSUMER_KEY = 'xxxxxxxxxx'
CONSUMER_SECRET = 'xxxxxxxxxxxxxxxxxxxxx'
auth = tweepy.OAuthHandler(CONSUMER_KEY, CONSUMER_SECRET)
ACCESS_TOKEN = 'xxxxxxxxxxxxxxxxxxxxx'
ACCESS_SECRET = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
auth.set_access_token(ACCESS_TOKEN, ACCESS_SECRET)

def main():
api = tweepy.API(auth)

# DB接続
conn = MySQLdb.connect(
user='kumanabe_valu',
passwd='xxxxxxx',
host='localhost',
db='kumanabe_valu',
charset="utf8")
cur = conn.cursor()

# 時価総額の昇順
cur.execute("SELECT * FROM items ORDER BY total_value DESC LIMIT 10")
tweet_list = []
date = datetime.now().strftime('-----VALU時価総額ランキング(%Y/%m/%d)-----\n')
tweet_prev = date + "順位:名前(ID):時価総額\n"
func_tweet(cur,api,tweet_list,tweet_prev,5,"BTC") # row[5] = total_value

# 騰落率の昇順
cur.execute("SELECT * FROM items ORDER BY diff_rate DESC LIMIT 10")
tweet_list = []
date = datetime.now().strftime('-----VALU騰落率ランキング(%Y/%m/%d)-----\n')
tweet_prev = date + "順位:名前(ID):騰落率\n"
func_tweet(cur,api,tweet_list,tweet_prev,14,"%") # row[14] = diff_rate

# 新規公募案件における総額の昇順
cur.execute("SELECT * FROM items WHERE newid = '1' ORDER BY total_value DESC LIMIT 10")
tweet_list = []
date = datetime.now().strftime('-----VALU新規公募時価総額ランキング(%Y/%m/%d)-----\n')
tweet_prev = date + "順位:名前(ID):時価総額\n"
func_tweet(cur,api,tweet_list,tweet_prev,5,"BTC") # row[5] = total_value

def func_tweet(cur,api,tweet_list,tweet_prev,index,unit):
tweet = ""
rank = 1;
# 時価総額10位以内を順に取得
for row in cur.fetchall():
tweet = tweet_prev + str(rank)+"位:"+row[1]+"("+row[0]+"):"+str(row[index])+str(unit)+"\n"
#140文字以上になる場合は、140文字以内になるように分割してリストに追加
if len(tweet) > 130:
tweet_list.append(tweet_prev+"\n"+str(random.randint(1,100)))
tweet_prev = str(rank)+"位:"+row[1]+"("+row[0]+"):"+str(row[index])+str(unit)+"\n"
tweet = ""
else:
tweet_prev = tweet

rank=rank+1
# 残ったTweetをリストに追加
tweet_list.append(tweet_prev+"\n"+str(random.randint(1,100)))

while tweet_list:
# Twitter投稿
tweet = tweet_list.pop()
api.update_status(tweet)

if __name__ == "__main__":
main()

スポンサーリンク
posted by くまなべ at 21:00 | Comment(0) | TrackBack(0) | Python
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

この記事へのトラックバックURL
http://blog.sakura.ne.jp/tb/181297481
※ブログオーナーが承認したトラックバックのみ表示されます。

この記事へのトラックバック