リンク先の画像をまとめて保存するpythonスクリプト

珍しく仕事のデータ形式に依存しないスクリプトを書いたので,調べたことなどを忘れないように.

ソース

#! /usr/bin/python
# coding: utf-8

import subprocess
import re
import sys
# import codecs
import os
import urllib2

IMGURL = re.compile(ur'http.*?\.jpg|JPG|jpeg|JPEG')

inputurl = sys.argv[1]
inputhtml = urllib2.urlopen(inputurl)
html = inputhtml.readlines()

target_lines = []
for line in html:
    for imgline in IMGURL.findall(line):
        target_lines.append(imgline)

dirname = re.sub(u'https?://', u'',inputurl).replace(u'/', u'--') + u'_imgs'
os.mkdir(dirname)
os.chdir(dirname)

for url in target_lines:
    getimg = subprocess.call([u'wget', url])

inputhtml.close()

使い方

$ python scriptname.py URL

やってること

  • 使うモジュールのインポート
import subprocess
import re
import sys
import os
import urllib2
  • ページ内の画像リンクを抽出するための正規表現文字列を指定
IMGURL = re.compile(ur'http.*?\.jpg|JPG|jpeg|JPEG')
  • URLからHTMLソースを行ごとに取得
inputurl = sys.argv[1]
inputhtml = urllib2.urlopen(inputurl)
html = inputhtml.readlines()
  • 各行にある画像のURLをパターンマッチングで取得し,URLのリストを作る
target_lines = []
for line in html:
    for imgline in IMGURL.findall(line):
        target_lines.append(imgline)
  • 保存用のフォルダをカレントディレクトリの直下に作り,移動
dirname = re.sub(u'https?://', u'',inputurl).replace(u'/', u'--') + u'_imgs'
os.mkdir(dirname)
os.chdir(dirname)
  • 取得したURLから画像を保存
for url in target_lines:
    getimg = subprocess.call([u'wget', url])
  • ファイルを閉じる
inputhtml.close()

余談

urllib2とwget

urllib2(pythonのモジュール),wget(シェルコマンド)がインストールされていないかもしれない.
前者はpipで

$ pip install urllib2

後者はhomebrewで

$ brew install wget

で取得.pipおよびhomebrewのインストールや使い方については調べてください.

保存するファイル
IMGURL = re.compile(ur'http.*?\.jpg|JPG|jpeg|JPEG')

単に拡張子でファイル形式を指定しているだけなので,ここを変更すれば保存するファイルを変えられる.

with codecs.open()とurllib2の組み合わせ
# import codecs
import urllib2

inputurl = sys.argv[1]
inputhtml = urllib2.urlopen(inputurl)
html = inputhtml.readlines()

inputhtml.close()

urllib2.urllibopen() はwith codecs.open()では開けなかったが,なにかやり方があるんだろうか.
おかげで時々UnicodeDecodeErrorが出る
# ソースhtmlのエンコーディングによるらしい
# sys.setdefaultencoding('utf-8')は試していない

保存先とos.mkdir()の仕様
dirname = re.sub(u'https?://', u'',inputurl).replace(u'/', u'--') + u'_imgs'

フォルダ名はURLから作成
http(s)://を削除し,/を--に置き換えている
/のままだとosモジュールにpathだと認識されてしまい,.mkdirでは再帰的にフォルダが作れない旨のエラーが出る

やってもいいかも

IMGURL = re.compile(ur'http.*?\.jpg|JPG|jpeg|JPEG')

jpg|JPG|jpeg|JPEG のところをコマンドライン引数で指定するようにしてもいいと思う.

18/06/21追記

urllibにurllib.urlretrieve(url, file)というのがあるらしい.

How do I download a file over HTTP using Python? - Stack Overflow

pLaTeXフォントの部分的変更に関する覚書

