「さんあ~る」からゴミ捨てカレンダーをスクレイピングする

4月に千葉県柏市のゴミ捨てアプリ「さんあ〜る」が、Web版としてリリースされました。

ごみ分別アプリ「さんあ~る」がインターネットでも利用できます!

さんあ〜るのごみカレンダーをiframeとしてWPに引っ張ってこようと思ったけど拡縮の問題があり挫折。
あと、画像がいらないな、と思ったのでpython3でスクレイピングしてみた。

# coding: UTF-8
import datetime
import urllib.request
import ssl
ssl._create_default_https_context = ssl._create_unverified_context
from bs4 import BeautifulSoup

today = datetime.date.today()
year = today.strftime("%Y")
month = today.strftime("%m")

# アクセスするURL
# XXXXXは地区番号らしい
url = 'https://manage.delight-system.com/threeR/web/calendar?jichitaiId=kashiwashi&areaId=22125&year={year}&month={month}'.format(year=year, month=month)

html = urllib.request.urlopen(url)
soup = BeautifulSoup(html, "html.parser")

table = soup.find_all("table")
cal = []
for tag in table :
    try:
        #import pdb; pdb.set_trace()
        days = tag.find_all("td")  # カレンダーのセル数(7日×週)
        for day in days:
            span = day.find_all('span')
            tmp = []
            if len(span) > 0  :
                for num in range(len(span)):
                    try:
                        if span[num].get("class")[0] in ('common','sat','sun'): 
                            tmp.append(span[0].string)           # 日
                            tmp.append(span[0].get('class')[0])  # 曜

                        elif span[num].get("class")[0] in 'trash_kind_name': # ゴミ種類
                            tmp.append(span[num].string)
                        cal.append(tmp)
                    except:
                        pass
            else : # 空白セル
                cal.append([''])
    except:
        pass

print(cal)

後で、オリジナルのカレンダーの情報に充てがう予定なのでlistに入れる。

結果

[[''], [''], [''], [''], [''], ['1', 'common'], ['2', 'sat'], ['3', 'sun'], ['4', 'common', '可燃ごみ'], ['4', 'common', '可燃ごみ'], ['5', 'common', '不燃ごみ'], ['5', 'common', '不燃ごみ'], ['6', 'common', '容器包装プラスチック類'], ['6', 'common', '容器包装プラスチック類'], ['7', 'common', '可燃ごみ'], ['7', 'common', '可燃ごみ'], ['8', 'common'], ['9', 'sat'], ['10', 'sun'], ['11', 'common', '可燃ごみ'], ['11', 'common', '可燃ごみ'], ['12', 'common'], ['13', 'common', '資源品', '容器包装プラスチック類'], ['13', 'common', '資源品', '容器包装プラスチック類'], ['13', 'common', '資源品', '容器包装プラスチック類'], ['14', 'common', '可燃ごみ'], ['14', 'common', '可燃ごみ'], ['15', 'common'], ['16', 'sat'], ['17', 'sun'], ['18', 'common', '可燃ごみ'], ['18', 'common', '可燃ごみ'], ['19', 'common', '不燃ごみ'], ['19', 'common', '不燃ごみ'], ['20', 'common', '容器包装プラスチック類'], ['20', 'common', '容器包装プラスチック類'], ['21', 'common', '可燃ごみ'], ['21', 'common', '可燃ごみ'], ['22', 'common'], ['23', 'sat'], ['24', 'sun'], ['25', 'common', '可燃ごみ'], ['25', 'common', '可燃ごみ'], ['26', 'common'], ['27', 'common', '資源品', '容器包装プラスチック類'], ['27', 'common', '資源品', '容器包装プラスチック類'], ['27', 'common', '資源品', '容器包装プラスチック類'], ['28', 'common', '可燃ごみ'], ['28', 'common', '可燃ごみ'], ['29', 'common'], ['30', 'sat']]

cal[n][1]の値は、
sat /土曜
sun / 祝祭日
common / 平日
らしい。

fluentdでMySQLにデータを入れる

https://github.com/tagomoris/fluent-plugin-mysqlを使って、fluentd-3.xでmysql 5.7にログを入れる方法のメモ

同時にbigqueryに対してもログを入れているので、@type copyを使う。

