Noto Serif(源ノ明朝)のCSS指定
CSS HTML font NotoSans NotoSerif
はじめに
最近提供されたばかりのNoto Serif(源ノ明朝)を試してみた。 まだCDN提供されていないので、現状はfont-faceでローカルファイルを指定しなければいけない。
フォントのダウンロード
https://www.google.com/get/noto/#serif-jpan からフォントをダウンロードして、fonts/NotoSerifCJKjp/などのディレクトリにファイルを展開する
ソースコードの記述
CSSなどに下記ソースコードを記述する srcのディレクトリは環境に合わせて変更する
@font-face { font-family: 'Noto Serif Japanese'; font-style: normal; font-weight: 100; src: url(../fonts/NotoSerifCJKjp/NotoSerifCJKjp-ExtraLight.otf) format('opentype'); } @font-face { font-family: 'Noto Serif Japanese'; font-style: normal; font-weight: 200; src: url(../fonts/NotoSerifCJKjp/NotoSerifCJKjp-Light.otf) format('opentype'); } @font-face { font-family: 'Noto Serif Japanese'; font-style: normal; font-weight: 400; src: url(../fonts/NotoSerifCJKjp/NotoSerifCJKjp-Regular.otf) format('opentype'); } @font-face { font-family: 'Noto Serif Japanese'; font-style: normal; font-weight: 500; src: url(../fonts/NotoSerifCJKjp/NotoSerifCJKjp-Medium.otf) format('opentype'); } @font-face { font-family: 'Noto Serif Japanese'; font-style: normal; font-weight: 600; src: url(../fonts/NotoSerifCJKjp/NotoSerifCJKjp-SemiBold.otf) format('opentype'); } @font-face { font-family: 'Noto Serif Japanese'; font-style: normal; font-weight: 700; src: url(../fonts/NotoSerifCJKjp/NotoSerifCJKjp-Bold.otf) format('opentype'); } @font-face { font-family: 'Noto Serif Japanese'; font-style: normal; font-weight: 900; src: url(../fonts/NotoSerifCJKjp/NotoSerifCJKjp-Black.otf) format('opentype'); } body{ font-family: 'Noto Serif Japanese', serif; } ```
Amazonの商品を文字列検索と切り取りでスクレイピングする
概要
Slackで商品名を投稿するとAmazonの商品を返すBotを作るために作成。 面倒だったので、APIもXMLパースライブラリも、正規表現も使わずに文字列検索と切り取りのみでのスクレイピング。
DOMの構造が変わったらこのプログラムは終わり。 商品によっては発生するバグは修正予定。
ソースコード
<?php if(!isset($_GET['keyword'])){ echo 'URL?keyword=検索キーワード'; die(); } //ソースの取得 $source = file_get_contents('https://www.amazon.co.jp/s/?url=search-alias%3Daps&field-keywords=' . urlencode($_GET['keyword'])); $item = subStrHtml($source, 'id="result_1"', 'id="result_2"'); //各項目の抜き出し $title = subStrHtml($item, 's-access-title a-text-normal">', '</h2>'); $link = subStrHtml(subStrHtml($item, 'a-text-normal" target="_blank"', '</a>'), 'href="', '"><img '); $image = subStrHtml($item, '<img src="', '" width="160"'); $price = subStrHtml($item, 'a-color-price s-price a-text-bold">', '</span>'); $star = subStrHtml($item, '<span class="a-icon-alt">5つ星のうち ', '</span>'); $prime = subStrHtml($item, '<span class="a-icon-alt">', '</span>'); //出力 echo '<img src="' . $image . '">' . '<br>'; echo '<a href="' . $link . '">' . $title. '</a>' . '<br>'; echo '価格: ' . $price . '<br>'; echo '★' . $star . '<br>'; echo $prime . '<br>'; function subStrHtml($data, $startStr, $endStr){ $start = mb_strpos($data, $startStr) + mb_strlen($startStr); $end = mb_strpos($data, $endStr, $start); $result = mb_substr($data, $start, $end - $start); return $result; }
実行結果
各種スクレイピングのために、レンダリング後(JavaScript実行後)のHTMLを出力するAPIを作る
api PhantomJS JavaScript GoogleAppsScript PHP
はじめに
スクレイピングをしようとして、いざJavaScriptやPHPやGoogle Apps ScriptでHTMLの取得をすると、JavaScript実行前のソースが取得されてしまう。Seleniumなどを使えばいいが、環境を構築したり起動させっぱなしにしなければいけないので、コストやハードルが高いと感じる。 なのでレンダリング後のHTMLを取得してくれるAPIがあれば様々なプログラムから参照でき、スクレイピングが捗るのではないかと思う。 自分はGoogle Apps Scriptで株価を取得しようとしたところ、参照先がAjaxで株価を動的に生成していたため、スクレイピングできなかっためこのようなAPIが欲しいと思った。 今回は、簡単に実装したかったので、コマンドラインブラウザとしてPhantomJS、API実装としてPHPを使用した。
やりたいこと
開発環境
実装の手順
CentOSにHttpdとPHPはインストールされている前提とする
PhantomJSのインストール
Macの場合はbrew installで簡単にインストールできる
$ sudo yum install epel-release $ sudo rpm -ivh http://repo.okay.com.mx/centos/6/x86_64/release/okay-release-1-1.noarch.rpm $ sudo yum search all phantomjs
PhantomJSでレンダリング後のHTMLを取得する
作業ディレクトリ(/var/www/html/rendered-html/など)に以下のファイルを作成する
下記のソースコードでは、 1. 第二引数で指定したURLの取得 2. ページを取得 3. ページの評価(evalute)を行い、レンダリング後のhtmlタグ内のソースを取得 4. htmlをコマンドラインに出力する を行なっている。
var page = require('webpage').create(); var system = require('system'); var args = system.args; var url = args[1]; if (url == undefined) { phantom.exit(); } page.open(url, function(status) { if (status === 'success') { var body = page.evaluate(function() { return '<html>' + document.getElementsByTagName('html')[0].innerHTML + '</html>'; }); console.log(body); } phantom.exit(); });
上記のソースコードを実行すると、HTMLが取得できる
$ phantomjs phantomjs-get-html.js http://google.com <html><head><meta content="世界中のあらゆる情報を検索するためのツールを提供しています。さまざまな検索機能を活用して、お探しの情報を見つけてください。" name="descrip...
PHPからPhantomJSを実行する
PHPのPhantomJSを使うという方法もあったが、Composerが多少コストが高いため使用せず execでPhantomJSを実行し、最終的にJSON形式でHTMLを出力している
<?php header('content-type: application/json; charset=utf-8'); //URLの取得 URLがない場合エラー if(!isset($_GET['url'])){ statusFailure(); } $url = $_GET['url']; //PhantomJSの実行 $cmd = 'phantomjs phantomjs-get-html.js ' . $url; exec($cmd, $arr); //データがない場合エラー if(!$arr){ statusFailure(); } //文字列結合 $html = ''; foreach($arr as $val){ $html .= $val; } //JSONにてHTMLを出力 echo json_encode([ 'status' => 'success', 'html' => $html ]); //エラー出力関数 function statusFailure(){ echo json_encode([ 'status' => 'failure', 'html' => '' ]); die(); }
実行結果
ブラウザにて、http://IPアドレス/rendered-html/?url=http://google.com にアクセスする 正常に取得できれば、successがかえってきて、HTMLが取得できる
{"status":"success","html":"<html><head><meta content=\"\u4e16\u754c...
おわりに
クライアントJavaScriptやGASでもレンダリング後のスクレイピングができるようになったので、やれることが多くなった。今後はクリックやDOM操作もできるAPIに発展させていきたい。
コマンドラインでURLエンコードをする (他コマンド未使用)
URLEncode URL Terminal Bash alias
はじめに
たまにURLエンコードが必要な時があるときは、検索エンジンを使いエンコードしていたが、ターミナルで変換したいと思い、いざターミナルでやってみようとするとnkfというコマンドが必要になることが分かった。インストールするのはしゃくなので、他のコマンドを使わずにforなどを駆使しURLエンコードするaliasを作成。
ソースコード
forにより一文字ずつ、変換対象の文字かそれ以外かを判定して、文字列結合して出力する
導入
~/.bash_profileなどに下記コードを追記をする
function url-encode() { local input="${1}" local output="" ##一文字ずつURL形式に変換 local i=0 for (( i=0 ; i<${#input} ; i++ )); do local o="" local c=${input:$i:1} case "$c" in [-_.~a-zA-Z0-9] ) o="${c}" ;; ##変換せず * ) printf -v o '%%%02x' "'$c" ##URL形式に変換 esac output+=$o done echo $output } alias url-encode=url-encode
コマンドの使用例
url-encode ‘エンコード対象’
$ url-encode '$$url##encode@@‘ %24%24url%23%23encode%40%40
ターミナルから簡単にSlackに投稿するエイリアス
Slack Bash Terminal alias bashrc
実行結果
ソースコード
メッセージをURLエンコードをかけ、curlにてslackに投稿する。
function param-url-encode() { local input="${1}" local output="" local i=0 for (( i=0 ; i<${#input} ; i++ )); do local o="" local c=${input:$i:1} case "$c" in [-_.~a-zA-Z0-9] ) o="${c}" ;; * ) printf -v o '%%%02x' "'$c" esac output+=$o done echo $output } function post-slack(){ local token="xoxp-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" ##Slackのアクセストークン local message=`param-url-encode $3` ##メッセージをURLエンコード curl -w "%{http_code}\n" "https://slack.com/api/chat.postMessage?token=${token}&channel=${1}&username=${2}&text=${message}" ##curlで送信する } alias post-slack=post-slack
エイリアスの導入方法
エイリアスの使用例
$ post-slack general username message #post-slack チャンネル ユーザー名 メッセージ {"ok":true,"channel":"C0R3AKR8","ts":"149087635.112142","message":{"text":"message","username":"username","bot_id":"B4DCDQML","type":"message","subtype":"bot_message","ts":"149076935.112142"}}200
人生の残り時間と一年の残り時間を定期的にSlackに通知する
GoogleAppsScript Slack slackbot JavaScript ライフハック
プログラムについて
人生の時間を大切にする為に、来年までの残りの時間日数月数と、人生の時間日数月数年数を定期的に通知する。また意識を高めるためにタイマーを8時間に設定。
ソースコードの使い方
- 時間操作はMomentのライブラリを導入 (プロジェクトキー: MHMchiX6c1bwSqGM1PZiW_PxhMjh3Sh48)
- 誕生日(birthday)と予想寿命(lifespan)を入力する
- xoxp-xxxxxxxxxxxxxxxxxxxxxxにはSlackのAccessTokenを入力する
- lifespanチャンネルを事前に作成しておく
- トリガーを8時間に設定して、定期的に通知する
function myFunction() { var birthday = '1980/1/1'; var lifespan = 80; var now = Moment.moment(); var end = Moment.moment(birthday).add('years', lifespan); var nextYear = Moment.moment(Moment.moment().add('years', 1).get('year') + '/1/1'); var message = ''; message += '来年までの残り時間はあと *' + -now.diff(nextYear, 'hours') + '時間* です\n'; message += '来年までの残り日数はあと *' + -now.diff(nextYear, 'days') + '日* です\n'; message += '来年までの残り月数はあと *' + -now.diff(nextYear, 'month') + 'ヶ月* です\n\n'; message += '人生の残り時間はあと *' + -now.diff(end, 'hours') + '時間* です\n'; message += '人生の残り日数はあと *' + -now.diff(end, 'days') + '日* です\n'; message += '人生の残り月数はあと *' + -now.diff(end, 'month') + 'ヶ月* です\n'; message += '人生の残り年数はあと *' + -now.diff(end, 'years') + '年* です\n'; message += '--------------------'; postSlack('xoxp-xxxxxxxxxxxxxxxxxxxxxx', 'lifespan', 'lifespan-bot', message); } function postSlack(token, channel, username, text) { var url = 'https://slack.com/api/chat.postMessage?token=' + token + '&channel=' + channel + '&username=' + username + '&text=' + encodeURIComponent(text); response = UrlFetchApp.fetch(url).getContentText("UTF-8"); }
おわりに
通知を見るたび、一年と人生の残り時間はとても短いことに気づかされる。
QiitaのContribution数が増えるたびにSlackに通知する
Qiita Slack slackbot GoogleAppsScript JavaScript
概要
環境はGoogle Apps Scriptを使用。
事前準備
SlackのAPIキーを取得する
https://api.slack.com/custom-integrations/legacy-tokens Appを登録するとxoxp-xxxxxxxxxxxxxxxxxxxxのようなコードが発行されるので、ソースコード内のslackTokenに設定する
Slackにチャンネルを追加する
Slackにて、qiita-contributionのチャンネルを新規で追加する
ライブラリにSlackAppを追加する
Slackを使用するためにsoundTrickerさんの以下のライブラリを導入する http://qiita.com/soundTricker/items/43267609a870fc9c7453 Library Keyをコピーして、リソース-ライブラリ-ライブラリを追加に入力する 今回はバージョン22を使用
取得したいユーザーのアカウント名を設定する
ソースコード内のuserNameにContributionを取得したいユーザーアカウント名を設定する
トリガーの設定
プロジェクトのトリガーを時間主導型で1分に設定する
ソースコード解説
今回はQiitaAPIを使わずに、簡易的な正規表現でのスクレイピングでContributeを取得 1. Qiitaのユーザーページをフェッチ 2. ユーザーページHTMLのContributeに該当する箇所のみ正規表現で抜き出し 2. Slackの以前の最後のメッセージと、今回の送信するメッセージを比較 3. 以前のメッセージと違ければ(Contributionが増えて入れば)新しいContribution数を通知する
function myFunction() { //各種設定 var userName = '_RJ'; var slackToken = 'xoxp-xxxxxxxxxxxxxxxxxxxx'; var slackChannel = 'qiita-contribution'; var slackUserName = 'contributin-bot'; var additionalStartMessage = 'QiitaのContribution数が' var additionalEndMessage = 'に増えました!' //Contributionの取得 var html = UrlFetchApp.fetch('http://qiita.com/' + userName).getContentText(); var contribution = /contributions\"><span class=\"userActivityChart_statCount\">([\s\S]*?)<\/span>/i.exec(html)[1].replace(/(^\s+)|(\s+$)/g, "") //Logger.log(contribution); //以前のメッセージと比較してContributionが更新されていれば投稿する var message = additionalStartMessage + contribution + additionalEndMessage; var lastMessage = getSlackMessage(slackToken, slackChannel)[0].text; if(lastMessage != message){ postSlackMessage(slackToken, slackChannel, slackUserName, message); }else{ //Logger.log('Duplicated'); } } function postSlackMessage(accessToken, channelName, userName, message){ //Get channels var slackApp = SlackApp.create(accessToken); var channels = slackApp.channelsList().channels; //Find channel by channel name var channel = null; channels.forEach(function(v, i){ if(v.name == channelName){ channel = v; } }); //Post Message to slack slackApp.postMessage(channel.id, message, { username : userName }); } function getSlackMessage(accessToken, channelName){ //Get channels var slackApp = SlackApp.create(accessToken); var channels = slackApp.channelsList().channels; //Find channel by channel name var channel = null; channels.forEach(function(v, i){ if(v.name == channelName){ channel = v; } }); //Get Message at slack return slackApp.channelsHistory(channel.id).messages; }