やりたかったこと

  • pLaTeX文書において,できるだけ簡便な指定で部分的に「文字修飾可能な等幅組み」をする
  • 半角文字は全角文字の半分の幅にする
  • できれば英数字はタイプライタ体になってほしい

問題

  • \texttt{}では日本語フォントが単に太字ゴシックになるだけで,しかも文字幅は上記のようにならない
  • 任意のフォントを上記のようなコマンドに割りあてる仕方がわからない
  • そもそもフォントを追加し,使えるフォントを増やす方法もよくわからない
  • フォント自体の指定の仕方がよくわからない

前提

現状

  • 問題は解決せず,やりたかったことは実現できていない
  • メトリックとかフォントマップとかに関わるファイルを触ったので,やったことだけは忘れないようにしたい

やったこと

メトリックファイルとフォントマップの追加

メトリック(フォントを組む幅や相対的なサイズなど)を記述したファイル (.tfm) と,メトリックとフォントファイル自体とを関連づけるフォントマップ (.map) というものがあり,これらは新しいtexliveには標準で付帯しているLCDF Typetoolsを使う.
ない場合はTeXのパッケージ管理ツールtlmgrを利用してインストールできるが,そもそもこれがないという場合はディストリビューション自体が古いか,完全にインストールできていないかの可能性が高いので,texlive自体を更新したほうがいい.
TeXliveの更新もtlmgrでできるらしいが,やり方はわかりませんので調べてください.

で,LCDF Typetoolsの中のotftotfmというコマンドを使って,OpenTypeフォントからTFMファイルとフォントマップファイルを生成する.
やり方はTeX Wikiの該当ページを完全に鵜呑みにするしかなかった(ので,後々微妙に間違ってるのか,そうでもないのかよくわからない事態に陥る).
https://texwiki.texjp.org/?TeX%E3%81%A8%E3%83%95%E3%82%A9%E3%83%B3%E3%83%88

が,生成したかったOsaka-MonoフォントはTrueTypeで,このコマンドでは処理できなかったので,ウェブのツールでOpenTypeに変換した.
これも正しかったのかどうかはわからないが,一応動作はするようになった.

otftotfm --no-type1 -e 7t -fkern -fliga OsakaMono.otf -n OsakaMono > Osaka.map

フォントマップはMonoにしなかったのは,このあと同様に無印のOsakaフォントを処理したからで,フォントマップはまとめてしまって構わないらしいため.
二回目以降の上記コマンドの実行には,標準出力からのファイルの作成を命令する > ではなく,既存のファイルへの追記を命令する >> を使って出力する.

上のコマンドを必要なだけ実行したら,

tex testfont

を実行する.必要に応じてsudo権限で実行する.割とややこしいので詳細は該当Wikiページ参照.

なお,これを書くためにWikiの該当ページを見直していて,Font Difinitionファイルというのを作っていないことに気づいた.
フォントのなになにのシェイプが定義されていません的なメッセージだったので,これが問題に関係している可能性がある.
しかしもう疲れたので今はやらない.

なお,MacOSのフォントは一箇所にまとまっておらず散らばっている.どこにあるのかは調べてください.
フォントそのものを移動すると面倒が生じかねないので,シンボリックリンクを貼ってやる.

ln -s /Library/Fonts/Osaka.otf /usr/local/texlive/texmf-local/fonts/opentype/osaka

tfmファイルなどもこのディレクトリに作ればいいっぽい.ちなみにデフォルトではtexmf-localまでしかないのでこの下は作る.
しかしmktexlsrやってもパスを通しても,あんまりちゃんとこのあたりのディレクトリを見てくれていないんだよなあ…

TeXファイルの記述

上記のWikiページにも(よく見ると)書いてあったし,他にもhttp://d.hatena.ne.jp/zrbabbler/20110911/1315737566に詳しいが,本文中でフォントを指定する方法は,要するに通常のフォント指定のコマンドに別のフォントを割り当てるというのが手っ取り早いらしい.

