Pythonを使ってヤフーファイナンス情報を取得、スクレイピング

#複数企業コード対応版 企業コードから企業名と株価を取得する
#Anaconda JupyterLabによる動作 Shift+Enterで実行になる

動作環境:MacOS 、Python、Anaconda JupyterLab

https://github.com/maseda1030/yahoofinance_python.git

面倒だった点は、複数タグが存在するタグ名tr,td,ddの扱い。複数の場合forで回して配列みたいにしてデータを取得する必要があるし、スペースが含まれるクラス名は別のプロパティ?で読み込む必要がある。

find_element_by_class_nameでスペースを含むクラス名はエラーとなるので、find_element_by_css_selectorとしてドット.で接続する。

誤り:el.find_element_by_class_name(‘stocksDtl clearFix’)

正:el.find_element_by_css_selector(“.stocksDtl.clearFix”)

以下はサンプルコード。使い方は、codelistに企業コードを入力すると、ヤフーファイナンスからスクレイピングして「コード、企業名、終値株価、前日比、日付」を半角スペース区切りで出力するだけ。ヤフーファイナンスに接続するときは、取得間隔を1,2秒程度空けている。連続で取得するとサーバ負荷が増えるから強制切断とかアクセスをブロックされてしまうかもしれない。

>>コード修正、Spanタグが存在しない時間帯があるみたいなので、存在するかどうかのチェックを追記した。2021年2月8日、12時06分

import time
from selenium import webdriver
import csv
import datetime
import os

codelist=[]
codelist=[6315,2207,6928,5724,2702]#企業コード、複数

def getName(code):
    # 仮想ブラウザ起動、URL先のサイトにアクセス
    driver = webdriver.Chrome('/usr/local/bin/chromedriver')#ここでエラーになったらパスが違うかchromedriverをインストールする
    #他のエラーで、「unexpectedly exited. Status code was:-9」だったら、Macの場合はシステム環境設定 → セキュリティとプライバシー で許可すればよい
    url='https://stocks.finance.yahoo.co.jp/stocks/detail/?code='
    driver.get(url+str(code)+'.T')
    #time.sleep(1)
    #title = driver.title
    #print(title)
    main = driver.find_element_by_id("main")
    el = main.find_element_by_id("stockinf")

    #エラー、nextClass = el.find_element_by_class_name('stocksDtl clearFix') Classにスペースが含まれるとエラーになるので別のメソッドにする
    nextClass = el.find_element_by_css_selector(".stocksDtl.clearFix")
    subNextClass = nextClass.find_element_by_class_name("forAddPortfolio")
    tableElem = subNextClass.find_element_by_class_name("stocksTable")
    dlElem = subNextClass.find_element_by_css_selector(".stocksInfo.clearFix")
    
    dds = dlElem.find_elements_by_tag_name("dd")
    trs = tableElem.find_element_by_tag_name("tr")
    symbol = trs.find_element_by_class_name("symbol")
    name = symbol.find_element_by_tag_name("h1")
    stockinfo = []
    stockinfo.append(str(code))
    stockinfo.append(name.text)
    tds = trs.find_elements_by_tag_name("td")
    
    for j in range(0,len(tds)):
        if tds[j].text != "":
            stockinfo.append(tds[j].text)

    ##ddタグの中に spanタグがあって、他に文字がある。spanタグのテキストだけが取得したかった。
    ##>>追記 2021年2月8日エラー処理を追加
    if len(dds[1].find_elements_by_tag_name("span"))>0:#存在チェック elementでなくてelementsにする
        stockinfo.append(dds[1].find_element_by_tag_name("span").text)#日付
    output=stockinfo
    driver.close()#起動したウィンドウを閉じる
    return output

