- 投稿日:2021-01-15T23:08:06+09:00
Scratch Offline Editor for Linuxを作る
ScratchはOffline Editorを提供しているため,オンラインでなくても使うことができます.
Scratch - Scratch Offline Editor
しかし,上記リンクを見れば分かるとおり,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 installScratchはReactで作られているので,これで
npm start
とすれば起動できますが,起動するたびにコンパイルされるのは少し時間がかかってしまいます.ということで,先にコンパイルしておきます.
結構時間かかります.npm run-script build終わると,
builds/
内にindex.html
が生成されていると思います.これをChrome等で開き,正常に動作すればオフライン版完成.
- 投稿日:2021-01-15T22:26:38+09:00
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 puppeteerinstallするものはこれだけです。
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.36headlessがfalseの場合はuserAgentに「Chrome」とあり,headless trueにするとuserAgentに「HeadlessChrome」とあることからheadlessか否かによってUserAgentが異なることがわかります。
UserAgentが異なると何が問題なのでしょうか?
Googleのログイン画面を見てみましょう。[headlessがtrue UserAgentがHeadlessChromeの場合]
[headlessがfalse UserAgentがChromeの場合]
ご覧の通り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ファイルを実行。
・間違っているところがあれば指摘していただけると幸いです。最後まで読んでいただきありがとうございました。
- 投稿日:2021-01-15T16:41:18+09:00
node.jsでTCPで通信 (server側の入力をclient側でエコーバックする)
node.jsでTCP通信をする方法を調べていると
- cilent側で文字を入力して
- それをserver側で折り返して
- それをclient側でコンソールに表示
というサンプルが山ほど見つかるのですが、
- server側で文字入力をして
- それをclient側で折り返して
- それを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'); });以上です
- 投稿日:2021-01-15T16:41:18+09:00
node.jsでTCP通信 (serverからの送信内容をclientでエコーバックする)
node.jsでTCP通信をする方法を調べていると
- cilent側で文字を入力して
- それをserver側で折り返して
- それをclient側でコンソールに表示
というサンプルが山ほど見つかるのですが、
- server側で文字を入力して
- それをclient側で折り返して
- それを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
- 投稿日:2021-01-15T12:14:08+09:00
【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.jsconst express = require('express'); require('dotenv').config(); const app = express(); app.locals.SAMPLE_ENV = process.env.SAMPLE_ENV;あとはPUGのなかで
SAMPLE_ENV
で呼び出すことができます。sample.pugp 結果 : #{SAMPLE_ENV}出力結果
結果 : This is sample environment