<match xxx.yyyy.accesslog>
  @type copy

  # bigquery用
  <store>
    @type             bigquery
    auth_method       json_key
    json_key          PATH/TO/FILE
    project           GCP PROJECT
    dataset           ${tag[0]}
    table             ${tag[1]}_${tag[2]}_%Y%m%d
    auto_create_table true
    schema_path       /etc/td-agent/schema.json

    <buffer tag,time>
      @type file
      path        /var/log/td-agent/buffer/papillon_accesslog
      timekey 1d
      chunk_limit_size 1000000
      queue_limit_length 128
      flush_interval 1
      retry_max_times 17
      retry_wait 1s
    </buffer>
    <inject>
      time_key time
      time_format %Y-%m-%d %H:%M:%S
    </inject>
  </store>

  # MySQL用にTimeをISO8061からDATETIMEに変換する。
  <store>
    @type record_reformer
    output_tag mysql.${tag_suffix[0]}   # tag名に「mysql」を追加
    enable_ruby true     # ruby有効化
    auto_typecast true
    <record>
      time ${require 'time'; Time.parse(record["time"]).strftime("%Y/%m/%d %H:%M:%S")} # TimeをISO8061からDATETIME
    </record>
  </store>
</match>


<match mysql.xxx.yyyy.accesslog>
    @type mysql_bulk
    host 10.254.0.xx
    database TABLE
    username USER
    password PASSWORD
    column_names time,user_id,uri,referer,remote_ip
    key_names time,user_id,uri,referer,remote_ip
    table log
    transaction_isolation_level read_committed    # 2018/5から、デフォルト値がnulになったので、指定しないとトランザクション貼れない。
    flush_interval 1s
</match>

ハマったのは、

transaction_isolation_level read_committed  

の記述の部分。

ここ以外の記述で、td-agentはちゃんと動くが、

2018-06-01 20:30:30 +0900 [warn]: #0 failed to flush the buffer. retry_time=4 next_retry_seconds=2018-06-01 20:30:30 +0900 chunk="56d92e8467c4fab0440db16ee36f0d34" error_class=Mysql2::Error error="You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1"

こんなエラーが出る。

mysql側でgeneral_logを有効にするも、

2018-06-01T20:30:46.022528+09:00           14 Connect   logger@fluentd01 on accesslog using TCP/IP
2018-06-01T20:30:46.023467+09:00           14 Query     SHOW COLUMNS FROM log
2018-06-01T20:30:46.024207+09:00           14 Quit

と、全然有用なログじゃないし。

結局、tcpdumpを取得して見たら、分離レベルを指定せずに 「SET SESSION TRANSACTION ISOLATION LEVEL」を投げている事が原因だった。

解決してよかったー

ansibleでタスクをスキップしても同名のregisterが設定される

ansibleでデプロイするタスクを書いていて遭遇した仕様の話。

やりたいことは、gitのtagやbranch名を引っ張ってきてslackに通知したい。
ステージング環境でのデプロイでも使っているタスクなので、「masterブランチのtag」が入るか、「develop/test」などのブランチが入ってきても適切に通知したい。

最初は、

# branchがmasterの場合、gittagにtag名を入れる
  - name: git status
    shell: git status | head -1 | awk '{print $2}'
    args:
      chdir: "{{ workdir }}/{{ repobase }}/{{ project }}"
    register: branch_name
    changed_when: false

  - name: check tag version
    shell: git status | head -1 | awk '{print $5}'
    args:
      chdir: "{{ workdir }}/{{ repobase }}/{{ project }}"
    register: gittag
    when: branch_name.stdout == "HEAD"
    changed_when: false


# branchがmasterではない場合、gittagにブランチ名を入れる
  - name: check branch
    shell: git status --short --branch | awk '{print $2}'
    args:
      chdir: "{{ workdir }}/{{ repobase }}/{{ project }}"
    register: gittag
    when: branch_name.stdout != "HEAD"
    changed_when: false

# slack送信
  - name : send tag (finish)
    slack:
      token: '{{ slack_token }}'
      msg: "デプロイが開始します\n
            ``` 
            project: {{project}}\n
            env: {{env}}\n
            version: {{ gittag.stdout }}\n
            date: {{ lookup(\"pipe\",\"date +%Y/%m/%d-%H:%M:%S\") }}
            ```"
      channel: '{{ post_channel }}'
      color: good
    run_once: true

と書いたが、branchがmasterで、check branchタスクがskipされているにも関わらず、register: gittagの中身が空っぽになる。
公式を確認すると、