###START
print("START#####################################")
tmpStr=""
f=0
for i in codelist:
    outputStr=getName(i)
    #コード、企業名、終値株価,前日比、日付
    for m in outputStr:
        if f==2:
            tmpStr+=m+'円'+' '
        else:
            tmpStr+=m+' '
        f+=1
        #print(outputStr[0],outputStr[1],outputStr[2]+"円",outputStr[3],outputStr[4])
    print(tmpStr)
    tmpStr=""
    f=0
    time.sleep(2)
print("End#####################################")

 

更に修正した。メソッドをクラスにした。また始値、高値、安値を入れた。2021/02/12

#Python を使って#ヤフーファイナンス 情報を取得、#スクレイピング 
#複数企業コード対応版 企業コードから企業名と株価を取得する
#Anaconda JupyterLabによる動作 Shift+Enterで実行になる
#修正、クラスに変更
#修正2021/02/12、始値、高値、安値を入れた

#手作業はcodelistにコードを入れる
import time
from selenium import webdriver
import csv
import datetime
import os

codelist=[]
codelist=[6134,7952,6462
         ]#企業コード、複数
#これからは、どんなに小さなプログラムでもクラスを使用する。そのほうが汎用性がある。
#同じメソッド名でもクラス名が異なればコピペして使用できる
class yahooFinanceClass():
        def __init__(self):
            self.hello="yahoo Finace"
        
        def getName(self,code):
            # 仮想ブラウザ起動、URL先のサイトにアクセス
            driver = webdriver.Chrome('/usr/local/bin/chromedriver')#ここでエラーになったらパスが違うかchromedriverをインストールする
            #他のエラーで、「unexpectedly exited. Status code was:-9」だったら、Macの場合はシステム環境設定 → セキュリティとプライバシー で許可すればよい
            url='https://stocks.finance.yahoo.co.jp/stocks/detail/?code='
            driver.get(url+str(code)+'.T')
            time.sleep(1)
            #title = driver.title
            #print(title)
            main = driver.find_element_by_id("main")
            el = main.find_element_by_id("stockinf")

            #エラー、nextClass = el.find_element_by_class_name('stocksDtl clearFix') Classにスペースが含まれるとエラーになるので別のメソッドにする
            nextClass = el.find_element_by_css_selector(".stocksDtl.clearFix")
            subNextClass = nextClass.find_element_by_class_name("forAddPortfolio")
            tableElem = subNextClass.find_element_by_class_name("stocksTable")
            dlElem = subNextClass.find_element_by_css_selector(".stocksInfo.clearFix")

            dds = dlElem.find_elements_by_tag_name("dd")
            trs = tableElem.find_element_by_tag_name("tr")
            symbol = trs.find_element_by_class_name("symbol")
            name = symbol.find_element_by_tag_name("h1")
            stockinfo = []
            stockinfo.append(str(code))
            stockinfo.append(name.text)
            tds = trs.find_elements_by_tag_name("td")

            for j in range(0,len(tds)):
                if tds[j].text != "":
                    stockinfo.append(tds[j].text)

            ##始値、高値、安値を取得
            detail = main.find_element_by_id("detail")
            innerDate = detail.find_element_by_class_name("innerDate")
            innerDate_divs = innerDate.find_elements_by_css_selector(".lineFi.clearfix")#S複数
            for n in range(0,len(innerDate_divs)):
                if len(innerDate_divs[n].find_elements_by_class_name("tseDtl"))>0:#存在確認は複数形
                    tseDtl = innerDate_divs[n].find_element_by_class_name("tseDtl")#こっちは単数
                    kubun=tseDtl.find_element_by_class_name("title").text
                    #共通の処理をまとめた
                    if kubun=="安値" or kubun=="高値" or kubun=="始値":
                        ymuiEditLink=tseDtl.find_element_by_css_selector(".ymuiEditLink.mar0")
                        kabuka=ymuiEditLink.find_element_by_tag_name("strong").text
                    #配列に追加    
                    if kubun=="安値":
                        stockinfo.append("安値:"+kabuka)
                    elif kubun=="高値":
                        stockinfo.append("高値:"+kabuka)
                    elif kubun=="始値":  
                        stockinfo.append("始値:"+kabuka)
                              
            ##ddタグの中に spanタグがあって、他に文字がある。spanタグのテキストだけが取得したかった。
            ##>>追記 2021年2月8日エラー処理を追加
            if len(dds[1].find_elements_by_tag_name("span"))>0:#存在チェック elementでなくてelements(複数S)にする
                stockinfo.append(dds[1].find_element_by_tag_name("span").text)#日付 こっちは単数のelement  
            output=stockinfo
            driver.close()#起動したウィンドウを閉じる
            return output
        
