Python 3.5.1 + Selenium + Chrome on Windows

GUIで操作するSeleniumを利用したことがあるような記憶はありましたが、
プログラムからも操作できるそうなので
Windows7上でPython3からSeleniumを利用してChromeを開くまでを行ってみました。

pipでseleniumをインストール

python -m pip install selenium
  • この時点では selenium-3.0.2 がインストールされました。
Collecting selenium
  Downloading selenium-3.0.2-py2.py3-none-any.whl (915kB)
    100% |################################| 921kB 856kB/s
Installing collected packages: selenium
Successfully installed selenium-3.0.2

ChromeDriverをインストール

https://sites.google.com/a/chromium.org/chromedriver/downloads

  • この時点では ChromeDriver 2.27 が最新でした。

PythonChromeを起動

  • executable_path にダウンロード後に展開した ChromeDriver のパスを指定します。
# -*- coding: utf-8 -*-

from selenium import webdriver
from selenium.webdriver.common.keys import Keys

browser = webdriver.Chrome(executable_path='[C:\\設置したパス\\chromedriver.exe]')
browser.get('https://www.google.co.jp/')
browser.find_element_by_name("q").send_keys("Python3 Selenium Windows Chrome")
browser.find_element_by_name("q").send_keys(Keys.ENTER)
  1. Chromeを指定。
  2. https://www.google.co.jp/ を開く。
  3. 検索ワードに Python3 Selenium Windows Chrome を入力。
  4. Enterキーを押す。

参考

http://clientver2.hatenablog.com/entry/2015/11/15/000054

Vagrant + Python3.5 を用いて Japronto を動かすメモ

WindowsVagrant + Python3.5 を用いて Japronto をとりあえず動かすまで。

VirtualBox/Vagrantはインストール済み

Japronto

  • レスポンスが高速らしいとのことで興味を持ちました。

VagrantにCnetOS環境構築

  • Vagrant, Python3 のセットアップは一部こちらを参考にしました。
$ vagrant box add py3_centos67 https://github.com/CommanderK5/packer-centos-template/releases/download/0.6.7/vagrant-centos-6.7.box
$ vagrant init
  • Vagrantfileを編集
Vagrant.configure("2") do |config|
  config.vm.box = "py3_centos67"
  config.vm.network "private_network", ip: "192.168.33.15"
  config.vm.synced_folder "./public", "/home/vagrant/public"
end
  • Guest Additionsをインストール後、Vagrantを起動してゲストOSに接続
    ※次回起動時に共有フォルダが共有されない問題が起きたときにGuest Additionsをインストールして解消しました。
$ vagrant plugin install vagrant-vbguest
$ vagrant vbguest
$ vagrant up
$ vagrant ssh

Python3 と Japronto インストール

$ sudo su -
$ yum install https://centos6.iuscommunity.org/ius-release.rpm
$ yum search python3
$ yum install -y python35u python35u-pip python35u-devel
$ pip3.5 install --upgrade pip
$ pip3.5 install japronto
$ exit

ホストOSで ./public/index.py を用意

  • 共有フォルダ(config.vm.synced_folder)を設定しているのでホストOS側で用意できます。
  • ゲストOSで操作する際は vi などで。
  • ./public/index.py ※日本時間で今日の日時を表示
#!/usr/bin/python3.5
# -*- coding: utf-8 -*-

from japronto import Application
import codecs
import datetime

DEFAULT_ENCODING = 'UTF-8'
JST = datetime.timezone(datetime.timedelta(hours = 9), 'JST')

def header(title):
    with codecs.open('header.txt', 'r', DEFAULT_ENCODING) as f:
        return f.read().format(title)

def footer():
    with codecs.open('footer.txt', 'r', DEFAULT_ENCODING) as f:
        return f.read()

def index(request):
    now = datetime.datetime.now(JST)
    text = ''
    text += header('index')
    text += '<div class="center">'
    text += now.strftime('<span class="l b">%Y</span><span class="s">年</span><br>')
    text += now.strftime('<span class="l b">%m</span><span class="s">月</span>')
    text += now.strftime('<span class="l b">%d</span><span class="s">日</span>')
    text += '<span class="s">(</span><span class="l b">{}</span><span class="s">)</span><br>'.format(
        ['日', '月', '火', '水', '木', '金', '土'][int(now.strftime('%w'))]
    )
    text += now.strftime('<span class="l">%H</span><span class="s">:</span><span class="l">%M</span><span class="s">:</span><span class="l">%S</span>')
    text += '</div>'
    text += footer()
    return request.Response(mime_type='text/html', text=text, encoding=DEFAULT_ENCODING)