If a task fails or is skipped, the variable still is registered with a failure or skipped status, the only way to avoid registering a variable is using tags.

タスクが失敗したり、スキップしたりした場合も変数は設定される。
らしい。

結局、以下のように修正。

  vars_files:
    - ../vars/vars.yml
  tasks:

# branchがmasterの場合、gittagにtag名を入れる
  - name: git status
    shell: git status | head -1 | awk '{print $2}'
    args:
      chdir: "{{ workdir }}/{{ repobase }}/{{ project }}"
    register: branch_name
    changed_when: false

  - name: check tag version
    shell: git status | head -1 | awk '{print $5}'
    args:
      chdir: "{{ workdir }}/{{ repobase }}/{{ project }}"
    register: gittag
    when: branch_name.stdout == "HEAD"
    changed_when: false


# branchがdevelopではない場合、gittagにブランチ名を入れる
  - name: check branch
    shell: git status --short --branch | awk '{print $2}'
    args:
      chdir: "{{ workdir }}/{{ repobase }}/{{ project }}"
    register: gitbranch
    when: branch_name.stdout != "HEAD"
    changed_when: false

# slack送信
  - name : send tag (finish)
    slack:
      token: '{{ slack_token }}'
      msg: "デプロイを開始します\n
            ``` 
            project: {{project}}\n
            env: {{env}}\n
            version: {{ gittag.stdout }}\n
            date: {{ lookup(\"pipe\",\"date +%Y/%m/%d-%H:%M:%S\") }}
            ```"
      channel: '{{ post_channel }}'
      color: good
    when: branch_name.stdout == "HEAD"
    run_once: true


  - name : send branch (finish)
    slack:
      token: '{{ slack_token }}'
      msg: "デプロイを開始します\n
            ``` 
            project: {{project}}\n
            env: {{env}}\n
            version: {{ gitbranch.stdout }}\n
            date: {{ lookup(\"pipe\",\"date +%Y/%m/%d-%H:%M:%S\") }}
            ```"
      channel: '{{ post_channel }}'
      color: good
    when: branch_name.stdout != "HEAD"
    run_once: true

上手い書き方無いかなぁ

CentOS7のファイルディスクリプタの設定

確認方法

cat /proc/`pgrep -f サービス名`/limits

Limit                     Soft Limit           Hard Limit           Units     
Max cpu time              unlimited            unlimited            seconds   
Max file size             unlimited            unlimited            bytes     
Max data size             unlimited            unlimited            bytes     
Max stack size            8388608              unlimited            bytes     
Max core file size        0                    unlimited            bytes     
Max resident set          unlimited            unlimited            bytes     
Max processes             29222                29222                processes 
Max open files            1024                1024                files     
Max locked memory         65536                65536                bytes     
Max address space         unlimited            unlimited            bytes     
Max file locks            unlimited            unlimited            locks     
Max pending signals       29222                29222                signals   
Max msgqueue size         819200               819200               bytes     
Max nice priority         0                    0                    
Max realtime priority     0                    0                    
Max realtime timeout      unlimited            unlimited            us        

変更方法

/usr/lib/systemd/system/サービス管理ファイル に、

LimitNOFILE=65536

を追加する。

追加した跡は、

systemctl daemon-reload
systemctl restart daemon

として、サービスを再起動する。

pecoで遊んでみる

Twitterでやり取りさせて貰っているShu1さんの記事が面白かったので、GCEで再現してみた
http://blog.jicoman.info/2018/04/ec2-ssh-using-peco/

※ gcloudの設定が終わっている事前提

pecoインストール

wget https://github.com/peco/peco/releases/download/v0.5.3/peco_linux_amd64.tar.gz

tar zxvf peco_linux_amd64.tar.gz
mv peco_linux_amd64/peco /usr/local/bin/
rm -rf peco_linux_amd64*

関数設定

vim ~/.bash_profile

# ssh簡単にするやつ
function ssh-gce() {
  local user="root"
  local host=$(gcloud compute instances list | grep stg | awk '{print $1,$3,$4}' | column -t -s" " | /usr/local/bin/peco | awk '{print $3}')
  ssh "$user@$host"
}

修正後に、

source ~/.bash_profile

出来たー!

ドメイン移管のエラー対応

他サービスで取得したドメインを、お名前.comに移管する際、一筋縄ではいかない。