\usepackage[OT1]{fontenc}
\renewcommand{\ttdefault}{Osaka}

\begin{document}

\ttfamily
This sentence is written in a typewriter font.

\end{document}

なんだが,よくわかっていないものの,そもそも和文フォントというのはJY1(横書き)かJT1(縦書き)でエンコードすべきものらしく,上のotftotfmの段階で間違っていたらしい.
しかし,上記のfontenc.styは欧文フォントエンコーディングにしか対応していない?らしい.
もっというと,どうやらこのfontenc.styは必須ではないらしいが,ではなにをしているのかというのもよくわからない.

結果

結局OsakaMonoでttfamilyを出力することはできず,等幅にもならなかった.さらにいうと,ワードのようにフォントだけ変えれば位置関係やサイズが揃うわけではなく,それらのことはメトリックという概念によってコントロールしているらしいことがわかった.であれば,そのためのメトリックを改めて記述してやれば,フォントがなんであろうと等幅で組むことができるということではないだろうか.
あと,フォント定義をしていないこととエンコーディングが間違っていたことがも問題であるように思われるので,これはおいおい試す.

参考(本文で挙げたもの以外)
http://www.geocities.co.jp/texuttex/jfontsettings.html
https://qiita.com/zr_tex8r/items/297154ca924749e62471
https://qiita.com/zr_tex8r/items/15ec2848371ec19d45ed
http://d.hatena.ne.jp/zrbabbler/20130210/1360488604
http://www.fugenji.org/~thomas/texlive-guide/font_setup.html
http://d.hatena.ne.jp/tama_sh/20120505/1336203065

beamerとpBIBTeXでフォント周りに手間取った話

結論から言うと

beamer何の関係もなかった

何の話かというと

beamerで学会発表用のポスターを作っていて,参考文献リストを出力するとき,本文と同じフォントサイズで出力する必要はないので小さくしようとして,

\begin{document}
\begin{frame}
...
\begin{block}{references}
    \begin{tiny}
        \bibliographystyle{bibstyle}
        \bibliography{bibfile}
    \end{tiny}
\end{block}
\end{frame}
\end{document}

とかやっていたのだが,なぜか,

Author, A. (2017) "paper title". Journal title. 30(2), 55-70. ADDRESS: PUBLISHER Inc.

みたいになるという謎事態が発生していた.

pBIBTeXでこういうことにはなったことがなかったので,使い慣れないbeamerとの関連で発生する問題かと思い,フォントのこととかbeamer特有の環境のこととかをいろいろ見てみたが,それらしい解決には行きあたらなかった.

参考:
PostScript Font (Official Site)
LaTeX の警告メッセージ - TeX Wiki
LaTeX で \newfont したい話 (bxnewfont パッケージ) - マクロツイーター
LaTeX に新しいフォントを持ち込む話 - マクロツイーター

このあたり勉強になったと言えばなったものの,例えば直接.fdファイルを書き換えても特に何も起こらず,解決に至らないのみならず手応えのない結果になった.
何も起こらないのは書き方が間違っていて無視されたということだろうか.

ちなみにbeamerでフォントサイズを変える方法としてよく出てきたのは,フレームごと相対的にサイズを変えるというもの.

\begin{frame}[shrink = 30]
shrinked text
\end{frame}

などとやると小さくできるらしい.
今回はポスターなので全体がひとつのフレームになっており,この方法は使えなかった.

参考:
beamer | Smaller Font Size for the Bibliography
biblatex - Beamer: Changing the font size of the references using the shrink argument causes an error - TeX - LaTeX Stack Exchange

結局どうしたかというと

.bblファイルを直接いじったら何とかなるのではと思い,何となく開いたら斜体部分が

{\it italicized text}

のようになっていて,何の気なしに(一応これが推奨されないとされているのは知っているものの,別にどっちでもいいと思っていた)

\textit{italicized text}