app = Application()
app.router.add_route('/', index)
app.run()
  • ./public/header.txt ※ファイルが長くなるので分けました。
<!DOCTYPE html>
<html>
<meta http-equiv="refresh" content="1;URL=/">
<head>
    <title>{}</title>
</head>
<style type="text/css">
.center {{
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    margin: auto;
    width: 200px;
    height: 80px;
    text-align:center;
}}
.b {{
    font-weight: bold;
}}
.l {{
    font-size: larger;
}}
.s {{
    font-size: smaller;
</style>
<body>
  • ./public/footer.txt ※ファイルが長くなるので分けました。
</body>
</html>

ゲストOSで index.py を起動

$ ./index.py
Accepting connections on http://0.0.0.0:8080

起動後はホストOSのブラウザで http://192.168.33.15:8080/ にアクセス

f:id:nfnoface:20170227151253p:plain

Python 3.5.1でプレミアムフライデーを求めてみた

偶然目にしたPHPプレミアムフライデー(月末の金曜日)を求めるが興味深かったのでPythonプレミアムフライデーを求めてみました。

# -*- coding: utf-8 -*-

import calendar

cal = calendar.Calendar(firstweekday=calendar.FRIDAY)

y = 2017
for m in range(1, 13):
    for row in reversed(cal.monthdayscalendar(y, m)):
        d = row[0]
        if d > 0:
            print('{}-{:0>2}-{:0>2}'.format(y, m, d))
            break

結果

2017-01-27
2017-02-24
2017-03-31
2017-04-28
2017-05-26
2017-06-30
2017-07-28
2017-08-25
2017-09-29
2017-10-27
2017-11-24
2017-12-29

Python 3.5.1 で文字コード指定してテキストファイルを読み込んだ際のメモ

  • codecs を使うと良い様子
# -*- conding: utf-8 -*-

import codecs
import os

file = '.{}UTF8.txt'.format(os.path.sep)

f = codecs.open(file, 'r', 'utf-8')
for lineno, line in enumerate(f, start=1):
    print(lineno, ':', line.rstrip())
f.close()

または

# -*- conding: utf-8 -*-

import codecs
import os

file = '.{}UTF8.txt'.format(os.path.sep)

with codecs.open(file, 'r', 'utf-8') as f:
    for lineno, line in enumerate(f, start=1):
        print(lineno, ':', line.rstrip())

など

  • codecs.open() の第三引数に 'utf-8' を指定してみたところ
  • 他、lineno で行番号を出力
  • 他、''.rstrip() で行末の改行を除去

出力例:

1 : これは1行目
2 : 2行目
3 : 3行目の文章
4 : 4行目

場合によっては、
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
などで標準出力の文字コードも変える。 以前試した別記事:

行番号 lineno についてはこちらを参考にしました:

Laravel 5.4.12 でカスタムコマンドを試す

artisan でカスタムコマンド HelloCommand を作る

  • make:command でカスタムコマンド HelloCommand を作成。
  • --command= オプションで $signature を指定して作成できる模様。
$ php artisan make:command HelloCommand --command="command:hello {message=Hello}"
Console command created successfully.

ファイルを編集: app/Console/Commands/HelloCommand.php

  • 新規に作成された app/Console/Commands/HelloCommand.php を編集。
  • $signature に、コマンドの名称と引数を設定。 --command= オプションで指定済みの値が入っている。
  • handle() に処理を書く。
<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;

class HelloCommand extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'command:hello {message=Hello}';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Command description';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        $funcs = ['line', 'info', 'comment', 'question', 'error'];
        foreach ($funcs as $func) {
            $this->$func($this->argument('message'));
        }
    }
}

ファイルを編集: app/Console/Kernel.php

  • 元からある app/Console/Kernel.php を編集して、 HelloCommand を登録。
<?php

    /**
     * The Artisan commands provided by your application.
     *
     * @var array
     */
    protected $commands = [
        Commands\HelloCommand::class
    ];

引数無しで実行してみる

  • php artisan command:hello
$ php artisan command:hello
Hello
Hello
Hello
Hello
Hello

Windows7のcmdで実行した場合、出力される文字に色は付かない。

引数に Hoge と指定して実行してみる

  • php artisan command:hello Hoge
$ php artisan command:hello Hoge
Hoge
Hoge
Hoge
Hoge
Hoge

