日経Web刊用紙面データ取得スクリプトを削除しました。

5/12に以下のようなクレームがYouTube経由で届きました。
本blogで公開していた、日経電子版紙面用画像取得スクリプトの公開について
・サイトポリシーに反する
・画像/記事等のコンテンツ著作権日経新聞社ほか情報提供者に帰属する
・記事を著作権者の承諾なしに取得/複製するソフトの作成と公開は
 日経新聞社の権利を侵害する行為であるとの見解(また、日経電子版の
 規約にも反するとの注記)
・私的使用の範囲を超える行為は著作権や法的利益の侵害行為と判断
 するため、速やかに削除するように
という内容でした(全文コピペするとそれはそれでクレームとなりそうなので
要旨をまとめなおしてあります)。

ということでソースは削除しました。悪しからずご了承ください。
主張の成否は法律に明るくないので判断つきかねます。どなたか分かればご指摘頂けると幸いです。

みゅおは引き続き日経新聞社がiPhone版やAndroid版のクライアントソフトウェアを公式展開頂けることを期待し、待っております。

日経Web刊、紙面ビューア用画像取得スクリプト(Python)改訂

日経Web刊紙面画像リサイズスクリプト(Python)のコメントで要望頂いた点などを反映し、画像取得スクリプトを改訂しました。

日経新聞社からのクレームにより削除いたしました。各位にお詫び申し上げます。

必要環境

  • Python2.5または2.6
  • lxml
  • PIL

変更点

  • ダウンロードした画像をリビルドし、1枚にして指定ディレクトリへ出力出来るようにした(出力時ファイル名は20100429_朝刊_01_1面.jpgというようになります)
  • 最大解像度の画像のみを取得し、転送量を削減した
  • エラー発生時のリトライを自動的に行うようにし、基本的に一度のコマンド実行で全面を取得出来るようにした

使い方

例によって契約せずにデータ取得を行うような用途のものではありませんので、正規のアカウント設定が必要となります。その他注意については日経Web刊、紙面ビューア用画像取得スクリプト(Python)を一読下さい。

settings = {
	'userAgent' : 'Mozilla/5.0 (Windows; U; Windows NT 6.1; ja; rv:1.9.2.2) Gecko/20100316 Firefox/3.6.2',
	'loginId' : 'username@example.com',
	'loginPass' : 'password',
	'dataDir' : 'C:\\Users\\testuser\\Desktop\\data',
	'rebuiltDir' : 'C:\\Users\\testuser\\Desktop\\nk_rebuilt',
	'outputSizeLevel1' : (500, 674),
	'outputSizeLevel2' : (1000, 1348),
	'outputSizeLevel3' : (1500, 2022),
	'classDef' : [(3000, 16)]
}

上記が設定部分です。
loginId, loginPassをそれぞれ指定、dataDirには取得した画像の元データ保存先を指定、rebuiltDirには1枚に結合した画像の出力先ディレクトリを指定して下さい。他はデフォルトのままでOKのはずです。

日経Web刊紙面画像リサイズスクリプト(Python)

先日書いた日経Web刊、紙面ビューア用画像取得スクリプト(Python)の関連記事です。
同記事にka_zuさんから頂いたコメントをもとに、取得した画像の結合ツールを作成してみました。

画像編集ライブラリとしてPILを利用していますので、Python Imaging Library (PIL)からあらかじめインストールしておく必要があります。

使用方法

settings = {
	'dataDir' : 'C:\\Users\\testuser\\Desktop\\data',
	'outputSizeLevel2' : (1000, 1348),
	'outputSizeLevel3' : (1500, 2022)
}

ここで

  • dataDirに取得済み紙面データがあるディレクトリを指定(画像取得スクリプトと同じ設定でOKです)
  • outputSizeLevel2に拡大版(2段階目)の画像書き出しサイズを指定(デフォルトは、そのまま結合して出力したサイズです)
  • outputSizeLevel2に超拡大版(3段階目)の画像書き出しサイズを指定(デフォルトは、データ量が生データの60%程度になるようにしています。(2000, 2696)と設定すると生データ同様のサイズになります)

これらを設定してスクリプトを実行すると、元データディレクトリに2.jpgと3.jpgが出力されます(生データは破壊しません)。
ゴリゴリと機能実装しただけで、あまり利用シーンを考えていないのですが、多分この先は他ディレクトリへの書き出しや、ディレクトリに対するedition.xmlから取得した情報の記入などが出来れば便利かな、と思います。
利用シーン(例えばWindows環境、WindowsMobile環境の特定画像ビューアと連携するなど)を勘案し、あると便利な出力方法などお気づきの際はコメント頂けると幸いです。

その他コメント

  • ひたすらJPEGを開いて加工というのは、まあまあ重い処理なのでAtom向けではないと思います。母艦でデータ取得・加工を行いネットブックへ投入という流れが良さそうです。
  • エラー処理をほとんど行っていないので、一部画像が欠落している(2000.jpg, 2001.jpg, 2003.jpgはあるが2002.jpgが無いなど)場合には例外を吐いて死んでしまいます。画像取得時に並行して処理していくようにすればある程度マシになるはずですが、まだまだ改善余地ありそうです。