に書き換えたところ,フォントサイズの変更命令が斜体部分にも適用されるようになった.
前者の書き方がなぜ推奨されないのかまだよくはわからないが,その外側にある別の環境と両立しないことがあるらしい.

ffmpegでmp3をエンコードするときに時間情報が正しくなるようにする方法

mp3ファイルには固定ビットレートCBRと可変ビットレートVBRというものがあるらしいが,可変ビットレートの場合にはVBRヘッダという情報が書き込まれていないと再生ソフトやfinderの時間表示がおかしくなる.
私の見たところ,iTunesQuickTime Player, finderの表示はおかしくなり,VLC Playerではリストの表示はおかしくてプログレスバーの時間は正しいようだった.

VBRタグを付加するツールはWindows用のものがいろいろ出回っているようだったが,Mac用のものはよくわからない.
"mp3 bitrate header" などとしてhomebrewで検索して出てくるもののうちどれかがそうである可能性はなくはないが未確認.

で,応急的にというか,VBRにこだわる理由も特にないので,取った対処は以下.

$ ffmpeg -i input.ac3 -ab 128k output.mp3

単にビットレートを指定してやるだけである.それでも,時間長はビットレートとファイルサイズから計算されているようなので,正しく表示されるようになる.
VBRは同等の音質ではファイルサイズが小さくなるが,容量がそこまでシビアでないならCBRでも特に困ることはない.

参考:
Audiograbber関連プログラム
最新ffmpegのオプションまとめ - MobileHackerz Knowledgebase Wiki

2019/3/23追記

可変ビットレートのまま正しく読めるようにするには,-q:aオプションというのを付加してやるとよいようだ.
ringil-blog.info

$ ffmpeg -i input.ac3 -q:a 1 out.mp3

この -q:aは-qscale:aのエイリアスだそうで,つまり引数によって値が可変する範囲を指定しているようだ.
trac.ffmpeg.org

sys.argvの不思議で便利な仕様,ワイルドカードでファイル名のリストを再帰的に取得する

以前のエントリ(条件に合致するファイルを連続して読み込み,処理後にファイル名を変えて保存 - 惰力飛行)でも実はsys.argvの仕様について触れているのだが,そのときにはなぜかあまり疑問に思わなかった.しかし改めて考えると変なことが起こっている.なんのことかというと,

import sys
a = sys.argv

などとしてコマンドライン引数を取得し,

python code.py /Volumes/h*g*

などとしてコマンドライン引数にワイルドカードを用いたパスを書いてやると,aの中身は,

['code.py', '/Volumes/hogehoge', '/Volumes/hugahuga', '/Volumes/hhgghhgg']

などといったことになる.

sys.argvの仕様をちゃんと調べてないのであれだが,別にパスを入力する場所として指定されているわけではないし,普通の文字列を取得することもできる.にもかかわらずなぜか,ワイルドカードでパスを入力すると,そのとき存在するパスを要素とするリストの形に展開して取得してくれる.
これはどちらかというとpythonないしsys.argvの仕様というより,シェルがそう解釈しているということだろうか.だとすると,シェルを経由せずに渡したワイルドカードは正しく展開されないということになりそうだ.検証は面倒なのでまた今度.

ついでに,このsys.argvの挙動を,ディレクトリを再帰的に探索してファイルとディレクトリの一覧を取得するos.walk()と組み合わせて使うと,探索する最上位のディレクトリを指定するだけで条件に合致するファイルの一覧を簡単に取得できる.
例えば,ファイル名に'hoge'を含むファイルを探したいなら,以下のようにすると標準出力にフルパスの一覧が出る.

import os
import sys

a = sys.argv
list = []

for root, dir, files in os.walk(a):
    for f in files:
        if 'hoge' in f:
            path = os.path.join(root,f)
            print path

ちなみにos.path.joinはパスらしき文字列をパスとして使える形に整形して繋げてくれるらしい.どういうことかはまだよく調べていない.

