20210115のNode.jsに関する記事は5件です。

Scratch Offline Editor for Linuxを作る

ScratchはOffline Editorを提供しているため,オンラインでなくても使うことができます.
Scratch - Scratch Offline Editor
image.png

しかし,上記リンクを見れば分かるとおり,Linux向けがありません.残念...

ところが,ScratchはEditorのGUIをオープンソースで公開しています.
LLK/scratch-gui

これを自分でビルドしてローカルで実行すれば,オフライン版Scratchとして使えそうです.

準備

以下を揃えてください.

  • Node.js
  • npm
  • git
  • 適当なブラウザ(ChromeとかChromiumとかFirefoxとか)

以下はUbuntuでの例です.

Ubuntuでの例

パッケージをインストールします.

Gitをインストールします.

sudo apt install git

次に,以下の記事を参考にしてNode.jsの環境を入れてください(引用させていただきます.ありがとうございます).
Ubuntuに最新のNode.jsを難なくインストールする

いざ

まず,Gitからリポジトリをクローン,依存関係のインストールを行います.結構時間かかるかも.

git clone --depth=1 https://github.com/LLK/scratch-gui.git
cd scratch-gui
npm install

ScratchはReactで作られているので,これでnpm startとすれば起動できますが,起動するたびにコンパイルされるのは少し時間がかかってしまいます.ということで,先にコンパイルしておきます.
結構時間かかります.

npm run-script build

終わると,builds/内にindex.htmlが生成されていると思います.これをChrome等で開き,正常に動作すればオフライン版完成.
image.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScript Puppeteerで絶対ハマらないブラウザ操作2021

はじめまして。マーティンです。Twitterフォロワー20人記念記事、爆誕です。
スクレイピングライブラリはいろいろありますがブラウザ操作用ライブラリはPuppeteer一択じゃぁ!
個人的にはPythonよりJSでブラウザ操作する方が好みですが、このPuppeteerというライブラリは少々くせ者ですのでハマりやすいところを解説します。
JavaScriptの文法を理解した初心者の方向けです。

目次

1.そもそもPuppeteerとは
2.始め方
3.おすすめ起動オプションと注意点
4.ページ遷移
5.よく使うpageのメソッドと注意点

1. そもそもPuppeteerとは

Puppeteer is a Node library which provides a high-level API to control Chrome or Chromium over the DevTools Protocol. Puppeteer runs headless by default, but can be configured to run full (non-headless) Chrome or Chromium.

Nodeで動くChromium操作用のライブラリ。「ヘッドレス」なので、デスクトップにChromiumを表示させずに動かすこともできるということですね。
Chromiumというやつがオープンソースで、それをPuppeteerで操作するという流れ。自分のパソコンに入ってるChromeを操作するわけではない。Chromiumはpuppeteerをインストールすると一緒についてくる。

Async AwaitのおかげでPuppeteerを使うのはめちゃくちゃ簡単になりました。
Puppeteerは操り人形という意味らしい。

2. 始め方

$npm install puppeteer

installするものはこれだけです。
Puppeteerで何かするときは以下の雛形を用います。

const puppeteer = require('puppeteer');

const URL = "https://example.com";

(async () => {
  const browser = await puppeteer.launch();//あとでオプション設定するよ
  const page = await browser.newPage();
  await page.goto(URL);//あとでオプション設定するよ

 //ここでなんかするよ

  await browser.close(); //開発時はコメントアウトしておくと良いよ
})(); //即実行する関数よ

3. おすすめ起動オプションと注意点

オプションの指定が可能です。

Puppeteerはデフォルトでヘッドレスが有効になっていますが、無効に設定できます。
slowMoを設定しないと処理が超高速で行われてしまいます。非同期処理に自身がない方は必ずslowMoしましょう。非同期処理に自身がある方も基本的にslowMoしたほうが良いかと。slowMo:50くらいにするといい感じの速度でブラウザが操作されるので見ていて面白い。

またwindowサイズを決定することも重要です。レスポンシブデザインのサイトでは閲覧するブラウザのwindowサイズによってHTMLの要素が変化する場合があるため、windowサイズを明示的に固定することでこれを防ぎます。
defaultViewportを設定しましょう。defaultViewportを設定することによりHMTLページのサイズを指定します。下にある --window-size はHTMLページのサイズではなくブラウザのサイズのみ指定します。defaultViewportなしではレスポンシブデザインによるHMTL要素の変化を防止できません。

goto(URL)するときはwaitUntilも一緒につけちゃいましょう。
https://pptr.dev/#?product=Puppeteer&version=v5.5.0&show=api-pagegotourl-options
ページ遷移を待ってくれます。

//開発時はheadless:falseが良い
await puppeteer.launch({
  headless:false,
  slowMo:50,
  args: ['--window-size=1900,1080'],
  defaultViewport: {
    width:1900,
    height:1080
  }
});

await page.goto(URL,{"waitUntil": 'networkidle0'})

