Chromeヘッドレスでスクレイピング Node.jsライブラリpuppeteer使ってみた

仕事の依頼でスクレイピングすることがあってヘッドレスブラウザを使ってみた。

目的

ホテルや病院などの検索WEBサイトから
検索結果の名前、住所、電話番号などCSVで取得する

ヘッドレスブラウザ

以前はPhantomJSが使われたようですが、Chromeのヘッドレスが今年リリースされたようで、実際両方使ってみたけどスピードはChromeの方が早い。

Node.jsからChromeのヘッドレスを使うためのライブラリ

chromyだとエラーが出てうまくいかなったし、以下の記事見てpuppeteerを使うことにしました。
ヘッドレスブラウザ(Chrome)を使ってSPAをスクレイピングする - Qiita

puppeteerがこれらのライブラリと一線を画すのは、なんと言っても本家ChromeのDevTool開発チームが作成・メンテナンスしている、という点でしょう。

環境

node v.8.9.1
npm 5.5.1
puppeteer

ソース

puppeteer/search.js at master · GoogleChrome/puppeteer · GitHub

'use strict';

// puppeteer読込み
const puppeteer = require('puppeteer');
// ファイル読み書きライブラリ
const fs = require('fs');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto(【URL】);
  await page.waitForSelector('#search_result .content');

  const links = await page.evaluate(() => {
    const anchors = Array.from(document.querySelectorAll('#search_result .content'));
    return anchors.map(anchor => anchor.textContent).map(text => text.trim().replace(/\s+/g, ','));
  });
  fs.writeFileSync( "puppeteer.csv" , links.join('\n') );

  await browser.close();
})();


スクレイピングも始めてて、Node.jsもほとんど未経験ですが結構簡単にできました!すごいね~。

TechPlay 女子部の皆様へ謝罪

今回はTechPlay女子部のアドベンチャーカレンダーに参加させて頂きました!
初!アドベンチャーカレンダー!(*^^)v
TECH PLAY女子部 Advent Calendar 2017 - Qiita

ただ、私の担当は12日だったのに・・ずるずるずるるるうzzz
ここまで引き延ばしてすみませんでした。。しかも内容も予告してたのと違う・・
来年は計画的に頑張りまっす。

gem Deviseでログイン日時を表示する

環境


DeviseはRubyのgemで、認証機能を簡単に実装できます。

Deviseのデフォルトは、findメソッドから取得できるカラムが決まっています。
例えば、最終ログイン日(:current_sign_in_at)などは取得できないのですが、それを取得できるようにカスタマイズする方法です。


findで取得できないカラムは、以下の定数で設定されています。

gems\devise-4.3.0\lib\devise\models\authenticatable.rb L:57

module Devise
 module Models

  BLACKLIST_FOR_SERIALIZATION = [:encrypted_password, :reset_password_token, :reset_password_sent_at,
    :remember_created_at, :sign_in_count, :current_sign_in_at, :last_sign_in_at, :current_sign_in_ip,
    :last_sign_in_ip, :password_salt, :confirmation_token, :confirmed_at, :confirmation_sent_at,
    :remember_token, :unconfirmed_email, :failed_attempts, :unlock_token, :locked_at]

 end
end

Module: Devise::Models::Authenticatable — Documentation for plataformatec/devise (master)


user.rbのモデルでBLACKLIST内から取得したいカラムを削除します。

class User < ApplicationRecord

 fillable = [:current_sign_in_at]
 Devise::Models::Authenticatable::BLACKLIST_FOR_SERIALIZATION.delete_if { |x|
  fillable.include?(x)
 }

end

ruby on rails - A missing attribute when using { render json: } - Stack Overflow


他のカラムも取得したければ、fillableにカンマ区切りで追加していけばOK!

vmware 共有設定 指定フォルダにマウント・再起動後の自動マウント

VMwareのフォルダ共有設定をしたとき
/mnt/hgfsではなく、指定のフォルダにマウントしたい
再起動後も自動マウントしたいって時に設定する

環境

設定方法

/mnt/hgfsではなく、指定のフォルダにマウントしたい

事前にマウント先には共有ファイルを置くフォルダを作成しておき、マウントする

# mount -t vmhgfs .host:/[共有フォルダ] [マウント先のパス]
例)# mount -t vmhgfs .host:/share /home/users/share
再起動後も自動マウントしたい

/etc/rc.d/rc.local にマウントするコマンドを追記する

# vi /etc/rc.d/rc.local
mount -t vmhgfs .host:/share /home/users/share

Rubocopで使うコマンド 便利設定

rubocopはコーディング規約を自動チェックするツールです。
GitHub - bbatsov/rubocop: A Ruby static code analyzer, based on the community Ruby style guide.

忘れがちな便利コマンドを記載。

ルール名の表示
rubocop -D

.rubocop.ymlに指定する

AllCops:
  DisplayCopNames: true
指定のルールだけ自動変換
rubocop -a --only ルール名,ルール名 ‥
例)rubocop -a --only Style/DefWithParentheses, Style/HashSyntax
ルール回避ファイル(.rubocop_todo.yml)の生成
rubocop --auto-gen-config

規約エラーとして出力させないため.rubocop.ymlに以下を記載する

inherit_from: .rubocop_todo.yml

Xserverにssh接続 windows環境でWinSCPを使う

Wordpressでのサイト構築を頼まれることが増えてきたので
自分のテスト環境をXserverで用意することに。

Xserverを選んだ理由

  • SSLが無料であること
  • ドメインが無料でもらえること
  • 友達が使用していて、使い勝手が良さそうだなと感じたこと
  • 1ヶ月使用料が1,000円以下であること

初回構築費は3,000円かかるけど、長い目で見れば気にならない。