このsys.argvの挙動,ワイルドカード検索の専用モジュールのglob.glob()とほぼ同様に使えるように思う.
上記のコードを置き換えるなら,/Volumes/以下を探すとして,下記のようになろうか.

import os
import glob

a = glob.glob('/Volumes')

list = []

for root, dir, files in os.walk(a):
    for f in files:
        if 'hoge' in f:
            path = os.path.join(root,f)
            print path

globを使ったほうのコードの動作確認はしてません.そのうち書き直すかも.

目的によってはいちいちコマンドラインからパスを取得する必要はないんだけど,そうだとしてもglobのほうが簡単に書けるというわけでもないし,sys.argvのほうが慣れているのでこちらを使い続けるように思う.

2018/12/7追記

さすがにいろいろひどすぎるので,訂正を書き加えておく.
sys.argvのワイルドカード展開はもちろんシェルがやっていることなので,避けたほうが無難な書き方である.
ワイルドカードはまずワイルドカードの文字列のまま受け取って(argparseの位置引数かなにかで),それをglobで展開するのが妥当だと思う.

下から2番目のコードサンプル

import os
import sys

# a = sys.argv -> これだとスクリプト名も含んだリストになる
a = sys.argv[1:]
# list = [] -> 無駄なリスト定義

for root, dir, files in os.walk(a):
    for f in files: # -> files(os.walkの3つ目の戻り値)はリストなので,展開してひとつひとつの要素ごとに処理
        if 'hoge' in f:
            path = os.path.join(root,f)
            print path
# -> /root/hoge

一番下のコードサンプル

import os
import glob

# a = glob.glob('/Volumes') # -> ['/Volumes']
a = glob.glob('/Volumes/*')

# glob.glob()はリストを返すが,os.walk()はリストを引数に取れない
for i in a:
    for root, dir, files in os.walk(i):
        for f in files:
            if 'hoge' in f:
                path = os.path.join(root, f)
# -> /root/hoge

emacsをmacネイティブの動作に近づける

emacsをずっと使ってきた人や,これからあえてemacsを使おうという人には本末転倒な話かもしれない.
方向キーに手を動かすロスをあえて可能にするという無駄な設定なのだが,とはいってもキーボード操作はほとんど言語のように習得しているもので,それをあえて別のものにするより,慣れている配置で使えたほうが短期的には速い.
まあ確かに方向キーは離れているから割と打鍵ミスをするけど,アルファベットキーならその数分の一程度のミス率だろうという気はするので,その意味でそっちに慣れたほうが速いのかもしれないが.

ちなみに,ごく基本的な動作(コピー,貼り付け,保存など)はもともとcmd+のショートカットが効くようになっている.

参考:
Mac なら Emacs は難しくない!Command キーのすすめ。 (フェンリル | デベロッパーズブログ)

カーソルの移動