###START
print("START#####################################")
tmpStr=""
f=0
obj=yahooFinanceClass()
for i in codelist:
    outputStr=obj.getName(i)
    #コード、企業名、終値株価,前日比、日付
    for m in outputStr:
        if f==2:
            tmpStr+=m+'円'+' '
        else:
            tmpStr+=m+' '
        f+=1
        #print(outputStr[0],outputStr[1],outputStr[2]+"円",outputStr[3],outputStr[4])
    print(tmpStr)
    tmpStr=""
    f=0
    time.sleep(2)
print("End#####################################")    
'''出力例
START#####################################
6134 (株)FUJI 2,795円 前日比+51(+1.86%) 始値:2,794 高値:2,832 安値:2,777 14:08 
7952 (株)河合楽器製作所 3,510円 前日比+380(+12.14%) 始値:3,200 高値:3,540 安値:3,155 14:06 
6462 (株)リケン 2,400円 前日比+114(+4.99%) 始値:2,449 高値:2,449 安値:2,329 14:07 
End#####################################  
'''

 

ワンピース98巻感想ネタバレ、カイドウの子ヤマト 2021/02/04発売

2021/02/04

ワンピース98巻の感想、ネタバレを含みます。

赤鞘九人衆は裏切り者カン十郎を討ち取る

カイドウの娘であるヤマトがルフィと合流する。
彼女はおでんにあこがれて自分がおでんになるとルフィに話す

カイドウは将軍オロチを倒し、自分の娘ヤマトをワノ国の将軍に据える予定だった。
しかし、そこに赤鞘九人衆ほか、うどんの家来たちが押し寄せてきた。

カイドウと同盟を組んだビッグ・マム。
彼女はルフィに恨みを持っていて、ルフィを倒そうとする。

ルフィは
カイドウのもとに急ぐが、ビッグ・マムほか多くの敵が彼の行く手を阻む。

元海軍将校の恐竜ドレークは、スパイがバレてしまいルフィの味方をすることになる。ドレークは海軍の元軍人だった。

太っちょクイーンはウイルスを悪用した「氷鬼こおりおに」と呼ぶ兵器を使用し、鬼ゾンビを作り出す。
この氷鬼と呼ぶ銃で打たれると鬼になってしまう。そしてこの鬼になった者から触られると鬼なってしまう感染を利用した武器だった。
この症状を治すにはワクチンが必要だった。
クィーンはワクチンをアブーに渡して、誰がワクチンを奪えるかというゲームを楽しむのだった
敵味方まじってワクチンを奪うためにアブーを追いかけるゲームが始まる

結末

追い詰められたヤマト、モモの助、くノ一のしのぶたち。
ヤマトは敵に追い詰められてもモモの助たちを助けるのだった。
次回99巻へ

 

良かったシーンビッグ・マムと対峙するナミとフランキー

ナミ:だめよフランキー逃げよう「四皇よ!?」

フランキー:「四皇」だから逃げる?おいナミ お前あんのか・・・!? うちの船長”海賊王”にする気はよォ!!!

Blender2.91、GreasePencilストロークStroke設定、滑らかメモ

GreasePencilを使用して曲線を書くとすごくギザギザになってしまうので滑らかにするように設定するときのメモ

これはあくまでも例

GreasePencilストロークStroke設定 >> 設定したらFileメニューのDefaultのSave startup Fileで保存しておく