まず拒否理由の内容

審査による拒否
詳細は下記内容をご確認ください。
拒否理由:[Whois情報に正確な情報の記載がございませんため、弊社審査において不受理とさせていただきました。  現ドメイン管理会社にて、Whois情報を正確な情報へご変更のうえ、再度お手続きをお願いいたします。]

正しい情報を住所をローマ字読みで入力しているのに、なんで!?

と、なる事が多いのでまとめておく。
先に言うと、『日本語の解釈と違う内容を求められる』事が最大の原因だと思う。

例として、東京都港区の区役所の住所「〒105-8511 東京都港区芝公園1丁目5番25号」を展開してみる。

項目 日本語 英語 説明
国(Country) 日本 JP ここは迷わない
郵便番号(ZIP/Postal code) 105-8511 105-8511 ハイフンあり、なしはどちらでもいい
都道府県(state) 東京都 Tokyo ここも迷わない
市区町村(city) 港区 Minato-ku 「市区町村」なので、「Minato-ku Shibakouen」と描きたいが、ここは英語でcityの場所の為、「Minato-ku」まで
番地(street) 芝公園1丁目5番25号 1-5-25, Shibakouen 番地って「1-5-25」だけじゃないの?と思うが、書き方は「番地 ,(カンマ) 町名」になる

他に電話番号やメールアドレスも登録する必要があるが、
それは迷わないと思うから省略。

これを適切に理解していれば、一発でドメイン移管が行える!

pop2imapを使ってメールデータを移行する

お客さんの環境(さくらレンタルサーバー)から、自前で構築したIMAPSの環境にメールデータを移行したいと話があった。

色々探して、perlのpop2imapってツールを見つけた。
これならサーバーにSSHログイン出来ない環境でも、IMAPかPOP3が動いていればデータの移行が簡単に出来る。

wget http://www.linux-france.org/prj/pop2imap/dist/pop2imap-1.27.tgz

tar zxvf pop2imap-1.27.tgz

cd pop2imap-1.27/

INSTALLを見ると、必要がモジュールが書かれている。

You need : 
- Perl 
  try : perl -v
  try : perl -c pop2imap

- Mail::IMAPClient module 
  try : perl -mMail::IMAPClient -e ''

- Mail::POP3Client module 
  try : perl -mMail::POP3Client -e ''

- Email::Simple module
  try : perl -mEmail::Simple -e ''

- Date::Manip module 
  try : perl -mDate::Manip -e ''

- IO::Socket::SSL  module (optional needed with --ssl1 or --ssl2)
  try : perl -mIO::Socket::SSL -e ''

Any software packager should add all the perl modules dependencies by default
since users can use any option (--ssl*).

これらは入っていなければyumなりCPAMなりでインストールする。
yumの場合は

yum -y install \
perl-Mail-POP3Client.noarch \
perl-Mail-IMAPClient.noarch \
perl-Mail-POP3Client.noarch \
perl-Date-Manip.noarch \
perl-IO-Socket-SSL.noarch

モジュールを入れたら、makeする。

make
make install  # /usr/bin/pop2imap にインストールされる

オプションは直感的に分かるけど、パスワードを平文で打つので、作業後はhistory消しておいた方が良さそう。

pop2imap --help

usage: /usr/bin/pop2imap [options]

Several options are mandatory. See the example below.

--from        <string> : parsed as <user1>@<host1>[:<port1>]
--host1       <string> : "from" POP server. Mandatory.
--port1       <int>    : port to connect. Default is 110 (ssl:995).
--user1       <string> : user to login.   Mandatory.
--password1   <string> : password for the user1. Dangerous, use --passfile1
--passfile1   <string> : password file for the user1. Contains the password.
--ssl1                 : enable ssl on POP connect
--to          <string> : parsed as <user2>@<host2>[:<port2>][/<folder>]
--host2       <string> : "destination" IMAP server. Mandatory.
--port2       <int>    : port to connect. Default is 143 (ssl:993).
--user2       <string> : user to login.   Mandatory.
--password2   <string> : password for the user2. Dangerous, use --passfile2
--passfile2   <string> : password file for the user2. Contains the password.
--ssl2                 : enable ssl on IMAP connect
--starttls2            : use starttls on IMAP connect instead of SSL
--timeout2    <int>    : Connections timeout in seconds. Default is 240.
--folder      <string> : sync to this IMAP folder.
--delete               : delete messages in "from" POP server after
                         a successful transfer. useful in case you
                         want to migrate from one server to another one.
                         They are really deleted when a QUIT command
                         is send.