;; 行頭/行末/先頭/末尾に移動するキーバインドをcmd + 矢印に設定
(define-key global-map (kbd "s-") 'move-beginning-of-line)
(define-key global-map (kbd "s-") 'move-end-of-line)
(define-key global-map (kbd "s-") 'beginning-of-buffer)
(define-key global-map (kbd "s-") 'end-of-buffer)

s- は "super key" だそうで,cmdキーはsuper keyに割り当てられている.
などは矢印のこと.
page up/downとかは最近のmacのキーボードにはついてないので,macのネイティブ動作は存在しないと考えて変えていない.

なお,一般的にキーバインドを変更する設定の書き方は,

(define-key global-map (kbd "keybind") 'command)

とする.
このとき,目的の動作がなんというコマンドで記述されているかを見るためには,

F1 c keybind

で,そのキーバインドに割り当てられた動作のコマンド記述がミニバッファに表示される.

ちなみにもっと細かく,左右のcmdキーに別々の機能を持たせるとかもできるらしい.
参考:
Mac なら Emacs は難しくない!Command キーのすすめ。 (フェンリル | デベロッパーズブログ)
init.el: MacのキーボードでMetaAltSuperHyper - Qiita

optionキーの動作

これは前のエントリでも書いた.

;; optionキーのemacs上の機能を無効化
(setq ns-alternate-modifier 'none)

参考:
macos - emacs上で,ことえりから「.」と「。」を切り替えて入力する方法 - スタック・オーバーフロー

その他

バッファ間の移動はC-x C-oよりControl+tabとかのほうがそれっぽいかも.この場合の設定は,

(define-key global-map (kbd "C-") 'other-window)

取り消しはC-gではなくESCがmacネイティブ的だろうが,そればかりは難しい.

個人的に便利なemacsの設定まとめ

一般的な設定

;; 変更のあったファイルの自動再読み込み
(global-auto-revert-mode 1)

;; スタートアップメッセージを非表示
(setq inhibit-startup-screen t)
;; Emacsからの質問をy/nで回答する
(fset 'yes-or-no-p 'y-or-n-p)

;; バックアップファイルを作らない
(setq make-backup-files nil)
;; オートセーブファイルを作らない
(setq auto-save-default nil)

;; タイトルバーにファイルのフルパスを表示
(setq frame-title-format "%f")

;; 起動時のサイズ,表示位置を指定
(setq initial-frame-alist
      (append (list
	       '(width . 145)
	       '(height . 125)
	       '(top . 0)
	       '(left . 0)
	       )
	      initial-frame-alist))
(setq default-frame-alist initial-frame-alist)

参考:
emacs基本設定 - emacs設定の覚え書き書き

行番号の表示

;; 行番号を表示
(require 'linum)
(setq linum-format
      '(lambda (line)
        (let *1
                         "d"))))
          (propertize (format fmt line) 'face 'linum))))
(global-linum-mode)

行番号は最初と最後の1行ずつだけで表示できるようになるが,「最低幅を確保した上で,常に右寄せで行番号を表示させ」るための設定.
(min-w 6)の数字を変更すると最低幅が変わる.

参考:
[Emacs] linum.el で左側に行番号を表示する - @kei10in の日記

optionキーに割り当てられている機能を無効にする

これはあまり需要がないかもしれないが,例えば日本語パンクチュエーションを「,」「.」に設定していて,たまに「、」「。」が打ちたいときがあるとする.あるいはバックスラッシュと円マークは,どちらかがデフォルトになっているとき,optionで他方に切り替えるようになっていることがある.

ひとつめの方法は,キーバインドを新設してしまうというもの.

;; option + ./,の定義
(define-key global-map (kbd "M-,") "、")
(define-key global-map (kbd "M-.") "。")

これでも実用上さして不便はないが,ひとつひとつ設定しないといけない.
もし英語以外のヨーロッパ言語を打つ必要がある場合,optionが生きていないとかなり不便なはずだ.
そこで以下,スタックオーバーフローで教えてもらった方法.

;; optionキーのemacs上の機能を無効化
(setq ns-alternate-modifier 'none)

参考:
macos - emacs上で,ことえりから「.」と「。」を切り替えて入力する方法 - スタック・オーバーフロー

バッファ間のカーソル移動のキーバインド変更

これも需要がなさそうな設定だが,ことえりで日本語入力モードになっていると,C-x oのようにcontrolを離してから押下するキーがきかない.
これがストレスだったのでcontrolを押しっぱなしにするバインドを追加した.

;; カーソルを次のバッファに移動するキーバインドをC-x C-oに設定
;; もとのキーバインドも有効
(define-key global-map (kbd "C-x C-o") 'other-window)

参考:
Emacs講座 -第5回- キーバインドの変更 / マスタカの ChangeLog メモ

カーソル移動系はほかにもいろいろ要望があるが,おいおい考える.

*1:fmt (let ((min-w 6) (w (length (number-to-string (count-lines (point-min) (point-max)))))) (concat "%" (number-to-string (if (< min-w w) w min-w