学生向けコーナー(需要あるのか?

Googleにログインした状態でブラウザ操作したいときがありますがPuppeteerでGoogleにログインすることは基本的にできません。詳しくは以下の記事がよくまとまっていますのでご覧あれ。
https://qiita.com/vicugna-pacos/items/a52e22d08856d1041316
自動でブラウザを操作していることがバレて弾かれちゃいます。
しかしなんと、教育用Googleアカウント: xxxxxx@ed.jp等を持っている学生ならそのアカウントを使ってGoogleにログインできます。謎ですね!ところが一つ問題点があります。
Headless trueでGoogleにログインしたい場合は必ずUserAgentを設定しなければならないということです。(個人的にハマったところ)
Puppeteerではフォームに値を入力するとき、フォームのHTML要素を指定します。よってheadless falseでブラウザを起動させてからデベロッパーツールを使ってHTML要素を調べるわけです。
headless falseではこちらが指定した通りGoogleにログインできるのですが、trueにした途端できなくります。原因はGoogleログインフォームとUserAgentの関係。
下を試してみると...

//headless trueのとき
console.log(await browser.userAgent())
// => Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/88.0.4298.0 Safari/537.36

//headless falseのとき
console.log(await browser.userAgent())
// => Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4298.0 Safari/537.36

headlessがfalseの場合はuserAgentに「Chrome」とあり,headless trueにするとuserAgentに「HeadlessChrome」とあることからheadlessか否かによってUserAgentが異なることがわかります。
UserAgentが異なると何が問題なのでしょうか?
Googleのログイン画面を見てみましょう。

[headlessがtrue UserAgentがHeadlessChromeの場合]
headless.png
[headlessがfalse UserAgentがChromeの場合]
chrome.png

ご覧の通りHeadlessChromeの場合とChromeの場合でGoogleのログインフォームのHTML構造が異なるため、メールアドレスを打ち込む際の(後に述べますが)inputフィールドを指定するセレクターの違いが生まれることがエラー発生の原因でした。

で解決策は?

Headless trueの状況下でUserAgentをChromeに無理やり設定することで解決!UserAgentはご自分のChromiumで「UserAgent 確認」と検索するといろいろなUserAgent確認用サイトがヒットしますのでそこで調べてください。

await page.setUserAgent("あなたのUserAgent")

4. ページ遷移

非同期処理って何?って方はこちらの記事が非常に分かりやすいので参考までに。
https://qiita.com/soarflat/items/1a9613e023200bbebcb3
https://qiita.com/klme_u6/items/ea155f82cbe44d6f5d88

ページ遷移はPuppeteerの肝。
「async await つけるだけでしょ」と思っていると少し危険です。
Puppeteerでページ遷移を待つときには page.waitForNavigation()が使われますが、これがよく誤用されがち。

waitForNavigationはページ遷移の前に宣言

await Promise.all([
    page.waitForNavigation(),
    page.click("button")
]);

buttonタグをクリックするpage.click()よりもあとに宣言された場合、たまにエラーが発生します。clickによるページ遷移がwaitForNavigationが呼ばれる前に完了した場合、waitForNavigationはページ遷移を認識できない。

waitForNavigationよりwaitForSelector

waitForNavigationにはいくつかオプション指定ができますが、どのオプションを持ってしても確実にページ遷移の非同期処理が行われるという保証はありません。そこで登場するのがwaitForSelector。

await Promise.all([
  page.waitForSelector("input[name='email']"),
  page.click("button")
])

読んで字の如く、指定したセレクターが出現するのを待ちます。セレクターの指定方法はquerySelectorと同じです(後述)。
waitForSelectorを使えば非同期処理エラーはまずないだろうと思いますが、それでも不安な方はwaitForTimeoutを使いましょう。

await Promise.all([<省略>])

await page.waitForTimeout(500) ///500ミリセカンド待つ

余談ですが少し前まで使われていたwaitForは非推奨となりましたので代わりにwaitForTimeoutを使いましょう。

waitFor is deprecated and will be removed in a future release. See https://github.com/puppeteer/puppeteer/issues/6214 for details and how to migrate your code.

5. よく使うpageのメソッドと注意点

page.click(selector[, options])

ページ内のHMTL要素をCSSセレクターを用いて指定します。
CSSセレクターはどうやって指定するのか?
デベロッパーツールを開いてお目当てのHTMLタグを右クリック、「Copy」=>「copy selector」で一発です。オプションはあまり使わないので言及しません。

page.type(selector,text[,options])

指定したCSSセレクターにテキストを入力します。

page.evaluate(pageFunc[,...args])

具体例を見てみましょう。

let text = await page.evaluate((selector) => {
  return document.querySelector(selector).innerHTML;
},"div > h1")

page.evaluateの第一引数にpageFuncとして渡すアロー関数を、第二引数にセレクター"div > h1"を渡していることがわかります。第二引数に渡したセレクターがpageFuncの引数selectorに代入され処理が実行されています。
注意したいことが3つ。
1つ目がpageFuncはブラウザ内で実行される関数であるため、スクリプトファイル内で定義された変数を直接関数内で使うことはできないこと。しっかりpage.evaluateの第二引数として変数をわたしてあげましょう。
2つ目がreturnをすること。returnしないとtextに値がを代入されません。
3つ目はmapとforeachについて。evaluate内でquerySelectorAllするときはmapを使いましょう。
return document.querySelectorAll("div").map(() => (do stuff here))
mapは新しい配列を返すがforeachは配列を書き換えるだけ。またmap内でもreturnを忘れないこと。
初歩的なことですが、よく忘れてしまうので自戒も込めて。

 その他

const URL = "https://------"のところで行末にセミコロンをつけないとエラーになる
・Puppeteerを使ってGoogleForm自動回答マシーンを作りたい方はhttps://stackoverflow.com/questions/59328036/how-to-select-an-opion-from-a-google-forms-popup-dropdown-puppeteer-nodejs 
が参考になります。
・Puppeteerでブラウザ操作を定期的に行いたいwindowsユーザーの方にはタスクスケジューラの使用をおすすめします。.batファイルでnode実行コマンド記述->タスクスケジューラで定期的に.batファイルを実行。
・間違っているところがあれば指摘していただけると幸いです。

最後まで読んでいただきありがとうございました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

node.jsでTCPで通信 (server側の入力をclient側でエコーバックする)

node.jsでTCP通信をする方法を調べていると

  1. cilent側で文字を入力して
  2. それをserver側で折り返して
  3. それをclient側でコンソールに表示

というサンプルが山ほど見つかるのですが、

  1. server側で文字入力をして
  2. それをclient側で折り返して
  3. それをserver側でコンソールに表示

という例が見つからなかったので、書いてみました。

server.js

var net = require('net');

var server = net.createServer(conn => {
    console.log('connected.');

    conn.on('data', data => {
        console.log('server-> ' + data);
    });

    conn.on('close', () => {
        console.log('closed');
    });

    process.stdin.resume()
    process.stdin.on('data', data => {
        conn.write(data)
    });

}).listen(3000);

console.log('listening on port 3000');

client.js

var net = require('net');

var client = new net.Socket();
client.setEncoding('utf8');

client.connect('3000', 'localhost', () => {
    console.log('connected');
});

client.on('data', data => {
    console.log('client-> ' + data);
    client.write(data);
});

client.on('close', () => {
    console.log('client-> connection is closed');
});

以上です

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

node.jsでTCP通信 (serverからの送信内容をclientでエコーバックする)

node.jsでTCP通信をする方法を調べていると

  1. cilent側で文字を入力して
  2. それをserver側で折り返して
  3. それをclient側でコンソールに表示

というサンプルが山ほど見つかるのですが、

  1. server側で文字を入力して
  2. それをclient側で折り返して
  3. それをserver側でコンソールに表示

という例が見つからなかったので、書いてみました。

server.js

var net = require('net');

var server = net.createServer(conn => {
    console.log('connected.');

    conn.on('data', data => {
        console.log('server-> ' + data);
    });

    conn.on('close', () => {
        console.log('closed');
    });

    process.stdin.resume()
    process.stdin.on('data', data => {
        conn.write(data)
    });

}).listen(3000);

console.log('listening on port 3000');

client.js

var net = require('net');

var client = new net.Socket();
client.setEncoding('utf8');

client.connect('3000', 'localhost', () => {
    console.log('connected');
});

client.on('data', data => {
    console.log('client-> ' + data);
    client.write(data);
});

client.on('close', () => {
    console.log('closed');
});

以上です

参考
https://nodejs.org/api/net.html#net_net_createserver_options_connectionlistener

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Express.js】PUGでサーバーサイドの環境変数を参照する

はじめに

PUGなどのフロントサイドのファイル内でサーバーサイドの環境変数を参照したい時に使える豆知識です。

前提条件

  • dotenvがインストールされていること
  • 環境変数ファイル(.envなど)で適当な値が宣言されていること

実装

Express.jsのapp.localsオブジェクトを使用します。

app.localsオブジェクトは、アプリケーション内のローカル変数である特性を持っており、一度設定されると、app.localsプロパティの値は、リクエストの存続期間中のみ有効なres.localsプロパティとは対照的に、アプリケーションの存続期間を通じて存続します。

簡単にいうと、アプリケーションレベルの環境変数を管理するので、PUGからも参照できるということです。

詳しくは公式ドキュメントを参照してください。
https://expressjs.com/en/api.html#app.locals

実装はすごく簡単です。
app.js内で下記のように宣言します。

SAMPLE_ENV='This is sample environment'
app.js
const express = require('express');
require('dotenv').config();

const app = express();
app.locals.SAMPLE_ENV = process.env.SAMPLE_ENV;

あとはPUGのなかでSAMPLE_ENVで呼び出すことができます。

sample.pug
p 結果 : #{SAMPLE_ENV}

出力結果

結果 : This is sample environment
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む