--idatefromheader      : sets the internal dates on host2 same as the 
                         "Date:" headers from host1. Turned on by default.
--dry                  : do nothing, just print what would be done.
--debug                : debug mode.
--debugimap            : IMAP debug mode.
--debugpop             : POP debug mode.
--tests                : Run non-regression tests
--quiet                : Only print error messages
--version              : print sotfware version.
--help                 : print this message.

Example: to synchronise pop  account "foo" on "pop3.truc.org"
                     to imap account "bar" on "imap.trac.org"

/usr/bin/pop2imap \
   --host1 pop3.troc.org --user1 foo --passfile1 /etc/secret1 \
   --host2 imap.trac.org --user2 bar --passfile2 /etc/secret2


Branched by Phil Carmody <phil.carmody@partner.samsung.com> from:
 $Id: pop2imap,v 1.27 2015/11/03 23:34:02 gilles Exp gilles $ 
      pop2imap copyleft is the GNU General Public License.

使い方

pop2imap --host1 移行元サーバー --user1 移行元ユーザー名 --password1 移行元パスワード --host2 移行先サーバー --user2 移行先ユーザー名 --password2 移行先パスワード -ssl2(IMAPSで接続)

おまけ
historyを削除する

history -c

EC2にswapを増やす

訳あってEC2のt2.nano(メモリ 0.5GB)でCPAMを使いたいけど、CPAMだけで300MB近くメモリを食うので、isntall処理がAbortしてしまう。

#  perl -MCPAN -e shell
Terminal does not support AddHistory.

cpan shell -- CPAN exploration and modules installation (v2.16)
Enter 'h' for help.

cpan[1]> install Mail::IMAPClient
Reading '/root/.cpan/sources/authors/01mailrc.txt.gz'
............................................................................DONE
Reading '/root/.cpan/sources/modules/02packages.details.txt.gz'
  Database was generated on Tue, 10 Apr 2018 14:54:48 GMT
....................................................Killed

遅くてもinstallが出来ればいいので、SWAP付けて逃げる。

# free -m
              total        used        free      shared  buff/cache   available
Mem:            483         174         222          32          86         245
Swap:             0           0           0  ← SWAPがない

/var/swapfileを作成して、SWAPに当てる

# dd if=/dev/zero of=/var/swapfile bs=1M count=1024
1024+0 records in
1024+0 records out
1073741824 bytes (1.1 GB) copied, 15.0153 s, 71.5 MB/s

# mkswap /var/swapfile
mkswap: /var/swapfile: insecure permissions 0644, 0600 suggested.
Setting up swapspace version 1, size = 1024 MiB (1073737728 bytes)
no label, UUID=2311e423-13a4-4451-b081-05ac4074c8bf

# swapon /var/swapfile
swapon: /var/swapfile: insecure permissions 0644, 0600 suggested.

# free -m
              total        used        free      shared  buff/cache   available
Mem:            483         175           5          32         302         244
Swap:          1023           0        1023  ← SWAP増えた!

Google Cloud Storageを公開する

ACLの確認

gsutil acl get gs://バケット名

レスポンス
[
  {
    "entity": "project-owners-xxxxxxxxxxx",
    "projectTeam": {
      "projectNumber": "xxxxxxxxxxx",
      "team": "owners"
    },
    "role": "OWNER"
  },
  {
    "entity": "project-editors-xxxxxxxxxxx",
    "projectTeam": {
      "projectNumber": "xxxxxxxxxxx",
      "team": "editors"
    },
    "role": "OWNER"
  },
  {
    "entity": "project-viewers-xxxxxxxxxxx",
    "projectTeam": {
      "projectNumber": "xxxxxxxxxxx",
      "team": "viewers"
    },
    "role": "READER"
  },
  {
    "entity": "allUsers",
    "role": "READ"
  }
]

全ユーザーにデータ書き込みを許可する

gsutil acl ch -u AllUsers:R gs://バケット名

レスポンス
Updated ACL on gs://バケット名/

全ユーザーにデータ読み込みを許可する

gsutil acl ch -u AllUsers:W gs://バケット名

レスポンス
Updated ACL on gs://バケット名/