登録したカスタムコマンドを確認してみる

  • php artisan list [<namespace>]
$ php artisan list command
Laravel Framework 5.4.12
:
:
Available commands for the "command" namespace:
  command:hello   Command description

参考

Three.js r76 で星空を作ってみた

リアルな感じではなくGIFアニメっぽい感じです。
完成系はこちらです。

Three.jsはまだ触れ始めたばかりですので手順として誤りなどが含まれているかもしれません。

目次

星を斜めに移動させたいのでカメラを回転させました

SphereGeometryを利用して挑んでいます。

カメラを引いてみると次のようになります。

f:id:nfnoface:20161202172610g:plain

この球体を利用すると地球に例えるなら北極と南極に当たる部分には頂点が密集しています。
この頂点が密集してる部分を利用すると等間隔っぽい並びにならなくなるためカメラに映りこまないようにしたいと考えました。
球体のY軸だけ回せばこの頂点が密集してる部分はカメラに映りこまないので、
斜めに移動しているように見せるためカメラそのもののZ軸を回すことにしました。

テクスチャ画像はjsにBase64エンコードして埋め込んでいます

やり方が悪かったのかデスクトップ上でテクスチャが読まれないので、テクスチャ画像をjsに埋め込みました。

// http://icooon-mono.com/14622-%E6%98%9F%E3%81%AE%E7%84%A1%E6%96%99%E7%B4%A0%E6%9D%907/
var starImg = new Image();
starImg.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABPElEQVQ4T5WTwU3DQBBF/48FV0IFpANikdyhAtwBOWCLG6GDdADcUMwhdGAqgDtGTirAdJBcg+xBu4sdrxU7ZCQfrJl5uzP/L9ESEgeeSnMwjZrK2JSQ5KaHLP/SeWd9THe23FbbDPgMxhDc6ybijmfTh/0AsZ8CPNFNInMOQ/ffAEn8PjImVoMjLt1wXodQL4o4NSehD0gXYA+A+qqRAqJutQRhQIKFAqjlHLWp0ZJbUT6uz8GOkmlfyAqSe1qFP8kUxIyyOxZwOh7dp7SUUZJRF9mhkupqR/8LnPW48IXlg63br9NqatiAqnmavWuZygbEfgTwsn0EeeUg1G/EmLQSEgey+ZVviIxMFWelK83jKvs2SzRyvhmAPML5mRSLMgs+mAC8Nen8gsPnd+sGZZFIVCTroxjP0KvCfwGZVYAhUmIo4gAAAABJRU5ErkJggg==';
var starTexture = new THREE.Texture();
starTexture.needsUpdate = true;
starTexture.image = starImg;

なお、テクスチャとして画像はこちらのものを利用させていただいています。

画像をjsに埋め込む際Base64エンコードをするため、「画像 base64エンコード」のキーワードでヒットしたサイトを利用しました。

SphereGeometryの頂点座標を活用しPlaneGeometryの星を配置しました

以前作ったThreejs r76で球体&パーティクルの頂点部分が星になれば良いかと考えて、SphereGeometryを利用しています。当初はPointsの個々に貼り付けたテクスチャを回転させることが出来ればと考えましたが、テクスチャを回転させるような手段は用意されていないようで、個々の星はPlaneGeometryを用いてSphereGeometryの頂点部分に貼り付ける形としています。

親の球体に子の星を追加しています

言い回しがあっているかわかりませんが、シーンに直接加えるとグローバル空間上の中心を軸とするようです。
この中心を軸に星々を回転させることは可能でしたが、星々をその場で回転させることは容易でないようです。
そこで、球体を親とし星を子として加えることで子の星はZ軸を回転させるだけでその場で回転させることが行えました。
なお、親を回転させれば子も追随してグローバル空間上を回転するので、やりたいことを達成しました。

  • 球体の頂点が密集している北極と南極に当たる部分はカメラに映りこまないのでその部分は除外するようにしています
if (this.starSphere[this.starSphere.length - 1].geometry.vertices[i].y > 130 || this.starSphere[this.starSphere.length - 1].geometry.vertices[i].y < -130) {
    continue;
}
  • 星になるPlaneGeometryを用意し
var plane = new THREE.Mesh(
 new THREE.PlaneGeometry(starSize, starSize),
  material.clone()
);
  • 親となる球体this.starSphere[this.starSphere.length - 1]の頂点座標を活用して
