プログラミンGOO

プログラミングナレッジ、ワードプレス、広告収入等について、気づき・備忘録を残していきます。

python&seleniumスクレイピング備忘録

【Selenium基本操作】

■基本形

>|??|driver.find_element_by_属性('要素名').操作|

■webdriverの指定
driver = webdriver.Chrome()#chromeをドライバに指定
driver = webdriver.Firefox()#Firefoxをドライバに指定
■基本メソッド
.get('URL')   #指定したURLに遷移する
.click()      #要素をクリック
.text         #テキストを取得
.send_keys('TEXT')        #'TEXT'を入力
.clear()      #テキストを削除
.get_attribute('VALUE')   #'VALUE'の属性を取得
.close()      #ウィンドウを閉じる
.back()       #一つ前の画面に戻る
.switch_to_frame('FRAME') #'FRAME'に移動
.switch_to.default_content()      #元のフレームに戻る
.switch_to_window(window_handle)  #ウィンドウを切り替える

【ドライバー設定】

■Opthins
from selenium import webdriver
from selenium.webdriver.chrome.options import Options

options = Options()
#このoptionsにいろいろ追加して設定する

driver = webdriver.Chrome(ChromeDriverManager().install(), options=options) #引数にoptionsを与える

・便利なオプション
#スクロールバーを非表示にする
options.add_argument('--hide-scrollbars')
#シークレットモードで起動する
options.add_argument('--incognito')
#ブラウザを表示しない
options.add_argument('--headless')

■ChromeDriverの最新版を常に使用する
ChromeDriverはブラウザがアップデートされるたびに最新化しなくてはならない。
ChromeDriverManagerを使用すると常に最新版のドライバを構成してくれる。
ただ、サードパーティーのモジュールであるため想定外のエラーが起こることもある。
SSLエラーに案るのもその一つ。この場合は、osモジュールで環境変数を操作し、SSLチェックを外す。

・実装
from selenimu import webdriver
grom webdriver_manager.chrome import ChromeDriverManager
import os

options = webdriver.ChromeOptions()
options.add_experimental_option('excludeSwitches', ['enable-logging']
options.use_chromium = True

os.environ['WDM_SSL_VERIFY']='0'
driver = webdriver.Chrome(ChromeDriverManager().install())

【要素の取得】

■基本形
driver.find_element_by_xpath('XPATH')

※要素はWebElementオブジェクトとして取得される
※複数の場合は'find_elements'とする
※以下は同義

1.driver.find_element_by_id('ID')
2.driver.find_element(By.ID, 'ID')

■指定の型

by_class_name('CLASS') #classでの指定
by_id('ID') #idでの指定
by_xpath('XPATH') #xpathでの指定
by_tag_name('h1') #'h1'タグでの指定

※構文を続けて複数回実行することも可能
例)driver.find_element_by_id('ID').find_elementby_class_name('CLASS')

【その他のよく使うメソッド】

■iframe内に移動
ifm = driver.find_element_by_xpath('XPATH') #iframeのパスを指定
driver.switch_to_frame(ifm)

・もとのiframeに戻る

driver.switch_to.default_content()

※iframeの切り替え前後は読み込み遅延によるエラーが出やすい。
WebDriverWait()を使用して回避すること。

■ウィンドウを切り替える
win  bu.window_handles
driver.switch_to.window(win[●]) #●番目のウィンドウに移動する

・ウィンドウハンドルの番号の取得

l = driver.window_handles
print(0)
print(1)
print(driver.current_window_handle) #現在のウィンドウハンドル
■指定の要素までスクロール(マウスオーバー)
elm = driver.find_element_by_id('ID')
actions = ActionChains(driver)
actions.move_to_element(elm).perform()
■ドロップダウン(選択リスト)を選択したいとき
from selenium.webdriver.support.ui import Select
elm = driver.finde_element_by_xpath('XPATH')
elm_select = Select(elm)
elm_select.select_by_value('VALUE')
■アラートを承認
Alert(driver).accept()  #アラートを承認
■テーブルの文字列を取得
tableelm = driver.find_element_by_xpath('XPATH')
tr_li = tableelm.find_element_by_tag_name('tr') #tr(各行)の要素をtrsにリストで格納
for i in range(1,len(tr_li)):
  td_li = tr_li[i].find_element_by_tag_name('td') #tr内のtd要素をリストで格納
  box = []
  for j in range(0,len(td_li)):
    box.append(td_li[j].text + '\t')
for k in box:
  print(p)
・boxへの追加の記法appendix
box += '%s\t' % (td_li[j].text)
■入力を求める

txt = input('入力してください')
print(txt) #入力された文字が出力される

【Xpathについて】

■基本形
driver.find_element_by_xpath('//*[@id="srchbtn"]')
■パスについて
//  #子ノード全体から探す
*   #該当するすべての要素を探す
/parent::td            #1つ上のtdまで含めた範囲を指定
/following_sibling::td #同階層のtdすべてを指定
//li[3]               #3番目の要素を取得 ※//li[position()=3]と同義
//li[position()>1]    #x番目以外の要素を取得
//p[string-length(normalize_space())]>400 #400文字以上の要素を取得
■特定のテキストを含む
//a[contains(text(),'TEXT')]
※完全一致の場合
//a[text() = 'TEXT']
■特定の文字から始まる

・基本形

//a[starts-with(@id, 'TEXT')]

・応用

//a[starts-with(@href, 'http://') or starts-with(@hrefs, 'https
://')]

【待機】

■準備

from selenium.webdriver.support import expected_condition as EC

time.sleep(1) #1秒待機

・要素が出現するまで待機

