人生シーケンスブレイク

シーケンスブレイク(Sequence breaking、シークエンスブレイクとも)とは、テレビゲームにおいて開発が想定している攻略ルートを逸脱し、ショートカットする行為のことである。

urllib.request.build_opener に User-agent を指定する

Pythonで要ログインのサービスをスクレイピングをする際にはbuild_openerを利用するが、User-agentも指定する必要があるケースがある。

通常のbuild_opener

# coding: utf-8
import http.cookiejar
import yaml
from bs4 import BeautifulSoup
from urllib import parse, request


def main():
    # yaml
    conf = yaml.load(open('conf.yaml').read())

    # login
    opener = request.build_opener(request.HTTPCookieProcessor(http.cookiejar.CookieJar()))
    post = {
        'username': conf['username'],
        'password': conf['password']
    }
    query = urllib.parse.urlencode(post).encode('utf-8')
    opener.open(conf['login_url'], query)

    # 以下スクレイピング処理など...
    soup = BeautifulSoup(opener.open(conf['scrap_url']).read().decode('utf-8'), 'html.parser')

もしUser-agentの指定が必要な場合には以下のようにするとよい。

# coding: utf-8
import http.cookiejar
import yaml
from bs4 import BeautifulSoup
from urllib import parse, request


def main():
    # yaml
    conf = yaml.load(open('conf.yaml').read())

    # login
    opener = request.build_opener(request.HTTPCookieProcessor(http.cookiejar.CookieJar()))
    opener.addheaders = [('User-agent', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.73 Safari/537.36')]
    post = {
        'username': conf['username'],
        'password': conf['password']
    }
    query = urllib.parse.urlencode(post).encode('utf-8')
    opener.open(conf['login_url'], query)

    # 以下スクレイピング処理など...
    soup = BeautifulSoup(opener.open(conf['scrap_url']).read().decode('utf-8'), 'html.parser')

解説

urllib.request.build_opener()で作成したインスタンスOpenDirectorオブジェクト と呼ばれるものであり、urllib.request.urlopen()と引数、返り値が一緒である。*1 加えて、addheaders()でOpenDirectorインスタンスにヘッダーを指定できるので、こちらを利用してUser-agentを指定すればよい。

公式にも以下のURLに例が載っているので、こちらを参考にすると他の用途も掲載されている。 http://docs.python.jp/3.4/library/urllib.request.html#examples

*1:urlopen()はグローバルな OpenerDirector の open() メソッドを呼び出しているにすぎない。

Bowerを使ってフロントエンドのパッケージ管理を行う

先行きが若干不安だけど。 Bower is alive, looking for contributors · Bower blog

npm はpackage.jsonがカオスになっている為開発用ライブラリのパッケージ管理のみ、BowerはBootstrapなどのフロントエンドのライブラリパッケージ管理で使い分けしている。

Bower インストール

$ npm install bower

Bower によるパッケージインストール

$ bower install bootstrap

デフォルトでは bower_components/ ディレクトリにインストールされる。 bower_components/ を.gitignoreに追加しておくとgitリポジトリに外部ライブラリの歴史が残らず綺麗に管理できる。

bower.jsonの作成

$ bower init

コマンドを叩き、npm init 感覚で幾つかの質問に答えていくとbower.jsonが生成される。

bower.jsonに記述されたライブラリのインストール

$ bower install

インストール先の変更

.bowerrc に以下のように記述すると良い。

{
  "directory": "static/vendor",
  "json": "bower.json"
}

まとめ

Bowerを使う際には以下のファイルを追加する。

$ tree -a
.
├── .bowerrc # パッケージインストール先の指定
├── .gitignore # パッケージインストール先の除外設定を追加
└── bower.json # パッケージ管理ファイル

CSSセレクタ42個ぜんぶ纏めてみた

Sassにせよcssにせよ、出来る限り限定的にCSSセレクタを指定できるといいよねってことで、W3C記載の表に基づいてCSSセレクタを纏めてみた。
なお、CSS3セレクタに関するW3Cのテクニカルレポートとして Selectors Level 3 が著名だが、 2015年時点のCSS情報を纏めた CSS Snapshot 2015CSS WG members によって作成されているのでこちらの情報を参考としている。*1

ブラウザ互換についてだが、まず What's my IP Address? What's my browser? のCSS3 Selectorsの項を見る限り、IE10で全セレクタ対応されている。
そして、IE10以下はVista IE9を除き2016年1月12日(米国時間)にサポート終了する。
Internet Explorerサポートポリシー変更の重要なお知らせ - Microsoft

Vista IE9はシェアがかなり少ないことから、実質ブラウザ互換を考える必要はなさそうである。
IE6に苦しまれたブラウザ互換については、CSSに関しては今後はほぼ考えずに済みそうだ。(たぶん)

CSSセレクタ42個まとめ

セレクタパターン セレクタの意味 セレクタが最初に定義されたCSS Level
* すべての要素 全称セレクタ 2
E E要素 セレクタ 1
E[foo] "foo"属性を持つE要素 属性セレクタ 2
E[foo="bar"] "foo"属性の値が"bar"と等しいE要素 属性セレクタ 2
E[foo~="bar"] "foo"属性の値がスペース区切りのリストで、そのいずれかが"bar"と等しいE要素 属性セレクタ 2
E[foo^="bar"] "foo"属性の値が"bar"で始まるE要素 属性セレクタ 3
E[foo$="bar"] "foo"属性の値が"bar"で終わるE要素 属性セレクタ 3
E[foo*="bar"] "foo"属性の値が"bar"を含むE要素 属性セレクタ 3
E[foo|="en"] "foo"属性の値がハイフン区切りのリストで、"en"はじまりであるE要素 属性セレクタ 2
E:root ドキュメントルートであるE要素 構造模擬クラス 3
E:nth-child(n) n番目の子要素であるE要素 構造模擬クラス 3
E:nth-last-child(n) 最後から数えてn番目の子要素であるE要素 構造模擬クラス 3
E:nth-of-type(n) n番目の兄弟要素であるE要素 構造模擬クラス 3
E:nth-last-of-type(n) 最後から数えてn番目の兄弟要素であるE要素 構造模擬クラス 3
E:first-child 最初の子要素であるE要素 構造模擬クラス 2
E:last-child 最後の子要素であるE要素 構造模擬クラス 3
E:first-of-type 最初の兄弟要素であるE要素 構造模擬クラス 3
E:last-of-type 最後の兄弟要素であるE要素 構造模擬クラス 3
E:only-child 他に子要素を持たないE要素 構造模擬クラス 3
E:only-of-type (兄弟要素のうち、)他に同一要素を持たないE要素 構造模擬クラス 3
E:empty (テキストノードも含めて)空のE要素 構造模擬クラス 3
E:link 未閲覧のハイパーリンクを持つE要素 リンク擬似クラス 1
E:visited 閲覧済みのハイパーリンクを持つE要素 リンク擬似クラス 1
E:active アクティブなE要素 ユーザアクション擬似クラス 1と2
E:hover ホバー中のE要素 ユーザアクション擬似クラス 1と2
E:focus フォーカス中のE要素 ユーザアクション擬似クラス 1と2
E:target 参照URIのターゲットであるE要素 ターゲット擬似クラス 3
E:lang(fr) 言語"fr"のE要素 :lang()擬似クラス 2
E:enabled enabled属性であるE要素 UI要素状態擬似クラス 3
E:disabled disabled属性であるE要素 UI要素状態擬似クラス 3
E:checked checked属性であるE要素 UI要素状態擬似クラス 3
E::first-line E要素の最初の行 ::first-line擬似要素 1
E::first-letter E要素の最初の文字 ::first-letter擬似要素 1
E::before E要素の前に生成されるコンテンツ ::before擬似要素 2
E::after E要素の後に生成されるコンテンツ ::after擬似要素 2
E.warning "warning"クラスを持つE要素 クラスセレクタ 1
E#myid ID"myid"を持つE要素 IDセレクタ 1
E:not(s) 単純セレクタsにマッチしないE要素 否定擬似クラス 3
E F E要素の子孫であるF要素 子孫結合子 1
E > F E要素の子であるF要素 子結合子 2
E + F E要素の直後にあるF要素 隣接兄弟結合子 2
E ~ F E要素の後ろにあるF要素 一般兄弟結合子 3

解説

E F, E > F, E + F, E ~ F などは皆知ってると思うので、CSS Level3で定義されたセレクタを中心に解説する。

E[foo="bar"], E[foo~="bar"]

既にCSSを利用している人は使ったことがあると思うので注意点のみ。
例えば、

<header class="navbar navbar-static-top bs-docs-nav">

の場合、header[class="navbar"] はヒットしない。header[class~="navbar"] はヒットする。
これは定義通り、E[foo="bar"]が単一要素と同一な場合に適用されるセレクタであり、E[foo~="bar"]は複数要素のなかに該当要素が含まれる場合に適用されるセレクタだからである。

Bootstrapをはじめ、マルチクラス設計で作られたCSS Frameworkや、jQueryを利用する場合には複数のクラスを持つケースが多いので、classを指定する場合には、素直に header.navbar を利用した方が良いだろう。

構造擬似クラスの幾つかを解説

E[foo^="bar"], E[foo$="bar"]

"bar"で始まる、または"bar"で終わる要素にマッチする。

利用例

a[href^="#"] {background-color:gold}
a[href$=".cn"] {color: red;}

と指定すると、ページ内リンクを持つa要素にのみ背景色を金にしたり、.cnドメインへのリンクのみ赤字にしたりできる。

属性セレクター - CSS: カスケーディングスタイルシート | MDN

E:nth-child(n), E:nth-last-child(n)

n番目の子要素であるE要素にマッチする。

<div>
  <p>1</p>
  <p>2</p>
  <p>3</p>
</div>

に対して p:nth-child(2) と指定すると<p>2</p>がマッチする。
nth-last-childは最後から数えたn番目の要素とマッチする。

E:nth-of-type(n), E:nth-last-of-type(n)

n番目のE要素にマッチする。

<div>
  <p>1</p>
  <p>2</p>
  <span></span>
  <p>3</p>
</div>

p:nth-of-type(3) と指定すると、<p>3</p>がマッチする。
ちなみに、括弧内に(odd)(even) を指定すると奇数/偶数番目にマッチする。
実は :nth-child(2n) で偶数や :nth-child(3n+1) で3の倍数+1にマッチできたりもする。

Selectors Level 3 に仕様が書いてあるが

:nth-child( +3n - 2 )
:nth-child( -n+ 6)

とか書いてあってなかなかキモい。

E:first-child, E:last-child, E:first-of-type, E:last-of-type

*-child は最初(後)の子要素であるE要素にマッチする。
*-of-type は兄弟要素の内、最初(後)のE要素にマッチする。

<div>
  <p>1</p>
  <p>2</p>
  <span></span>
  <p>3</p>
</div>
<div>
  <p>4</p>
  <p>5</p>
  <span></span>
  <p>6</p>
</div>

p:first-childであれば、 <p>1</p><p>4</p> がマッチする。
p:first-of-typeであれば、 <p>1</p><p>4</p> がマッチする。

ただしこの場合、
span:first-childは、ここではspanは最後の子要素では無いためいずれの要素もマッチせず、
span:first-of-typeであれば、 <span>あ</span><span>い</span> がマッチする。

*-child が子要素全体の中からの検索であり、*-of-typeは該当要素のみの検索であることを考慮する必要がある。

E:only-child, E:only-of-type

E:only-child は唯一の子要素である場合にのみE要素がマッチする。
E:only-of-type は兄弟要素のうち、他にE要素が存在しない場合にのみE要素がマッチする。

<div>
  <p>1</p>
  <p>2</p>
  <span></span>
  <p>3</p>
</div>

の場合、span:only-child は該当なし、 span:only-of-type<span>あ</span> がマッチする。

否定擬似クラスの解説

E:not(s)

単純セレクタsにマッチしないE要素にマッチする。

<li class='active'>1</li>
<li>2</li>
<li>3</li>

の場合、li:not(.active)とすると、 <li>2</li><li>3</li> がマッチする。

ちなみに、単純セレクタとは、要は普段cssdiv.class-name {...;} と定義している際の div.class-name のことである。 Selectors Level 3 の内容を参照するに、

が該当する。つまり#idも.classも:hoverも:linkも大体使える。

纏め

CSSセレクタ多用しまくると何がなんだか分からなりそうなので、使う時はBEMによるCSS設計に基いたりSassを使ったりして構造化を意識して利用しよう。(戒め)。

構造的なCSS設計, Sass設計については、やはり以下の書籍が参考になるのでフロントエンドを学ぶ技術者でまだ買っていない人はぜひ一読をオススメしたい。

Web制作者のためのSassの教科書 これからのWebデザインの現場で必須のCSSメタ言語

Web制作者のためのSassの教科書 これからのWebデザインの現場で必須のCSSメタ言語

参考:

*1:といってもCSSセレクタの内容に変更はない。

flake8 で continuation line under-indented for visual indent [E128] が出る時

flake8でコーディングチェックを行っていると、continuation line under-indented for visual indent [E128] が出る時がある。

f:id:ShineSpark:20151115155229p:plain

Vim標準、もしくは jedi.vim 利用時の自動インデントに倣って入力してるつもりだが、flake8では例えば引数などは、第1引数の開始箇所に倣って第2, 第3引数の開始箇所をあわせていく必要がある。

@app.route('/add', methods=['POST'])
def add_entry():
    if not session.get('logged_in'):
        abort(401)
    g.db.execute('insert into entries (title, text) values(?, ?)',
                 [request.form['title'], request.form['text']])
    g.db.commit()
    flash('New entry was successfully posted')
    return redirect(url_for('show_entries'))

上記のように()内の引数の開始位置を揃えると警告が出なくなる。

みんなのPython 第4版

みんなのPython 第4版