動かざることバグの如し

近づきたいよ 君の理想に

RubyのRestClientでエラー時に本文を取得したい

環境

  • ruby 2.5
  • RestClient 2.0.2

やりたいこと

rubyrest-clientというHTTPクライアントがある。

RestClientで404とか500とかエラーになった場合例外が発生してしまう。

よって以下のように例外をキャッチするようにする。

begin
  res = RestClient.get(url)
rescue => e
  p e
end

ここまではいいのだが、例外が発生した場合、つまりエラーのときにresには何も入らないので取得したデータがわからない問題がある。

対策

以下で取れたは。 ちゃんとコード読もうな(

e.response.body

docker-composeでアスタリスク(*)を含むコマンドを実行したい

なんでできへんのや

環境

$docker -v
Docker version 18.06.1-ce, build e68fc7a

$docker-compose -v
docker-compose version 1.22.0, build f46880f

やりたいこと

docker-composeが起動している上で以下のようにするとdocker内でコマンドを実行できる。

docker-compose exec サービス名 コマンド

でそのコマンドの中で*(アスタリスク)を使うことって多いと思うんだよね。ワイルドカードみたいに

しかし実行すると予想通りの結果にならない。

$ docker-compose exec php-fpm ls /tmp/*.json
ls: cannot access '/tmp/*.json': No such file or directory

確実にjsonファイルが有るはずなのに表示できない。。。

原因

docker-composeで実行されるのはシェルではなくコマンド?

ワイルドカード*の機能はシェル/bin/shの機能なので使えない」ってこと

解決

shするしかない

docker-compose exec php-fpm sh -c 'tail -f /tmp/*.json'

でいけた

どんなデータもぶっこ抜くWIFIアクセスポイントを作る

自分用メモ

環境

mitmproxyのインストール

pipで一発インストールできる

pip install mitmproxy

ここで mitmproxy を実行

Androidスマホwifi設定の「プロキシ」を設定

  • IP: サーバーのIP
  • ポート番号: 8080

でいける。がhttpsのサイトにアクセスすると証明書エラーで死ぬと思う

そこでmitmproxyが発行するオレオレ証明書を食わせる。 http://mitm.it へアクセスして、インストール Androidの場合、証明書の種類は「VPN/アプリ」だった

Wifiアクセスポイントの作成

かつてはhostapdインストールして云々と、面倒だった記憶だが、最近ではcreate_apコマンドで一発で無線アクセスポイントを作ることができる。

まずhostapdをインストールしておいて

apt install hostapd

git cloneでインストール

git clone https://github.com/oblique/create_ap
cd create_ap
make install

作成

create_ap wlan0 eth0 hogehoge password

コマンドの構文は

create_ap <アクセスポイントで使う無線デバイス名> <有線(インターネットにつながってる)デバイス名><SSID><WIfiのパスワード>

でいける。他にも豊富なオプションがあるが。公式ドキュメント見てもらったほうが早い

mitmproxyとアクセスポイントの連携

この手のプロキシは実は透過型プロキシと呼ばれており、mitmproxyの公式ドキュメントにも記載されている。

docs.mitmproxy.org

手順としては以下

IPフォワーディングを許可

sysctl -w net.ipv4.ip_forward=1

ICMPのリダイレクトを無効化

sysctl -w net.ipv4.conf.all.send_redirects=0

ルーティング追加ここのwlan0はさっきcreate_apで指定した無線のデバイス

iptables -t nat -A PREROUTING -i wlan0 -p tcp --dport 80 -j REDIRECT --to-port 8080
iptables -t nat -A PREROUTING -i wlan0 -p tcp --dport 443 -j REDIRECT --to-port 8080

いざ実行

mitmweb --web-iface 0.0.0.0 --mode transparent --showhost

Cloud Functions+Puppeteerでサーバーレスなスクレイピング

いやこれはありだと思った。

準備

そもそもGCPのアカウントがないと始まらないのは当然として、手元の環境でgcloudコマンドがアカウント紐付いた状態で叩ける必要がある。(コマンドでCloud Functionsへデプロイをするため)

gloudのインストールは以下を参考に

qiita.com

コード

今回はyarnではなくnpmでやる。適当なディレクトリでnpm init -y

Puppeteerのインストール

npm install puppeteer -y

すると以下のファイルが生成される

package.json

{
  "name": "puppeteer_sample",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "puppeteer": "^1.7.0"
  }
}

index.jsを用意して以下

const puppeteer = require('puppeteer');
let page;

async function getBrowserPage() {
  const browser = await puppeteer.launch({args: ['--no-sandbox']});
  return browser.newPage();
}

exports.screenshot = async (req, res) => {
  const url = req.query.url;

  if (!url) {
    return res.send('params["url"] is blank');
  }

  if (!page) {
    page = await getBrowserPage();
  }
  await page.setViewport({ width: 1366, height: 768 });
  await page.goto(url);
  const imageBuffer = await page.screenshot({quality: 20, fullPage: true, type:'jpeg'});
  res.set('Content-Type', 'image/jpeg');
  res.send(imageBuffer);
};

デプロイ

いざデプロイ screenshotは任意 URLの一部になる。

gcloud beta functions deploy screenshot --trigger-http --runtime nodejs8 --memory 1024MB

結構時間かかるが、しばらくすると以下のようなレスポンスが返ってくるはず

Deploying function (may take a while - up to 2 minutes)...done.
availableMemoryMb: 1024
entryPoint: screenshot
httpsTrigger:
  url: xxxxxxxxxxxxxxxxxxxxxxxxxxx
labels:
  deployment-tool: cli-gcloud
name: xxxxxxxxxx
runtime: nodejs8
serviceAccountEmail: xxxxxxxx@appspot.gserviceaccount.com
sourceUploadUrl: https://storage.googleapis.com/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
status: ACTIVE
timeout: 60s
updateTime: '2018-09-03T13:48:58Z'
versionId: '4'

返ってきた値のhttpsTriggerがCloud FunctionsのAPIエンドポイントになるので

https://httpsTriggerのURL?url=http://example.com/

でアクセスすると無事にhttp://example.com/のキャプチャ画像が表示されるはず

以下はgithubをフルスクリーンで撮ったキャプチャ画像だけど結構綺麗に取れてる。日本語フォントもいけるし結構いいのでは。

f:id:thr3a:20180903230328j:plain

欠点

デバッグが超絶やりづらい

公式ドキュメント上ではCloud Functionsのローカル上でのデバッグ方法載ってるけど手順が悪いのかうまくいかない。。。。

VoTTでラベル付けしたJSONをAWS SageMaker用に変換する

VoTTとはMSが作った画像系機械学習に使うラベリングをGUIで行えるツール

f:id:thr3a:20180903212547j:plain

これのおかげでくっそダルくて発狂しそうなラベル付作業が少しだけ楽になる。神ツール

が、当然マイクロソフト謹製の機械学習用アプリケーション(CNTK)ように作られているので、ライバルのAWSのSagemakerの仕様なんか知ったこっちゃない。だけどどうしてもsagemakerで使いたかったのでCNTK用のJSONをSageMaker用に変換するスクリプトを作った

環境

コード

import json

vott_json_path = "images.json"
images_path = ""
class_list = {'cat': 0, 'dog': 1, 'bird': 2}

images = []
import os
# 画像ファイル名の配列作成
for item in os.listdir(images_path):
    base, ext = os.path.splitext(item)
    if ext.lower() == '.jpg':
        images.append(item)

# 1つのJSONを1つずつに分割していく
with open(vott_json_path) as f:
    obj = json.load(f)
for index, v in obj['frames'].items():
    item = {}
    item['file'] = images[int(index)]
    item['image_size'] = [{
        'width':int(v[0]['width']),
        'height':int(v[0]['height']),
        'depth':3
    }]
    item['annotations'] = []
    for annotation in v:
        item['annotations'].append(
            {
                'class_id': class_list[annotation['tags'][0]],
                'top': int(annotation['y1']),
                'left': int(annotation['x1']),
                'width': int(annotation['x2'])-int(annotation['x1']),
                'height': int(annotation['y2']-int(annotation['y1']))
            }
        )
    item['categories'] = []
    for name, class_id in class_list.items():
        item['categories'].append(
            {
                'class_id':class_id,
                'name':name
            }
        )
    print(item)
    # 書き込み
    os.makedirs(os.path.join("jsons"), exist_ok=True)
    basename = os.path.splitext(images[int(index)])[0]
    with open("jsons/{}.json".format(basename), mode='w') as f:
        json.dump(item, f)

必ず変更が必要なのは以下

  • vott_json_path VoTTが出力のJSONパスを指定 相対パス
  • images_path 画像があるディレクトリを指定 注意しなきゃいけないのはVoTTのJSONにはファイル名の記述はなく、あくまでファイルのインデックスしか持ってないのでファイル名がズレたりファイルの増減があると死ぬ
  • class_list 学習に分けたリストを指定 JSONから読んでパースできないこともないけど面倒だった

あとは実行すれば同一ディレクトリにjsonsができて画像の数だけJSONファイルが生成されているはず!