wt = WebDriverWait(br,10)	//何秒でタイムアウトするか
wt.until(ec.visibility_of_element_located((By.XPATH,●path●)))  #()を2重にしないとエラーが出る
wt.until(ec.visibility_of_element_located((By.CLASS_NAME, 'classname')))

・Enterが押されるまで待機
>|??|input()|

■要素が見つからなければ例外処理

try:
element = driver.find_element_by_tag_name('article')
except NoSuchElementException:
print('探した要素は存在しない')
finally:
driver.save_screenshot('result.png')
driver.quit()

■selectの解除
deselect
■疑似要素のクリック
actions.move_to_element_with_offset(要素,x,y)
actions.click()
actions.perform()
■終了

driver.quite() #終了してブラウザを閉じる
driver.close() #現在のページを閉じる


xpathのparentとかの前はスラッシュ
select_by_visible_text()
selenium for pythonでタブの切り替えをする

【appendics】

■スクリーンショット
FileName = os.getcwd() + '\\screen.png' #ファイル名
driver.save_screenshot(FileName) #スクリーンショットを保存

■フルスクリーンショット
imoprt base64

viewport = {
'x' : 0,
'y' : 0,
'width' : width,
'height' : height,
'scale' : 1
}

image_base64 = driver.execute_cdp_cmd(
'Page.captureScreenshot', {
'clip' : viewport,
'captureBeyondViewport' : True
}
)

image = base64.b64decode(image_base64['data'])
FileName = os.getcwd() + '\\screen.png'

with open(FileName, 'wb') as f:
f.write(image)

・参考
Python + Selenium で Chrome の自動操作を一通り - Qiita

■印刷を使用する
アナリティクス系の画面は、グラフの描画などレンダリングのタイミングが複雑でシンプルにスクリーンショットを取ることができない。
印刷の使用を検討する。
印刷設定は以下のファイルに書かれており、これをドライバ起動中に上書きした状態で使用する
C:\Users\your_name\AppData\Local\Google\Chrome\User Data\Default\Preferences

・参考
Python - WebページをPDFで自動保存する ~Selenium pdf Print Option~ HTML to PDF - デジタル推進課
PythonのSeleniumでhtmlをpdfにするときの設定メモ | 彗星