右上に表示されるStrokeをクリックする

Smooth:1.7 上げるほど曲線が直線の方向に簡略化される。

Iteration:3 曲線の計算(?)上げるほど再設定される。簡素化したいなら上げる

Subdivision:1 (よくわからない)

Simplify:0→0.01 上げるほどポイントが簡略化される。Line化、ポリゴン化するときにポイントを減らしたいときに増やすとよい。増やしすぎるとカクカク線になる。

 

Simplifyを増やすと、ポイントの数が減り少しカクカク線になって、密度が減った状態になる。

pythonでCSVを読み込みクラスオブジェクトを配列に入れる

CSVを読み込みクラスオブジェクトを配列に入れる

CSV例,6列必要。1行目は除外する。米ヤフーファイナンスから日経平均株価データを取得すると、今日の分は取得できるのに、なぜか前日のデータが時間帯によって取得できない。そのため、取得できたデータと取得できないデータをCSVにしておいて使用する。

日付,高値,安値,始値,終値,調整終値

となっている。

index      High      Low    Open   Close    AdjClose  列数チェックが必要                                                               
2021-01-07,27624.730469,27340.460938,27340.460938,27490.130859,27490.130859
2021-01-08,28139.029297,27667.750000,27720.140625,28139.029297,28139.029297
2021-01-12,28287.369141,27899.449219,28004.369141,28164.339844,28164.339844
2021-01-13,28140.10,28503.43,28139.66,28456.59,28456.59
2021-01-14,28979.529297,28411.580078,28442.730469,28698.259766,28698.259766

クラスから実際のデータを取得するときは

df[0].indexとかdf[0].closeみたいにして取得する。つまり1行分が1オブジェクトの配列として登録されている。

#CSVにするときは、列数があっているかをチェックする。ヤフーファイナンスはAdjCloseがあるが、ほかはCloseしかない。
import os
import pandas_datareader.data as data
from datetime import datetime,timedelta

#日経平均株価データのクラスオブジェクト、カプセルデータ
class nikkeiClass:
    def __init__(self, cindex, chigh,clow,copen,cclose,cadjclose):
        self.index = cindex
        self.high = chigh
        self.low = clow
        self.open = copen
        self.close = cclose
        self.adjclose = cadjclose
#########################
end = datetime.now()
#start = datetime(end.year, end.month, end.day-8)#こっちだと月マタギでエラーになる。
start = end - timedelta(days=7)
print('start:',start.strftime("%Y/%m/%d"))
print('today:',end.strftime("%Y/%m/%d"))

df=[]
checkLineCount=0
#CSVを読み込む
ifile='/Users/toshiromaseda/Documents/blender/script/2021年1月14日日経.csv'
try:#ファイルが存在しないときのエラー処理try
    with open(ifile,'tr') as fin:
        for iline in fin:
            #try:
                tmp=iline.strip().split(",")#stripしてからsplit()だと理解している。
                if 'High' in tmp[0]:
                    continue
                if len(tmp) < 6:#全部で6列存在する。
                    print('ERROR 列の数があっていません。少ないようです。行数:',checkLineCount)
                    break
                #here
                df.append(nikkeiClass(tmp[0],tmp[1],tmp[2],tmp[3],tmp[4],tmp[5]))
                checkLineCount+=1
except FileNotFoundError as e: # FileNotFoundErrorは例外クラス名
    print("ファイルが見つかりません。パス、ファイル名を確認してください", e)
    ifile.close()
    sys._exit()#ファイルがなければ終了

print(df[0].index) 
for f in df:
    print('日付:',f.index)


print('END')

 

Blender2.91 外部ファイルからオブジェクトの読み込みAppend

外部ファイルからオブジェクトの読み込みAppend処理

Blendファイルのオブジェクトを読み込む。他のスクリプトで、オブジェクトを全部削除してから処理をするため、別ファイルのオブジェクトを読み込めると作業が減って便利。

file_path:フルパスでBlendファイルを指定する

