i++

プログラム系のメモ書きなど

gcloud : ERROR: (gcloud.auth.git-helper) Invalid input line format: [path=].

gcloud source repos clone (リポジトリ名) --project=(プロジェクト名)Google Cloud Platform 上のソースコードをクローンしようとしたときに発生。

ERROR: (gcloud.auth.git-helper) Invalid input line format: [path=].
fatal: remote error:

解決方法

git config --system --unset credential.helper の実行で解決。

ただし

error: could not lock config file C:\Program Files\Git\mingw64/etc/gitconfig: Permission denied

と初回実行時には出ていたので、該当のフォルダ(C:\Program Files\Git\mingw64/)を右クリック → プロパティ → セキュリティでアクセス許可を編集する必要があった。

参考

gcloud cli - On Windows git pull and clone for Google cloud repository pops credential manager dialog box - Stack Overflow

Robobrowser + α によるスクレイピングの忘備録

Pythonクローリング&スクレイピング -データ収集・解析のための実践開発ガイド-』を読んでしばらく Robobrowser を使ってみようと思うので、その忘備録。

edx.hatenablog.com

基本

browser = RoboBrowser(
    parser='html.parser',
    # Cookie が使用できないと表示されてログインできない問題を回避するため、通常のブラウザの User-Agent を使う。
    user_agent='Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:45.0) Gecho/20100101 Firefox/45.0)'
)
broswer.open(スクレイピング対象のページのURL)
# 取得したページの中身の確認。デバッグ時以外は不要。
# print(broswer.parsed)

user_agent の指定はサイトによっては不要。少なくとも Amazon.co.jp については必要(設定しない状態で open すると、取得できる html で Cookie を有効にして下さい、という旨のメッセージが入っていることがわかる)。

以降のコード例は基本的に上記の続きで broswer が Robobrowser インスタンスを表すものとする。

CSSセレクターを使った要素の取得

select 関数で CSSセレクターを使用可能。条件に一致する要素の配列が取得できる。

条件を指定して要素を取得する関数としては find_all 関数も利用できるので、好みで。find_all の方が柔軟かもしれない。

実際に使用したことのある一部の例のみ記載。

探したい要素 書き方
特定のクラスを持つ要素 要素.クラス a.active
特定の要素の子孫 要素 要素 body h1
特定の要素の直接の子 要素 > 要素 body > h1
特定のID属性 #ID #main
属性が特定の値 要素[属性=“値”] a[href=“/sample/index.html”]
属性が特定の値で始まる 要素[属性^=“値”] span[id^=“itemPrice_” ]
属性が特定の値で終わる 要素[属性$=“値”] img[src$=“.jpg” ]

以下の例では g-item-details というクラスを持った div 要素を取得し、取得した各要素に更に select を実行して必要な値を取得している

for item_info_element in browser.select('div.g-item-details'):
    title = item_info_element.select('a[id^="itemName_"]')
    assert len(title) == 1
    # text でテキストを取得。
    title = title[0].text.strip()
    # href 属性をキーにリンク先を取得
    # 要 from urllib.parse import urljoin
    url = urljoin(browser.url, title[0]['href'])
    # URL の ref= 以降は不要。
    url = item.url[:item.url.find('ref=')]
    price = item_info_element.select('span[id^="itemPrice_"]')
    # 必要な処理を続ける

特定のテキストを持ったリンク

get_link 関数の第一引数でテキストを指定する。取得したリンクを開くには follow_link 関数を使用する。

# この例ではリンク先(href の値)が特定の URL パターンに一致することも確認している
sign_in_link = browser.get_link('サインイン', href=re.compile(r'https://www\.amazon\.co\.jp/ap/signin\?'))
# assert sing_in_link is not None
# browser.follow_link(sign_in_link)

フォーム

action をキーに get_form 関数を使って取得。 name 要素をキーに各入力欄にアクセスして値を設定し、submit_form 関数で実行