#印刷設定
appState = {
'recentDestinations' : {
{
'id' : 'Save as PDF',
'origin' : 'local',
'account' : 2
}
}

prefs = {
'printing.print_preview_sticky_setting.appState' : json.dumps(appState),
'savefile.default_derectory' : os.getcwd()
}

options.addexperimental_option('prefs', prefs)
options.add_argument('--kiosk-printing') #印刷ボタンを自動でEnterする

■ログイン情報の保存
ブラウザは起動時の設定プロファイルを持っているて、ログイン情報や、ブラウザ起動時の拡大倍率などもここに保存されている。
seleniumで起動される場合はこれとは別の一時的なプロファイルが使用されるため、起動時は毎回ログインが求められる。
このプロファイルをdriver用に作ってあげ、毎回こちらを参照するようにすることで、PCを再起動してもログインは不要になる。
※普段使われているプロファイルを参照する方法もあるが、Seleniumで起動する場合、同じプロファイルを複数起動することを禁止しており、すべてのブラウザを閉じなければならない

どのプロファイルで実行されているかはURLに以下を打ち込むとわかる
chrome://version/

・実装
profile_path = os.getcwd() + '\\profile' #profileというフォルダを作成しここにプロファイル情報を保存する
options.add_argument('--user-data-dir=' + profile_path) #先ほどのprofileフォルダを参照する
options.add_argument('--profile-directory=Default') #profileフォルダ内のDefaultフォルダを指定する
driver = webdriver.Chrome(ChromeDriverManager().install(), options=options)


■拡張機能の使用
ドライバーは新規プロファイルでブラウザを起動するため普段使用している拡張機能が無い。
常時型の拡張機能であれば、拡張機能が保存されたフォルダパスを指定することで、ドライバー起動時にあわせて導入することができる。
しかし、何かをキーにして動く起動型の拡張機能は、レンダリングされたページではなくブラウザのツールバーに組み込まれるためSeleniumで操作ができない。
そのため、拡張機能にショートカットキーを設定 >pyautoguiでキーボードをアナログに押した動作を再現 して起動する。
キーを送るにはsend_keyを使用するのが一般的だが、これはwebページに対してキーを送ることになり、拡張機能の起動には使用できなかった。
例としてGoFullPageという、スクロールしてフルスクリーンショットを取る拡張機能を使用する。

・実装
拡張機能のパスを調べる
だいたい以下
C:\\Users\\username\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Extensions
このフォルダに拡張機能のIDで格納されている。拡張機能のIDは以下の手順で確認できる。
ブラウザ右上の設定>その他のツール>拡張機能>デベロッパーモードをonにする

#driver起動時のoptionsに含める
extension_path='先ほど調べたパス'
options.add_argument('f'load-extension={extension_path}')

これで拡張機能を含めた形でdriverが起動される

【エラー】
■システムに接続されたデバイスが機能していません
>特に動作に影響はない
ログを消したいならwebdriverにオプションを追加する
・参考
https://miya-mitsu.com/python-0x1ferror/

■クリックされない
.click() #最後の()忘れていませんか?

死かk

AWSのMFA設定、QRコードはどこ?複数デバイスで使用する方法

AWSの認証、MFAに使用するスマホアプリ、Authenticator。

ディスプレイ_Authenticator

今回携帯電話を機種変更したのですが、このAuthenticatorの再設定のやり方を解説したドキュメントが見つからず少し苦戦しました。

AWSの管理画面からはQRコードは表示できない

Authenticatorの設定には、QRコードを読み込むか、セットアップキーを入力するしかないのですが、このQRコードが見つからない。

AWSの管理画面をいくら探しても無い。

まだ設定していない状態であれば、以下の手順で設定できるようですね。
右上の自身のアカウントをクリック>マイセキュリティ資格情報>多要素認証(MFA)>仮想MFAデバイス

なのですが、僕のように機種変更で認証アプリを別のデバイスで設定したい場合。
この最後の行程である『仮想MFAデバイス』が無いのです。

QRコードは過去に使用していたアプリから引き継ぐ

じゃあどうするかというと、QRコード過去に使用していたスマートフォンのアプリ画面で表示できます。

昔、認証に使用していたスマホを開いて、アプリを起動します。

そして、右上の三点リーダをクリックし、『アカウントのエクスポート』を選択。

Authenticator

ここからQRコードを設定することができます。

こんな単純なことですがかなり苦戦しました。

認証のアプリケーションはAmazonのものではないから仕方ないとはいえ、AWSの管理画面から表示できるようにならないもんかな…

要件定義の書き方、書くべき内容

背景
アプリケーション作成に至った背景、現状、課題を示す。

概要

システムの概要を記載する。

機能

アプリケーションの主たる機能を記載する。

ユースケース

┗アプリケーション提供者、ユーザなど、ステークホルダーの関係を図解する。
ユースケース図の中で記載した各アクションについて解説する。以下を明確にすること

  • 実行者(アクター)
  • 事前条件
  • メインフロー(アクターの行動内容とシステムの挙動を明記する)
  • 例外フロー(メインフローから外す正常でないアクターの動作と、その際のシステムの挙動を明記する)
  • 事後条件(正常に処理が完了した場合の結果となるアクターの行動、またはシステムの挙動を明記する)

外部設計書の書き方、書くべき内容

目的

本仕様書の作成目的を記載する

仕様ソフトウェア ※各ツールについてはバージョンも記載する

┗開発言語(JavaRubyjavascriptなど)
┗アプリケーションフレームワーク(Spring、Node.js、Vue.js、Vuex、Seleniumなど)

画面仕様

┗画面構成
┗画面遷移図
┗各画面

DB定義
┗DB構成
┗各テーブル定義

javascript備忘録

【基本事項】
■バージョン確認
自動でアップデートされていくので知っても意味がない。
確認方法も煩雑。
というか、ESの実装がjsであり、バージョンは無い?

・scriptで宣言した後

use strictを書いておくとエラーをコンソールに出力してくれる

基本的にファイルのコードはすべて{}でくくってあげる。

するとスコープがわかれるため、変数の干渉を防ぐことができる

・文字列にシングルクォーテーションを用いるか、ダブルクォーテーションを用いるかはどちらでも構わない。typescriptのESlintがシングルを強要することからシングルのほうが無難か。

'use strict';
{
//この中にコードを書く
}

■定義

const t = 'test';

ハイフンはNG、0始まりはNG

大文字、小文字は区別される

予約語は使えない(constなど)

・属性宣言(定数・変数定義)で関数を使う場合に

その関数は後から書いても良い

【フォームの処理】form

■参考

http://www.kogures.com/hitoshi/javascript/get-form/getform1.html

■inputの値が変更されるたびに発火 :oninput

<input type="text" id="input"> 入力された文字列: <span id="result"></span>

<script>

  input.oninput = function() {

    result.innerHTML = input.value;

  };

</script>


■イベントハンドラとイベントリスナーどっちを使用する?

参考:https://ichi.pro/js-no-ibentohandora-to-addeventlistener-no-chigai-wa-nani-desu-ka-165150920301620

>一般的にはイベントリスナー。イベントハンドラは複数あった場合に処理を上書きしてしまう。

<button onclick=”funk()”> //イベントハンドラ

addEventLister //イベントリスナ

■formタグって必要なの?

参考:https://marsquai.com/745ca65e-e38b-4a8e-8d59-55421be50f7e/f83dca4c-79db-4adf-b007-697c863b82a5/d3ffe8bf-0f83-4191-a660-f7e1388dfcda/

以下の違いがある

タグのデフォルトのバリデーションがされる
デフォルトでEnterキーによる送信処理が実装される
デフォルトの送信は非同期処理でされない

・js側でformタグを使うとnameをチェーンして取得できる

~index.html~

<form name=”myform”>

<input type=”text” name=”sample”>

<button type=”submit”>送信</button>

</form>


~main.js~

const form = document.forms.myform;

//formの送信をcatchする場合

form.addEventListener('submit', function() {

  event.preventDefault();

  console.log(form.sample.value);

});

//inputの変更をcatchする場合

form.sample.addEventListener('change', function() {

  event.preventDefault();

  console.log('状態が変化しました!');

});


【文字列の操作】

■エスケープ

前に\をつけるとエスケープ




■連結

  1. でつなぐ


■utlil

・文字数

t.length

・部分抽出

t.substring(start, end);

例)console.log(1,2); //'es'

・分割

t.split(str)  //strで分割し配列にする

一文字ずつに処理を行いたい場合はsplit(‘’)で一文字ずつの配列にする

function uuid() {

  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'

    .split('') //1文字ずつの配列に分割

    .map(c => { //1文字ずつに処理を行う

      switch (c) {

        case 'x': return (Math.random() + 16 | 0).toString(16);
        case 'y': return ((Math.random() + 4 | 0) + 8).toString(16);
        default: return c;
      }
    })

    .join('');
}

■テンプレートリテラル(文字列の埋め込み)

const msg = 'world';

console.log(`hello ${msg}`); //これはjQueryか?

■指定文字列の検索

・参考

https://qiita.com/kazu56/items/557740f398e82fc881df




・matchで検索

str.match('word')  //配列 or null

・indexOf

str.indexOf('word')  //位置のインデックス or -1

■文字列の置き換え

text.replace(/aaa/g, 'ccc'); //通常は初めの1回のみだが、gを指定すると複数回置き換える

str.indexOf(文字列) //あればインデックス、無ければ-1が帰る

■文字をクリップボードにコピー

input要素、textarea要素しかコピーできない。

要素をまるまる取得して、値でなく要素自体を取得し、その要素に対し入力されている文字列を選択する。

input要素、textarea要素しか取得できないのは、クリップボードはHTML上でしか操作できないためと思われる。




・方法

~index.html~

<textarea id="output"></textarea>

~main.js~

const output = document.getElementById('output');
output.addEventListener('click', function() {

  output.select(); //要素のテキストを画面上で選択
  document.execCommand('Copy'); //クリップボードにコピー

  alert('コピーしました');
})

■正規表現

let regex = startStr + ‘.*?’ + endStr: //正規表現を用意

・リプレイス

result = string.replace(new RegExp(regex, ‘g’), ‘’);
//regexを空白に置き換える
//初期値は最初に見つかった文字列のみを対象とするが
//'g'を指定することですべての文字列が対象となる

・存在を調べる

string.match(regex); //一致すればstring、一致しなければnullが返る

【数値の操作】

■少数

.toFixed(2) //小数点2桁まで表示


■ランダムな数値

Math.floor(Math.random() * 6 + 1); //6~1のランダムな数値を表示する
//random :0~0.9999のランダムな数値
//floor :小数点切り捨て

//フィッシャーイェーツのシャッフル
function shuffle(arr) {

  for (let i = arr.length - 1; i >= 0; i--) { //arr.length - 1; 配列の最後のインデックスを表現

    const j = Math.floor(Math.random() * (i + 1));  //ランダムに選ぶ要素のインデックスを表現

    [arr[j], arr[i]] = [arr[i], arr[j]];  //分割代入で要素を入れ替え
  }

  return arr;
}

【データ型】
■型判定
typeof(val)

データ型の変換 parse●●

console.log('5' + 3);

console.log(parseInt('5', 10)); //8
数値に変換できなかったら『NaN』が出力される

・基本データ型 :文字列、数値、真偽値など

・Object型 :配列、オブジェクト ※参照型




■参照型のオブジェクトを参照でなくコピーにするには

const shuffledChoices = shuffle(quizSet[currentNum].c);
//quizSet[currentNum].cは参照型

・[]でくくってスプレッド演算子で配列を参照する

shuffle([...quizSet[currentNum].c]);

【関数】

function fnc(msg = '初期値') {

  return msg;
}

console.log(fnc('test'));

※関数内でreturnするとそのあとの処理は実行されない




・関数式で定数や変数に直接代入もできる

※最後にセミコロンにつく点に注意!

const keisan = function(msg = '初期値') {

  return msg;
};

keisan('test'); //定数として扱われ、関数名がないので無名関数と呼ばれる

・アロー関数 関数式の書き換え

const keisan = (msg) => {    //msgは引数

  return msg;
};

※returnするだけの場合は

const keisan = (msg) => msg;

でOK

※さらに引数が1つだけなら()も不要

const 定数名 = 引数 => 処理;

【if文】

■基本形

if (arg == val) { }

★『=』だと代入が行われてしまうため注意!
■省略形

const score = 40;
score >= 80 ? console.log('OK') : console.log('NG');

&& :AND
|| :OR
! :NOT

【for文】

for (let i = 1; i <= 10; i++) { //初期化すること

  console.log(i);

  if (i === 4) {

continue; //スキップ
  }

  if (i === 7) {

break; //終了
  }
}

■forEach

※break、continueは使えない

a.forEach (

  function( item ) {

    console.log( item );
  }
)


・アロー関数で書いてみると

a.forEach(item => {

  console.log(item);
});

a.forEach(item => console.log(item); //Aに対してBの処理をする、と覚える

・インデックスもとる場合

a.forEach((item, index) => {

  console.log(`${index}:${item}`);
}

【while文】

let hp = 100;

while (hp > 0) {

  console.log(`${hp} HP left!`);

  hp -=15;
}

※条件を書き忘れると無限ループになりPC、ブラウザの再起動をする羽目になるので注意

do whileも使用できる




【配列】

■基本形

const arr = [80, 90, 40]; //constは、再定義はできないが、値の変更はできる

■プロパティ

arr.index

arr.length :長さを取得

■要素の追加

scores.unshift(100); //先頭に追加
scores.push(120); //末尾に追加

■要素の削除

scores.shift(); //先頭を削除
scores.pop(); //末尾を削除

■配列を空にする

arr.length = 0;

■途中の要素

scores.splice(2, 0, a, b); //第1:スタート地点、第2:削除個数、第3以降:追加要素

削除した要素は返り値で取得することもできる

const b = scores.splice(2, 2, a, b); //bに返り値が入る

■map  配列のすべての要素に対して処理を実行

const b = a.map(item => return item * 2);

■filter trueの要素だけとってくる

const b = scores.filter(item => item % 2 ===0); //偶数だけ取得

【オブジェクト】

const player = {

  name: 'taguchi',

  score: 32, //最後の行はカンマはつけてもつけなくてもOK
}

player.name または player[name] でアクセス




・プロパティ(メンバー)の追加 ただ、keyとvalueを渡せばOK

player.email = 'test@exsample.com';

・プロパティの削除

delete player.email;

■オブジェクトメソッド

Object.keys(player) //keyの配列

Object.values(player) //valueの配列

Object.entries(player) //[[key1, value1], [key2, value2]]といった2次元配列

■スプレッド演算子 『...●●』 :配列(オブジェクト)の連結

★配列でも参照型ではなく、コピーになる!

const a = [10, 20];
const b = [1, 2, ...a];
console.log(b); //[1, 2, 10, 20]

・引数にもよく使われる

const a = [10, 20];
const sum = (a, b) => a + b;
console.log(sum(...a)); //30 ※sum(10, 20)となる


・オブジェクトに使う場合

const o1 = {a: 1};
ocnst o2 = {...o1, b: 2}; //{a:1, b:2}

■分割代入 :配列(オブジェクト)の各値に対応させて代入

const numbers = [1, 2];
const [a, b] = numbers; //a=1, b=2

・スプレッド演算子との組み合わせ;

const numbers = [1, 2, 3, 8];
const [a, b, ...rest] = numbers; //a=1, b=2, rest=[3, 8]

・オブジェクトに使う場合

const player = {

  name: 'taguchi',
  score: 55,
  hp: 33,
}

const {name, ...points} = player; //name = 'taguchi', points = {score:55, hp:33}

【クラス】

class Player {

  constructor(name, score) {

    this.name = name;
    this.score = score;
  }

  showInfo() {

    console.log(`name: ${this.name} score: ${this.score}`);
  }

  static showVersion() { //クラスメソッド(静的メソッド)

    console.log('Player class ver 1.0');
  }
}

const taguchi = new Player('taguchi', '32'); //インスタンス化
console.log(taguchi.name); //呼び出し

・thisについて

this.nameのようにパラメータとして付与しているものは、本来であれば

const name = name;

のように宣言するものだが、パラメータとして付与する場合は宣言不要で

かつそのクラスに紐づいた要素になるので便利。




thisを付与するタイミングは以下の時
┗パラメータにアクセス
┗クラスメソッドにアクセス





■クラスの継承 ※上記のPlayerクラスを継承する

class SoccerPlayer extends Player {

  constructor(name, score, number) {

    super(name, score); //親クラスからメソッドを継承

    this.number = number;
  }
}

■カプセル化(オブジェクト思考)

クラスプロパティは外部から直接呼び出すのは避ける。

呼び出したい場合はクラス内でプロパティを呼び出す関数を定義し、

クラスメソッドとして呼び出す

例)

class Panel {

  constructor() {

    this.el = document.createElement('li');
  }

  getEl() { //メソッドでプロパティを呼び出す

    return this.el;
  }
}

board.appendChild(panel.getEl()); // カプセル化


【例外処理】

const a = 5;

try {

  console.log(a.toUpperCase()); //toUpperCaseは大文字にする処理なのでエラー

} catch (e) { //エラーオブジェクトをeに格納してくれる

  console.log(e.message); //eのmessageメソッド
}

【非同期処理】

javascriptはシングルスレッド処理。スタックに処理が積まれるため、ネストされた時間のかかるメソッドは後回しされる場合がある。

Promiseオブジェクトでラップすることで処理の終了を保証して次に進める。

async/awaitを使用することでよりシンプルにPromiseの機構を使用することができる。

asyncで定義した関数はPromiseオブジェクトにラップされるため、処理の終了が保証される。

awaitはasyncで定義した関数内で使用することができ、awaitで定義した関数内でPromiseが返されるまで待機する。asyncと何が違うのかいまいち理解できていない




【appendics】

■日付

const d = new Date();

//引数に数値を指定するとその日時のオブジェクトを作成できる(年,月は必須)

console.log(d.getFullYear()); //年

console.log(d.getMonth());  //★0-11

console.log(d.getDate()); //日

console.log(d.getDay());  //曜日0~6

console.log(d.getTime()); //世界共通の数値化された日時

・フォーム

~HTML~

<form name="myform">

  <input type="date" name="date" value="2012-02-16">

</form>

・日付のセット

d.setHours(10, 20, 30) //10時20分30秒

※自動補正が入る(Date(日)で32と入れると翌月になる)




・日付の追加

d.setDate(d.getDate() + 3);

・日付の経過計測

const d1 = new Date(2018, 11, 1); //11月1日
const d2 = new Date(2018, 11, 10); //11月10日
console.log(d2 - d1); //77760000ミリ秒
console.log((d2 - d1) / (24 * 60 * 60 * 1000)); //9日 86400000秒/1日

■アラート :ウィンドウオブジェクト

window.alert('hello');  //windowは省略可

const answer = confirm('Sure?') //承認を求める

console.log(answer);  //返り値はtrue or false

・HTMLで簡単にアラートを出す ※NOを押せばキャンセルされる

https://proengineer.internous.co.jp/content/columnfeature/8007

コード

<a href="http://www.google.co.jp/" onclick="return confirm('よろしいですか?')">リンク</a>

■タイマー

・一定間隔で処理を実行 :setInterval

★処理がどんどん進むのでシステムに負荷をかけないよう配慮が必要

const tim = () => { //関数式で定数に関数を代入

  console.log(new Date());

};

setInterval(tim, 1000); //関数, ミリ秒でセット

・タイマーを止める

変数にsetIntervalを格納

カウンタ変数を別途用意し、関数内でカウンタを増やす

カウンタに対してif分岐を設定しておいてclearInterval()する

let lim = 0;

const tim = () => {

  console.log(new Date());

  lim ++;

  if (lim > 3) {

    clearInterval(timer);
  }
};

let timer = setInterval(tim, 1000);

★・1回だけ実行する :setTimeout

setTimeoutは処理が終わってから実行するため

システムに負荷をかけない

const tim = () => {

  console.log(new Date());
};

setTimeout(tim, 1000);  //ミリ秒で指定

これをループさせることで、一定間隔での処理を実装することもできる

const tim = () => {

  console.log(new Date());

  setTimeout(tim, 1000);  //ミリ秒で指定
};

tim();

止めたい場合は

let lim = 0;

const tim = () => {

  console.log(new Date());

  lim ++;

  let timid = setTimeout(tim, 1000);

  if (lim > 2) {

    clearTimeout(timid);
  }
};

tim();

・カウントアップの基本

const timer = document.getElementById('timer');
let startTime;

cntUp() {

  let leftTime = Date.now() - startTime;
  textContent = (leftTime / 1000).toFixed(1);

  setTimeout(() => {

    cntUp();
  }, 100)
}

★関数を呼び出す

※定数の無名関数の場合、スコープの都合上creaTimeoutできないことがよくあるので関数を使うのがベター

setTimeout(fnc(), 1000); ではなく

setTimeout(() => {

fnc();

}, 1000);

とする。




■1つのjsファイルを複数ページで使いまわす。

複数ページで使いまわすと、現在のページ以外の要素が存在しないと怒られる。

ページごとにユニークなidなどをキーにif文で出しわける。

if(document.getElementById(‘uniqueId’)) {

uniqueIdを持つページの処理
}


■要素内のテキストが画面幅によって折り返しが起きているかどうかを判定する
RangeオブジェクトにNodeをつっこむことで解析できるようにする。
Rangeオブジェクトのメソッド、getClientRects()で表示されている行数を判定できる。
参考:
javascript - htmlでテキスト表示する際、(画面幅等により、)折り返しが発生するかどうかを事前に判定する方法はありますか? - スタック・オーバーフロー

const items = document.getElementsByClassName('item');
let texts;
let range = new Range();
for(let i = 0; i < items.length; i++) {
  texts = items[i].children;
  for(let j = 0; j < texts.length; j++) {
    range.selectNode(texts[j]);
    if(range.getClientRects().length > 2) {
      //複数行になっていた場合に行いたい処理
    }
  }
}


■即時関数の利用

javascriptはスコープがグローバルか関数内ローカルの2種類しかない。

そのため、画面読み込み時の初期化など、再利用される可能性の無いものは即時関数としてくくることでグローバル領域の汚染を防ぐ。

(function () {

    var label = document.getElementById('date_label'),

        now = new Date();

        label.innerText = now;

}()); //★ここに不思議なカッコがあるので注意

参考:https://qiita.com/katsukii/items/cfe9fd968ba0db603b1e




■数字を文字列として出力しても数値として演算ができる

※プラスの場合だけ、文字列の連結として扱われる




■わからないことがあったらMDNで調べよう




■jsはxss(クロスサイトスクリプティング)の危険があるので注意する

https://www.websec-room.com/2013/03/14/567




■javascriptが動作しているかテスト




以下のテストコードをmain.jsなどに記載し、

htmlの要素に id="target"を付与。

表示した要素をクリックして背景がピンクに変われば動作している


document.getElementById('target').addEventListener('click', () => {

document.getElementById('target').style.background = 'pink';

});

【検索ワード】

async await 違い

Promise 中身 取得

javascript備忘録 DOM操作編

【コンソールから操作する】

デベロッパーツール>コンソール

documentと打ち込むとDOMツリーを確認できる

jsのスクリプトからDOM(HTML)を操作する

【要素の取得】

■idで取得 :getElementById()

document.getElementById('target').textContent = 'Changed!';

■要素名、セレクタで取得 :getElementsByTagName()、querySelector()

document.getElementsByTagName('h1');

document.querySelector('h1').textContent = 'h1dayo!';
document.querySelectorAll('li').forEach(li => {
  li.textContent = 'list dayo!';
});

・クエリセレクタはノードを直接記述することもできる。
例)id="result"直下のp要素

let resultScore = document.querySelector('#result > p');

・フォーム部品の指定 input[type="text"]

const i = document.querySelector('input[type="text"]');

//textareaはHTMLでvalueが設定されていなくても.valueで値を取得する
const t = document.querySelector('textarea');
console.log(t.value);

//チェックボックス・ラジオボタン
~HTML~
<input type="checkbox" name="experience" value="done">
<input type="checkbox" name="decide" value="yes" checked>

~js~
console.log(document.querySelectorAll('input[type="checkbox"]')[1]);

//checked属性の指定
console.log(document.querySelectorAll('input[type="checkbox"]')[1].checked);

//プルダウン
~HTML~
<select>
  <option>item1</option>
  <option selected>item2</option>
</select>

~js~
>|??|
console.log(document.querySelectorAll('select > option')[1].selected); //true

・子要素の指定::nth-child()

document.querySelectorAll('li:nth-child(0)').forEach(li => {
  li.textContent = 'list dayo!';
}

//奇数の要素を取得
document.querySelectorAll('li:nth-child(odd)').forEach(li => {
  li.textContent = 'list dayo!';
}

■CSSクラス名で取得 :getElementsByClassName() //★Elementsとなることに注意!

■親要素の指定

const p = document.querySelector('li');
console.log(p.parentNode); //liの親要素であるul要素が取得できる

■子要素の指定

const p = document.querySelector('ul');
console.log(p.chilren);

複数要素がある場合はchildren[1]のようにアクセスできる
すべての要素を出力したい場合、HTMLコレクション(配列ではない)へのアクセスとなるのでforeachを使用すると以下のエラーが出力される。

forEach is not a function at HTMLDivElement

for文であればコレクションにも対応できるのでこちらで記述する。

const p = document.querySelector('ul');
  for (let i = 0; i < p.children.length; i++) {
    console.log(p.children[i]);
}

■カスタムデータ属性へのアクセス :dataset
HTMLで以下のようなカスタムデータ属性を設定していたとする。

<h1 title="this is title" data-app-id="app1">Title</h1>

その場合、以下の注意事項がある
┗データ属性名のハイフンは削る
┗2語目の文頭は大文字
┗jsからのアクセスはdataではなくdatasetとする

const h = document.querySelector('h1');
console.log(h.dataset.appId);

新たなカスタムデータ属性を付与する場合は以下のようにする

h.dataset.appIdd = 'this is changed';
>|??|
するとHTML側では以下のようになる
>|??|
<h1 title="this is title" data-app-idd="this is changed">Title</h1>

【要素の操作】

■基本

document.body.textContent = 'hello'; //bodyに文字を出力
document.title = 'catch me!' //タイトルを変更

基本的には定数に格納してそれに対して操作を用いることが多い

const h = document.querySelector('h1');
h1.textContent = 'hello!';

■テキストの挿入・変更

a.textContent = 'b'; //要素aのテキストをbにする

■属性の追加・変更

sample.setAttribute(name, value)
sample.setAttribute("href", "/sample")  //要素sampleにhref="/sample"が追加される

■CSSスタイルの追加

a.style.backgroundColor = 'gray';

■idの付与

const exam = "idname";  //id名を定数で定義
a.setAttribute("id", "idname");


■cssクラスの付与・変更 ※使いずらいので下のclassListの方がよく用いられる

a.className = 'b'; //要素aにクラスbを付与する

※上書きしてしまうので既存のクラスがある場合はそれも設定する
 例えばcというクラスがすでに設定されていた場合は、a.className = 'c b'; とする

■cssクラスの操作 :classList

a.classList.add('b') //要素aにクラスbを追加する
a.classList.remove('b') //要素aからクラスbを削除する

■特定のcssがあれば削除:contains

const d = document.querySelector('div');
if (d.classList.contains('border-pink')) {
  d.classList.remove('border-pink');
} else {
  d.classList.add('border-pink');
}

・単純な入れ替えは以下でもできる

d.classList.toggle('border-pink');

■要素の生成 :createElement()

const h = document.createElement('h1'); //要素を作成 ※まだ配置していない
h.textContent = 'Title';
document.body.appendChild(h); //bodyの最後の子要素に追加

※位置を指定する場合

const b = document.querySelector('body');
b.appendChild(b);

・要素の途中に追加するには?

const h = document.createElement('h1'); //要素を作成 ※まだ配置していない
h.textContent = 'Title';
const d = document.querySelector('div'); //HTMLの配置ポイントを取得
document.body.insertBefore(h, d); //hをdの直前に配置

※まだ配置していない要素に追加する場合はdocument.は不要

■要素のコピー

const d = document.querySelector('div');
const c = cloneNode(true); //falseにすると空の要素を作る

■要素の削除 ;removeChild

const d = document.getElementById(‘id’);
d.parentNode.removeChild(d); //親要素のノードからremoveChildすることで自身を削除

※複数の要素をリストに格納しfor文で削除する場合、削除のたびにリストの中身が変わるので、インデックスではなく常に先頭の要素を削除するなどの注意が必要。

■子要素をすべて削除

targetArea.innerHTML = '';

■要素のフォーカス
初期値でテキストエリアなど、要素にフォーカスさせたいとき

const t = document.querySelector('textarea');
t.focus();

・フォーカスさせつつ要素を全選択させたい場合 :select()

t.select();

・入力されないようグレーアウトさせる :disabled

t.disabled = true;

■イベントリスナーで関数を実行 :eventListener
ボタンがクリックされたとき、キーボードが操作された時など、様々なイベントをキーとして動作を追加できる

const b = document.querySelector('button');
b.addEventListener('click', () => {

  console.log('clicked!');
})

【appendix】

■スクロール
指定の要素までスクロール

targetNode.scrollIntoView()

・ゆっくりスクロールさせたい場合

targetNode.scrollIntoView({
  behavior: 'smooth'
})

・スクロール位置を微調整したい場合

const targetZahyou = targetNode.getBoundingClientRecte()
targetY = targetZahyou.top + window/pageYOffset

sindow.scrollTo({
  top: targetY,
  behavior: 'sooth'
})

■座標の取得 :mousemoveイベントのclientX,clientY

const d = document.querySelector('div'); //divに座標を表示するためにdを用意
document.addEventListener('mousemove', e => { //mousemoveイベントを実行。引数にeを設定
  d.textContent = `${e.clientX}:${e.clientY}`;  //eのclientXでX座標を取得できる
});

↑引数にeを渡してあげるとイベントオブジェクトが返ってくるので、それにアクセスすることで動作を指定できる。

・aタグに対し操作を行う際は href属性が空だと自身のページを読み込むためNG

★要素の規定の動作をキャンセル :preventDefault()
~HTML~

.hidden {
  display: none;
}

<p>テストだよ!<span class="hidden">隠しテキストだよ!</span><a href="">読み込む…</a></p>

~js~

const a = document.querySelector('a');
const s = document.querySelector('span');

a.addEventListener('click', e => {

  e.preventDefault(); //aタグの規定の動作(ページ遷移)をキャンセル
  a.classList.add('hidden');
  s.classList.remove('hidden');
});

■addEbentListenerいろいろ
参考:https://qiita.com/kogirix/items/77417702cbc474f49df1

■要素がクリックされたらイベントを起動

const st = document.getElementById('start'); //スタートボタンをidで取得
st.addEventListener('click', () => {

    sttime = Date.now(); //クリックされた時の処理
  });

■押されたキーボードの値を取得

window.addEventListener('keydown', (e) => {
  e.key //ウィンドウで押下されたキーの値を取得
})

■画面をクリックしたらイベントを起動

window.addEventListener('click', (e) => { //画面をクリックしたら
})

■アラートを表示

alert('Game Over'); //アラートを表示

■画面幅で改行が発生しているかを判定

window.addEventListener('DOMContentLoaded', () => {
	let range = new Range();
	range.selectNode(inlineNode);
	console.log(range.getClientRects().length) //行数が取得できる
})

【エラー対応】

■HTMLCollectionのlengthが取得できない
document.getElementsByClassNameで要素を取得するとHTMLCollectionが返ってくる。
console.logで出力するとlengthはしっかりカウントされている(ゼロでない)のだが、このlengthを取得しようとするとなぜか0になる。

const a = document.getElementsByClassName;
console.log(a);  //HTMLCollectionが表示され、プロパティのlengthは0ではなく、値も取得できている。
const b = a.length;
console.log(b);  //なぜか0になる

・解決方法
調べたところ、HTMLCollectionの値が取得される前にconsole.log(b)を実行してしまっている可能性。
正確な読み込みタイミングは調べていないが、以下の順?

  • 変数aが作成される
  • getElementsByClassNameが実行される(DOMがレンダリングされるまで待機)
  • 変数bが作成される
  • a.lengthが実行される(まだDOMレンダリング途中のためlength=0)
  • レンダリングが完了しaにHTMLCollectionが返る
window.addEventListener('DOMContentLoaded', () => {  //DOMが完全に読み込まれてから処理を行う
	if(document.getElementsByClassName('sample-class').length) {
		console.log('exist');
	} else {
		console.log('not');
	}
})

試してはいないけど、処理順の問題ならasinc、awaitでも解決できそう。

Uncaught ReferenceError: 

typescript備忘録

【概要】

マイクロソフト開発

JavaScriptの拡張。大規模開発に対応できるように

・静的な型付け(jsは動的)なので一度型が決まると変えられない

・クラスベースのオブジェクト指向

・公式サイト: https://www.typescriptlang.org/




【基本】

main.tsファイルを作成

tsc main.ts //実行

node main.js //コンソール出力




型推論が適用される

var i: number = 10; //型明示

var i = 10; //型推論でintと判断される

var x; //any型 

■配列

var results: number[];

results = [10, 5, 3];

■列挙

enum Signal {

    Red = 0,

    Blue = 1,

    Yellow = 2

}

enum Signal {   //数値は省略できる

    Red,    //0

    Blue = 3,

    Yellow  //4 ※設定の後からの連番になる

}

var result: Signal;

■関数

function 関数名(引数): 戻り値 {

    return a + b;

}

例)

function add(a: number, b: number): number {

    return a + b;
}

//戻り値が無い場合はvoid

//?を付けて引数の要求を任意にすることができる。(a: number, ?b: number)

ただし、任意の引数の後に必須の引数を定義することはできない

//引数に初期値を渡すこともできる(a: number = 10, ?b: number)




■無名関数

var add = function(a; number, b: number): number{ 

    return a + b;

}

アロー関数にすると

var add = (a; number, b: number): number => { 

    return a + b;
}

さらに短く

var add = (a; number, b: number): number => a + b;

■関数のオーバーロード(関数名が同じで型が違う)

function add(a: number, b: number): number;
function add(a: string, b: string): string;

function add(a: any, b: any): any { //関数宣言はany型にする
    if(typeof a === "string" && typeof b ==="string"){
        return a + " " + b;
    }

    return a + b;
}

console.log(add(5, 3)); //8
console.log(add("hello", " world"));    //hello world

//その他の組み合わせはコンパイルエラーになる

■クラス

class User {

    public name: string;

    constructor(name: string){

        this.name = name;
    }

//変数宣言とコンストラクタ処理を同時に行う場合、

//以下のように宣言もコンストラクタ内の記述も不要

    constructor(public name: string){

//private _nameのように、プライベートの場合は慣習的に_を付ける
    }

    sayHi(): void{  //メソッド。functionは書かない

        console.log("hi! i am " + this._name);
    }

    //ゲッター(getter)とセッター(setter)

    get name() {

        return this._name;
    }

    set name(newValue: string) {

        this._name = newValue;
    }
}

var tom = new User("Tom");

console.log(tom.name);

tom.sayHi();

■継承 ※例として上記のUserクラスを継承

class AdminUser extends User {

    constructor(_name: string, private _age: number) {

        super(_name);
    }

    public sayHi(): void {  //親クラスと同じメソッド名を使うとオーバーライドする

        console.log("my age: " + this._age);

        super.sayHi();  //親クラスのメソッドを呼ぶ
    }
}

継承される可能性があるクラスは、親クラスのプロパティの修飾子はprotectedが良い




■静的メンバ・静的メソッド

class User {

    constructor(private _name: string){

        User.count++; //静的メンバにアクセスするにはクラス名(User)を付ける
    }

    static count: number = 0; //静的メンバ

    static showDescription(): void { //静的メソッド

        console.log("this class is about users");
    }
}

console.log(User.count); //静的メンバ呼び出し

User.showDescription();

【環境構築】

参考:https://qiita.com/ochiochi/items/efdaa0ae7d8c972c8103

・node.jsのインストール

https://nodejs.org/ja/

コマンドプロンプトで確認

node -v

npm -v

・typescriptコンパイラのインストール

npm install -g typescript //-g:グローバル環境にインストール

確認

tsc -v