inner_path:タイプ、ここではオブジェクトobjectを指定した。マテリアルの場合はmaterial等になる。

object_name:オブジェクトの名称、できるだけ作成時に重複しない名称がよい。

以下のスクリプトは読み込んだオブジェクトのスケールを縮小した。

import bpy
import os
 
 
# 既存要素削除
for item in bpy.data.meshes:
    bpy.data.meshes.remove(item)
 
        
file_path = '/Users/toshiromaseda/Documents/blender/model/テスト、研究モデル/Neko/Neko_simple_001.blend'
inner_path = 'Object'
object_name = 'Neko'
 
bpy.ops.wm.append(
    filepath=os.path.join(file_path, inner_path, object_name),
    directory=os.path.join(file_path, inner_path),
    filename=object_name
    )

bpy.context.scene.cursor.location=(0.0,0.0,0.0)
# 指定オブジェクトを取得する
#arg_objectname='Neko'
selectob = bpy.data.objects[object_name]
selectob.select_set(True) #2.9
bpy.ops.transform.resize(value=(0.37,0.37,0.37)) # 図形を変形

 

Blender2.91、シンプルなIKボーンarmature設定メモ

IKボーンはいろいろなところで使えそうだがアニメーションを作らないときは全く使用しないのですぐ忘れる。なのでまたメモ。何度もメモする。

参考

https://www.youtube.com/watch?v=cGvalWG8HBU&t=3s

英語の綴りは自信がない。

ボーンを使用しないIKでもいいような気がしてきたが、参考ユーチューブはほとんどばボーンとIKの組み合わせばかり。

ボーンを使用しないIKは見つからん。ボーンを作りEで子ボーンを作成。離れた位置はShift+aで追加ボーン。Ctr+pでkeep Off setは親が最後

注意点:IKの設定、Shift+ctr+cは「Pose mode」でないと設定できない。Blender2.91ではレイヤーは廃止されて、コレクションになったらしい。ただ、ボーンのレイヤーは存在する。

ボーンの配置

全体

①waist(ボーンの先端を選択して、Eキーで子ボーンを作成できる)
②chest
③head

(鏡面コピーが必要)④〜⑬
④upper_arm
⑤Lower_arm
⑥Hand
⑦IK:腕を動かすためのボーン

⑧Upper_Leg
⑨Lower_Leg
⑩Foot
⑪IK:脚を動かすためのボーン

⑫Elbowひじ :⑤Lower_armの調整用
⑬Kneeひざ :⑨Lower_Legの調整用

⑭Base :全体をまとめるためのボーンで、このボーンを動かすと全ボーンが動く

手順

armatureを追加して、①〜⑭までのボーンを作成

⑦⑪のボーンは、eを使って作成した場合、親子関係になっているので、EditモードになってAlt+pで解除する。

⑦⑪⑫⑬⑭のDeformのチェックを外す

⑥⑦の順で選択して、Ctr+p  Keep Off Set

⑩⑪の順で選択して、Ctr+p  Keep Off Set

>>>Pose modeに切り替える

Poseモードになって⑦IK、⑤Lower_armの順で選択して、Shift+ctr+c でInverse Kinematics(IK)を選択(選択順がCtr+pのときと逆になる)

⑪IK、⑨Lower_Legの順で選択して、Shift+ctr+c でIKを選択(選択順がCtr+pのときと逆になる)

⑤Lower_arm、⑨Lower_Legのボーンコンストレイントから、設定をする

設定:例、肘の設定

Pole target:armature

Bone:⑤ならElbow、⑨ならKneeを選択する

Chain Length:2(制御したいボーンの数、人間とかなら1か2個、蛇とか長いものは多くなるはず)

腕足が変な回転をしていたら、Pole Angleを回転させて調整する。

鏡面コピーが必要 ④〜⑬ Shift+cで3D カーソルを原点に移動させる。

Shift+dでコピーしてからs,x,-1で反転させる。