ソースコード

import glob, os.path, math
from lxml import etree
from PIL import Image

settings = {
	'dataDir' : 'C:\\Users\\testuser\\Desktop\\data',
	'outputSizeLevel2' : (1000, 1348),
	'outputSizeLevel3' : (1500, 2022)
}

class RebuildNkImages:
	def __init__(self, settings):
		self.settings = settings

	# dummy, right now
	def rebuildAll(self):
		pass

	def rebuildMostRecent(self):
		target = self.getMostRecentLocalPaperId()
		self.performRebuild(target)

	def getMostRecentLocalPaperId(self):
		papers = glob.glob(self.settings['dataDir'] + '/*')
		if len(papers) == 0:
			raise Exception, 'no paper found on local datadir'
		papers.sort()
		papers.reverse()
		return papers[0]

	def performRebuild(self, target):
		configPath = target + '/edition.xml'
		if (os.path.exists(configPath) == False):
			raise Exception, 'no config XML found on ' + configPath
		editionXml = etree.parse(configPath)
		pages = editionXml.xpath("//edition/pages/page")
		for page in pages:
			imageDir = target + '/' + page.attrib['id']
			# process level 2 images
			self.rebuildImages(imageDir, 2, (1000, 1348), self.settings['outputSizeLevel2'])
			# process level 3 images
			self.rebuildImages(imageDir, 3, (2000, 2696), self.settings['outputSizeLevel3'])

	def rebuildImages(self, imageDir, level, defaultSize, outputSize):
		# const values should be splitted off
		nbTiles = {2: 4, 3: 16}
		outImage = Image.new('RGB', defaultSize)
		dstFilePath = imageDir + '/' + str(level) + '.jpg'
		for i in range(nbTiles[level]):
			srcFilePath = imageDir + '/' + str(level * 1000 + i) + '.jpg'
			offsetX = int(i % math.sqrt(nbTiles[level])) * 500
			offsetY = int(i / math.sqrt(nbTiles[level])) * 674
			outImage.paste(Image.open(srcFilePath), (offsetX, offsetY))
		if (defaultSize != outputSize):
			outImage = outImage.resize(outputSize, Image.ANTIALIAS)
		print 'generated: ' + imageDir + ' / level ' + str(level)
		outImage.save(dstFilePath)

rebuilder = RebuildNkImages(settings)
rebuilder.rebuildMostRecent()
# rebuilder.rebuildAll()

日経Web刊、紙面ビューア用画像取得スクリプト(Python)

先日書いた日経Web刊 紙面ビューア、ほんの少しだけ技術面の続きとなります。

大事な前書き

結構デリケートなものでありますので、本題の前に少々断りごとを記載します。
今回のツールを含め、みゅおの取り組んでいるものは全てnikkei.comでの(2010年5月以降の)有料会員登録を行わずに記事を読んだり、課金を回避したりするような目的のものではありません。あくまでも正規会員の方が自身の責任で(アカウント停止など、日経新聞社の判断で行われる可能性を認識しつつ)利便性を追求するためのものです。
本来、日経新聞社自体がスマートフォン向けのきちんとしたビューア類を公式展開して頂ければこれに勝るものはありません。しかしながら現状としてAndroidはおろかiPhone向けすら公式ビューアが提供されていません(2010年4月5日現在)。おそらく2010年4月にローンチを間に合わせるというスケジュールや投下資本に対するリターンといった諸々を検討した上で現在の機能や対応プラットフォームへと落ち着いているはずで、企業活動としては至極当然の判断でしょう。
しかし、みゅおとしては長年待ちに待った日経朝刊をスマートフォンで読めるところまであと一歩となっている、技術的にはアクセスも可能である、しかし公式ビューアが無いためアクセス出来ない・・という状況が残念すぎるのです。
このため、みゅおは独自にデータ取得や閲覧を出来るようにしようとする試みを行っています。
その成果は極力オープンにしていこうと考えていますが、それらを利用することで利用者の方と日経新聞社との間でトラブルが発生する可能性は否定出来ません。また産経新聞の紙面ビューアとは違ってnikkei.comへの登録時点で、ある程度の個人情報を日経新聞社側へ渡している認識も持ちつつ使って頂きたいと願っております(日経新聞社側でリクエスト送信のパターンからツール利用者を抽出するのは造作も無いことでしょう)。

何をするツールか

前書きが長くてすみません。本題です。
このスクリプトは、最新の日経朝刊について日経Web刊の紙面ビューア用JPEG画像と紙面一覧xmlファイルを取得してくるものです。機能としては

  • nikkei.comへログインする
  • 最新朝刊のサムネイル、通常画像、詳細画像を取得する(このとき、サムネイル→通常画像→詳細画像の順で取得する)

を持っています。CUIツールで延々裏側で処理をして無言で終了します。気の利いたGUIなどは残念ながらありません。一応裏側でログは出力しているので、エラー終了時などには参照ください。