browser.follow_link(sign_in_link)
form = browser.get_form(action='https://www.amazon.co.jp/ap/signin')
if form is None:
    raise Exception('Failed to get signin link.')
form['email'] = '.....@gmail.com'
form['password'] = 'secret, of course!'
browser.submit_form(form, headers={
    'Referer': browser.url,
    'Accept-Language': 'ja,en-US;q=0.7;q=0.3'
})

Amazon では Referer と Accept-Language の設定が必要。必要ないサイトもある。この辺りの必要性は実際に確かめるしか無い?

関数のリトライ設定

Retrying パッケージを使用して、関数に @retry デコレータを使用する

@retry(stop_max_attempt_number=3, wait_exponential_multiplier=1000)
def signin() -> RoboBrowser:
    # 処理に失敗した場合は raise Exception('失敗の理由') などで例外を発生させる

github.com

golang : eval (数式の evaluation)を行う

go/token パッケージ、go/types パッケージ、go/constant パッケージを使用します。

import (
    "fmt"
    "go/token"
    "go/types"
    "go/constant"
)

fs := token.NewFileSet()
tv, err := types.Eval(fs, nil, token.NoPos, "1 + 2 * 3 % 4")
if err != nil {
    fmt.Println(err)
    return
}
val, ok := constant.Int64Val(tv.Value)
if !ok {
    fmt.Println("Failed to get Int64Val")
    return
}
fmt.Println(val)

// 結果は 3。

(Go 言語で プログラマ脳を鍛える数学パズル シンプルで高速なコードが書けるようになる70問 をトライ中に 2問目を解くために方法を探しただけで、中身はあまりよくわかっていません。)

Angular2 : [ts] Property 'map' does not exist on type 'Observable<Response>'

http.get(url).map(...) を試みたところ発生。

[ts] Property 'map' does not exist on type 'Observable<Response>'

import rxjs/add/operator/map を追加することで解決。

import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/map';

catch には import 'rxjs/add/operator/catch'; が必要になる。 一括でインポートするには import 'rxjs/Rx';

golang : template の range で index を使う

以下の様なデータを template.Execute のデータに渡したとすると

type data struct {
    Values      []string
    OtherValues []string
}

テンプレートで

<ul>
    {{range $i, $v := .Values}}
    <li>{{index $.OtherValues $i}} のようにするとインデックスでアクセスできる( OtherValues[i]の値)。{{$v}} はもちろん Values[i]</li>
    {{end}}
</ul>

のようにすれば、Values にループをかけている最中にインデックスを使って他の配列やスライスの特定の値にアクセスできる

template - The Go Programming Language に書いている通り、テンプレート中の {{index スライスや配列の変数 インデックス値}}ソースコード中のスライスや配列の変数[インデックス値] に対応する。

index
    Returns the result of indexing its first argument by the
    following arguments. Thus "index x 1 2 3" is, in Go syntax,
    x[1][2][3]. Each indexed item must be a map, slice, or array.

また、$ が Execution に渡されたデータを指すので、range 中にデータの他の変数にアクセスするには $.変数名 となる。

golang : コミット時に gofmt を実行する(pre-commit hook)

misc/git/pre-commit - The Go Programming Language を参考に作成しました。

オリジナルではフォーマットされていないコードがある場合にコミットを中断していますが、以下のスクリプトでは gofmt を実行して git add するところまでやっています。

#!/bin/sh

gofiles=$(git diff --cached --name-only --diff-filter=ACM | grep '.go$')
[ -z "$gofiles" ] && exit 0

unformatted=$(gofmt -l $gofiles)
[ -z "$unformatted" ] && exit 0

echo >&2 "Info: Running gofmt as some Go files are not formatted with gofmt."
for fn in $unformatted; do
    gofmt.exe -s -w $fn
    echo >&2 "gofmt -s -w $fn"
    git add $fn
done

exit 0

リンク先は、gofmt に加えて、gocyclo によるCyclomatic complexity のチェック(見るだけ)と goimports の実行も合わせたものになります。

github.com