原点の反対向き、鏡面コピーができないときは、x軸に対して移動させる。

④⑧(コピー分も)を選び①を最後に選びCtr+pでKeep Off Set

⑦⑪⑫⑬(コピー分)を選び、⑭を最後に選びCtr+p Keep Off Set

最後にオブジェクトモードにて、オブジェクトとボーンを選択してCtr+p with automatic weightを選択して適用する。

ボーンごとに、ウェイトマップを調整する。WeightPaintモードからオブジェクトデータプロパティにてVertex Groupから該当のボーンを選んで、塗るか消す。赤いほどボーンの影響を受けて、青は影響を受けない。たまにとんでもないところに、色が塗られているからオブジェクトの裏側、下側もチェックする。赤、黄、緑、青

Pose modeで隠したいボーンを選び、mキーを押して2段めの左端を選択するとボーンをレイヤー移動して、隠せる。

 

Blender 2.91 基本、忘れやすいショートカットキーや便利な機能メニュー メモ

細分化 w :Subdivideさぶでばいど Subdivisionサブディビジョン
頂点結合 m
面貼り f(もしかしたらアドオンが必要かも)
押出 e
移動 g
回転 r
スケール s
隠す h
分離 p :1オブジェクトを2つのオブジェクトに分ける。原点がそれぞれに付く。面の分割ではない。
分割 ctr+r:面等を分割する。分離ではない。
複数オブジェクトを1つのオブジェクトに結合 Ctr+j : (単に1オブジェクトにしたいときに使う)

グループ Ctr+g:複数オブジェクトをまとめる。1オブジェクトにするわけではない。ドープシートでは、Shift+gで選択してiキーでキーフレームを挿入
法線反転 Mesh>Normal>Flip:レンダリングしたときに、一部の面で色合いが違うときは法線の向きが他と異なっている。それかポイントが複数存在する。
3Dカーソルを原点 Shift+c

Blender animationNode Textが上から落ちてくる設定メモ

Blender2.91で animationNodeを使用して Textが上から1文字ずつ落ちてくる設定メモ

animationNodeは無料のアドオン。ダウンロードして追加する。

animationNode利点

animationNodeを組み合わせることで、細かな動きを何度も設定できる。テキストの文字ごとに動きを設定できるので、製作の手間が省ける。キーフレームにキーを打つことなく、複雑な動きを設定できる。

難点:

アニメーションノードの設定が難解である。どれがどれで、どれとつなぐと結果がどうなるのかさっぱりわからない。

NOあり

アニメーションノードの説明

(ノードの場所がわからないときはノードの検索窓でキーワードを検索する。似たような名称のノードが多すぎて、目当てのノードかわからないときがある。)
①番は、名称を入力。ここではfly in. 設定前に+アイコンをクリックしてobject Listを選択する

②番は、「Initial Transforms」を検索する。検索すると②のノードに変わって接続する端子がでてくる。

③番は、テキストを選択して、Updateをクリックするとテキストが1文字ずつ分割される。よくわからないが、一度Updateを設定すると、テキスト文字の修正ができない。

④番は、fly inを選択。テキストが複数あるときは、③と④をセットにして複製して、別のテキスト選択すると同じようなアニメが作成される。

⑤番は、Z軸に数値をプラス設定をすると上からテキストが落ちてくる。下の場合はマイナス値を入れる。手前や奥から動かしたいときは、Y軸に値を入れる。

7−9番を設定することで、1文字ずつの動きに時差を設定できる。

⑧、⑧−2番のDelayは、次の文字が動き出すフレームになる。これを0にすると同時に動く

⑩番は、回転を追加する。回転が不要なときは、⑧-2、⑨、⑩、⑪番のノードは不要

⑪番は、Rotation>Random Eulerノードで、scaleを大きく設定すると回転する。scaleが表示されていないときは、ノード内のリストのように見える小さなアイコンをクリックする。EulerとRotateで接続できる