これが出来て何がうれしいか

  • あまり一般の方にはうれしくないでしょう(紙面コレクターの人にはうれしいのかもしれません)。
  • Flash環境用のビューアなどを開発している人にとっては何かの参考になるかもしれません。
  • PCでもAtom環境などFlash版ビューアが重過ぎるという場合は、DirectXなどで簡単なビューアを書くことでかなりレスポンスを改善できるかもしれません。


本エントリ末尾にソース全文を貼り付けておきます(Google Codeからの取得はこちら)。

使い方

	'loginId' : 'username@example.com',
	'loginPass' : 'password',
	'dataDir' : 'C:\\Users\\testuser\\Desktop\\data',

上記のloginIdをnikkei.comの登録メールアドレスに、loginPassをパスワード(平文)に、dataDirを紙面データの保存先ディレクトリに変更して実行してください。途中でエラーとなって取得が失敗した場合は、再実行することで残分を取得出来ます。

ソースについて少々コメント

  • コメントはありませんがログ出力を結構大量に埋め込んでいるので挙動が気になる場合はそちらを読んでください。
  • 実行するとカレントディレクトリにcookies.txtが作成されますが、現状特に意味はありません(ログイン状態記憶等がサイト自体に実装されていないのでセッション継続への利用も出来ない)。気になるようだったらLWPCookieJarを使わないようにするなど適当にいじってください。
  • 実際の画像取得部分には前のエントリで書いたように適当なウェイトを入れています。ソース的にウェイトを外すことは簡単ですが、「ぶっこぬき」っぽい挙動はあなた自身のアカウント寿命を縮めるような気がしませんか?
  • あまり読まない面の詳細画像を取得せずに負荷軽減する機能は残念ながらまだ実装していません。ご要望頂ければ早めに実装しますのでblogのコメント欄でも@muo_jp宛でも結構ですのでコメントください。

ソース

日経新聞社からのクレームにより削除いたしました。各位にお詫び申し上げます。

日経Web刊 紙面ビューア、ほんの少しだけ技術面

  • XMLには見出し情報と、その紙面上におけるリージョン定義が含まれているのだけど、これを使って勝手にユーザ参加型(Android用、某ビューアのコメント機能など面白いのだけど)のものを作るとトラブルに発展する恐れ大。たまにデータおかしいものの基本的にはきれいに整理されているのだけに残念。
  • 記事画像は基本的に250KB前後。段階は3つ。サムネイル(1面につき1枚)、やや詳細(1面につき4枚)、かなり詳細(1面につき16枚)から構成されている。
  • 記事画像の「やや詳細」程度のものでなんとか読めなくは無い程度。読みやすさでは、やはり「かなり詳細」のものが必要そう
  • 結果、データ量は朝刊1日につき250(KB) * 21 * 40 = 210MB。結構なボリュームである。
  • サムネイルは紙面ビューアロード時に全て読み込まれるが、他はオンデマンド。
  • ちなみに全面カラー広告面などでは1画像あたり350KB程度あったりする。
  • 200MB以上毎朝並行コネクション張ってごっそり取得していたらすぐに怒られそう
  • 通常利用で想定される以上の負荷をかけたくはない
  • 紙面ビューアでの朝刊内容確認に30分程度かけるものとすれば、2秒/枚 程度のスピードで取得していけば文句は出ないと考えたい。
  • サーバが混んでくる時間帯を避け、朝刊更新後結構すぐに取得を開始して5時頃には取得完了しておけばなお良いか。
  • 実際の取得時には、「あまり読まない面」設定を設けて詳細画像を取得せず、紙面閲覧時点でオンデマンド取得するようにしておけば帯域負荷的にも良いのではないか(あくまでもサムネイルは取得する。広告回避などで日経新聞社のビジネスモデルに対する挑戦/妨害と受け取られるのは避けたいので)。例えば証券面(1〜4)などは人によっては読み飛ばすことが多く(関連業界の掲載された面しかチェックしない等)、読まないけれどデータだけ取得するというのは誰も幸せにならない感。

追記(2010/4/5): 関連記事として日経Web刊、紙面ビューア用画像取得スクリプト(Python)を書きました

日本経済新聞Web刊を少々使ってみた感じ

全体: 結構な高機能を作りこんだなぁという印象。Financial Timesの電子版成功を研究して取り組んだような感じを受けた。しかし3/26の早朝時点でも結構重い。
朝刊/夕刊のページについて:

  • 見出しだけでは情報が少なすぎる、わざわざ別ページへ飛んで全文読むのは時間がかかりすぎる。新聞を読む際は精読せず必要そうな情報のみ拾い読みするので、やはり紙の新聞(慣れたUI)に近いほうが便利

紙面ビューアについて:

  • Flashインターフェイスの紙面ビューアは少々もたつく。高機能ゆえか
  • 紙面一覧に見出しが表示されるけれど、クリックしてのジャンプは出来ないのが微妙にUI統一取れていない感じ
  • 日経本紙の朝刊は40面中14面、実に3割が全面広告なのだと気づいた(!!)。実質26面か。まず読むことの無いテレビ番組面を除くと実質24面。