他のレンタルサーバとの比較

あんまり多くを知らないんだけど・・

  • ロリポップは管理画面が見づらい
  • GMOドメイン管理のサービスを使ってるけど、通知メールが多いイメージがあって嫌い。
  • さくらは高い

設定方法

レンタルサーバ

公式サイト通り
SSH設定 | レンタルサーバー【エックスサーバー】

  1. SSH設定をオンにする
  2. 公開鍵認証用鍵ペアを生成する
  3. 公開鍵は自動登録されるので、登録等の操作は必要なし
  4. 秘密鍵は自動ダウンロードされるので、適当なフォルダに格納する
クライアント側

1. WinSCPを開き、接続情報を設定する(詳細は公式サイト通り)
f:id:meikotan:20170903000542j:plain

2. 編集->設定->SSH->認証->認証条件に先ほどダウンロードした秘密鍵のパスを設定する
3. 自動変換のダイアログ通り、PUTTY形式に変換する(鍵生成時に設定したパスワードが必要)
f:id:meikotan:20170903000555j:plain
4. 秘密鍵の拡張子がppkになっていればOK
f:id:meikotan:20170903000626j:plain
5. セッション画面にログインする

Redmineのプラグイン メモ

  • プラグインのフォルダ名が大事
  • たまにバグがあるけど、railsわかれば対処可能
  • 日本語対応してない場合もある

環境・コマンド

インストール

bundle exec rake redmine:plugins:migrate RAILS_ENV=production

アンインストール

bundle exec rake redmine:plugins:migrate NAME=redmine_plugin VERSION=0 RAILS_ENV=production
rm -rf plugins/redmine_plugin

プラグイン

redmine_code_review 0.7.0

コードにレビューを書き込める
ソース:Code Review - Plugins - Redmine

バグ有りのため、以下を参考に。
Redmine コードレビュープラグイン : アジャイル株式会社

custom_menu 1.7.2

自分の好きなページへのリンクを簡単に作成する
ソース:Custom Menu - Plugins - Redmine

以下のプラグインもインストールする必要あり。
a_common_libs 2.2.4
https://bitbucket.org/dkuk/a_common_libs.git

clipboard_image_paste 1.9

画像をコピペで挿入できる。
ソース:Attach image from clipboard - Plugins - Redmine

recurring_tasks 2.0.0

定期タスクを登録する
ソース:Recurring Tasks - Plugins - Redmine

日本語対応なし。

redmine_banner

ヘッダーにバナーを表示する。
ソース:About Redmine Banner Plugin · akiko-pusu/redmine_banner Wiki · GitHub

redmine_lightbox2 0.3.1

ソース:Lightbox Plugin 2 - Plugins - Redmine

redmine_work_time 0.3.3

ソース:Work Time - Plugins - Redmine

usability 2.1.6

monacaで作成 canvasを使ったスクラッチカードアプリ

f:id:meikotan:20170319220425p:plain

クラッチカードの結果用の canvas と、削られる用の canvas を重ねて設置する。
削られる部分の canvas にタッチして色をクリアにすると、結果用の canvas に書かれた文字が見える。


HTML

<style>
canvas{ position: absolute; }
</style>

<canvas id="parent" width="400" height="300"></canvas>
<canvas id="child" width="400" height="300"></canvas>


javascript

var resultString= "1等";

// スクラッチ読み込み
var canvas  = document.getElementById("parent"),
  canvas2 = document.getElementById("child");
var ctx     = canvas.getContext('2d'),
    ctx2    = canvas2.getContext('2d');
 
var startX,startY,x,y,
    isDrawing = false;
    
// 結果用の canvas レイヤ
// canvas の初期化
ctx.fillStyle = "#fff";     
ctx.fillRect(0,0, canvas.width, canvas.height); // 塗りつぶし

// 文字の描画
// 文字のフォント設定
ctx.font = "bold 26px 'MS Pゴシック'";
ctx.textAlign = 'center';
ctx.fillStyle = '#ff0000';

// 結果の描画
ctx.fillText(resultString, canvas.width/2, canvas.height/2, canvas.width); // (文字,始点x,y,最大横幅)に文字を描画


// 削る用の canvas レイヤ
// canvas の初期化      
ctx2.fillStyle = "#999999";
ctx2.fillRect(0,0, canvas.width, canvas.height); // 塗りつぶし


// タッチ開始  
$('#child').on('touchstart', function(e) {   
 isDrawing = true;
  
 // 開始位置としてタッチした場所を設定
  var canvas2Rect = canvas2.getBoundingClientRect();
  var touch = event.targetTouches[0];
  startX = Math.floor((touch.pageX-canvas2Rect.left)/(canvas2Rect.right-canvas2Rect.left)*canvas2.width);
  startY = Math.floor((touch.pageY-canvas2Rect.top)/(canvas2Rect.bottom-canvas2Rect.top)*canvas2.height);
});

// タッチ中
$('#child').on('touchmove', function(e) {
 if(!isDrawing) return;

  // 終了位置の設定
  var canvas2Rect = canvas2.getBoundingClientRect();
  var touch = event.targetTouches[0];

  x = Math.floor((touch.pageX-canvas2Rect.left)/(canvas2Rect.right-canvas2Rect.left)*canvas2.width);
  y = Math.floor((touch.pageY-canvas2Rect.top)/(canvas2Rect.bottom-canvas2Rect.top)*canvas2.height);
  ctx2.beginPath();
  ctx2.moveTo(x, y);
  ctx2.clearRect(x,y,40,40, 2*Math.PI, true); // 切り抜き
  startX = x;
  startY = y;
});

// タッチ終了
$('#child').on('touchend', function(e){
 isDrawing = false;
});           
}

自分で作るスクラッチカードくじ

play.google.com