※ラインがノードに重なるときは、シフトキーを押しながら右クリックすると接続点がラインに追加される

今日のエラー、Mysql、Can’t connect to local MySQL server

A・症状2021/01/06

iMacを再起動した後で、Anaconda NavigatorからPythonを実行した
するとエラーが発生。PythonでMysqlを使用しているエラーのようだ。
エラーの一部:Can’t connect to local MySQL server through
Mysqlサーバに接続できない

・対応

Mysqlを使用しているPythonでエラーが発生したとき、Can’t connect to local MySQL server through
たぶんMysqlが起動していないから(PCを起動した直後や再起動するとMysqlは停止しているからMysqlを起動する)
(1)mysqlサーバを起動する。ターミナルから実行する
$ mysql.server start
このrootはいつもの(秘密)パスワードで、CSV取り込みは、Test1030のパスワード

(2)Mysqlに接続する(以下は個別のアカウントmasedaを使用した場合。rootの場合はmysql -u root -p)
$ mysql -u maseda -p
Test1030(これは知られてもよい)のパスワード

(3)通常のMysqlコマンドやPythonから実行する
実行できた。

 

B SyntaxError

・症状

pythonを実行したらエラーメッセージが表示された

・エラーメッセージ

SyntaxError: invalid syntax

対応

指定された行の上の行の閉じカッコの数があっていなかった。
1個少なかったために、次の行でエラーが表示されていた。
test((a,a),(1,1,1)   <<<閉じカッコが足りない
testB()<<<ここでエラーが表示


test((a,a),(1,1,1))

今日のエラー、Blender2.91、BEERのMALTアドオンでSHADER LINKER ERRORエラー

エラー内容

症状:

初めての新しいシェーダアドオンをインストールした。BEERのMALTアドオンをインストールしてマテリアル設定で、MALTシェーダーを選択するとエラーになる。簡易レンダリング、最終レンダリングできない。できないというかMaltアドオンが機能していない。

対応:

なし

現在の状況

改善できない。放置。shader情報がネットで見つかったら、試す予定。

環境:MacOS BigSur,Blender2.91

--以下、MaltERRROR
NPR_Pipeline : PRE_PASS : SHADER LINKER ERROR :
WARNING: Output of vertex shader 'MODEL' not read by fragment shader
ERROR: Implementation limit of 16 active fragment shader samplers (e.g., maximum number of supported image units) exceeded, fragment shader uses 21 samplers
NPR_Pipeline : MAIN_PASS : SHADER LINKER ERROR :
WARNING: Output of vertex shader 'MODEL' not read by fragment shader
ERROR: Implementation limit of 16 active fragment shader samplers (e.g., maximum number of supported image units) exceeded, fragment shader uses 22 samplers
NPR_Pipeline : SHADOW_PASS : SHADER LINKER ERROR :
WARNING: Output of vertex shader 'MODEL' not read by fragment shader
ERROR: Implementation limit of 16 active fragment shader samplers (e.g., maximum number of supported image units) exceeded, fragment shader uses 19 samplers

NPR_Pipeline : PRE_PASS : SHADER LINKER ERROR :
WARNING: Output of vertex shader 'MODEL' not read by fragment shader
ERROR: Implementation limit of 16 active fragment shader samplers (e.g., maximum number of supported image units) exceeded, fragment shader uses 21 samplers
NPR_Pipeline : MAIN_PASS : SHADER LINKER ERROR :
WARNING: Output of vertex shader 'MODEL' not read by fragment shader
ERROR: Implementation limit of 16 active fragment shader samplers (e.g., maximum number of supported image units) exceeded, fragment shader uses 22 samplers
NPR_Pipeline : SHADOW_PASS : SHADER LINKER ERROR :
WARNING: Output of vertex shader 'MODEL' not read by fragment shader
ERROR: Implementation limit of 16 active fragment shader samplers (e.g., maximum number of supported image units) exceeded, fragment shader uses 19 samplers