plane.position.set(
    this.starSphere[this.starSphere.length - 1].geometry.vertices[i].x + ((variability * Math.random()) - (variability / 2)),
    this.starSphere[this.starSphere.length - 1].geometry.vertices[i].y + ((variability * Math.random()) - (variability / 2)),
    this.starSphere[this.starSphere.length - 1].geometry.vertices[i].z
);
  • addしています
this.starSphere[this.starSphere.length - 1].add(plane);
  • 子を親に加えた後は親のオブジェクトをシーンに加えます
this.scene.add(this.starSphere[this.starSphere.length - 1]);

球体はあくまでも軸としているだけなのでMaterialは不要と思っていましたが。

やり方の問題かもしれませんが、子の星を追加していない状態ならば何もレンダリングされないようなのですが、Material未指定のPointsを利用したところ球体の頂点が青白い点でレンダリングされてしまいました。

this.starSphere.push(new THREE.Points(
    new THREE.SphereGeometry(200, particleSize, particleSize)
));
f:id:nfnoface:20161202174239g:plain

透明のMaterialを利用することで回避しました。

this.starSphere.push(new THREE.Points(
    new THREE.SphereGeometry(200, particleSize, particleSize),
    new THREE.PointsMaterial({transparent: true, opacity: 0})
));

Raycasterでマウスに反応するようにしてみました

マウスに流れ星以外の星が重なると色が変わり回転が速くなります。

f:id:nfnoface:20161202173345g:plain
  • マウスが動いたら位置を記録
this.renderer.domElement.addEventListener('mousemove',function(e){
    this.mouseVector2.x = (e.clientX / this.renderer.domElement.width) * 2 - 1;
    this.mouseVector2.y = -(e.clientY / this.renderer.domElement.height) * 2 + 1;
}.bind(this), false);
  • マウスに重なった星intersectsを取り出して処理

※この際、個々の星の色を変えるためにmaterialclone()しています。同じマテリアルだと同じマテリアルを利用している星全ての色が変わってしまいます。

f:id:nfnoface:20161202180403g:plain
var checkMouseMove = (this.mouseVector2.x >= -1 && this.mouseVector2.x <= 1 && this.mouseVector2.y >= -1 && this.mouseVector2.y <= 1);
if (checkMouseMove) {
    this.raycaster.setFromCamera(this.mouseVector2, this.camera);
}

for (var i = this.starSphere.length - 1; i >= 0; --i) {if (checkMouseMove) {
        var intersects = this.raycaster.intersectObjects(this.starSphere[i].children);
        if (intersects.length > 0){
            for (var j = intersects.length - 1; j >= 0; --j) {
                var add = this.localAddZ[intersects[j].object.geometry.id];
                intersects[j].object.rotation.z += add > 0 ? 0.15 : -0.15;
                intersects[j].object.material.color = this.starMaterialMouseenterColor;
                intersects[j].object.scale.set(1.5, 1.5, 1);
            }
        }
    }
}

Raycasterの使い方についてはドキュメントにあるExampleを参考にしています。
https://threejs.org/docs/api/core/Raycaster.html

フレームレートは 30 fps にしています

Firefox 50.0.2 だと次のような警告が出力されます。

Error: WebGL: texImage2D: Incurred CPU-side conversion, which is very slow.
Error: WebGL: texImage2D: Incurred CPU pixel conversion, which is very slow.
Error: WebGL: texImage2D: Chosen format/type incurred an expensive reformat: 0x1908/0x1401

Firefoxの問題のようです。
https://bugzilla.mozilla.org/show_bug.cgi?id=1246410

requestAnimationFrame()を利用すると 60 fps になるそうですが、処理の負荷軽減になるかと思い 30 fps になるようにしました。

if (this.frame++ % 2 === 0) {
    this.render();
}

こちらのサイトを参考にしました。

CakePHP2系で指定Exceptionのログ出力を抑制する

環境は PHP 5.6.24, CakePHP 2.7.11

コード

  • 例えば MissingControllerException の出力を抑制するなら、app/Config/core.phpに、'skipLog' => ['MissingControllerException']を加える。
    Configure::write('Exception', [
        'handler' => 'ErrorHandler::handleException',
        'renderer' => 'ExceptionRenderer',
        'log' => true,
        'skipLog' => ['MissingControllerException']
    ]);

参考サイト

  • 初めにたどり着いたサイトはCakePHP3系用だったので書き方が少々異なりました。
  • 更に調べたところCakePHP2系のものにたどり着きました。