惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

D
DataBreaches.Net
S
Schneier on Security
T
The Exploit Database - CXSecurity.com
Webroot Blog
Webroot Blog
AI
AI
P
Palo Alto Networks Blog
Attack and Defense Labs
Attack and Defense Labs
WordPress大学
WordPress大学
月光博客
月光博客
阮一峰的网络日志
阮一峰的网络日志
Spread Privacy
Spread Privacy
T
Tor Project blog
罗磊的独立博客
小众软件
小众软件
S
Security Affairs
酷 壳 – CoolShell
酷 壳 – CoolShell
量子位
Apple Machine Learning Research
Apple Machine Learning Research
T
Threatpost
NISL@THU
NISL@THU
博客园_首页
PCI Perspectives
PCI Perspectives
大猫的无限游戏
大猫的无限游戏
IT之家
IT之家
N
News and Events Feed by Topic
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Forbes - Security
Forbes - Security
博客园 - 叶小钗
D
Darknet – Hacking Tools, Hacker News & Cyber Security
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Last Week in AI
Last Week in AI
L
LINUX DO - 热门话题
T
Threat Research - Cisco Blogs
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
腾讯CDC
Security Latest
Security Latest
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
The Cloudflare Blog
A
About on SuperTechFans
爱范儿
爱范儿
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
TaoSecurity Blog
TaoSecurity Blog
宝玉的分享
宝玉的分享
G
GRAHAM CLULEY
雷峰网
雷峰网
F
Full Disclosure
I
Intezer
Cloudbric
Cloudbric
博客园 - 三生石上(FineUI控件)
U
Unit 42

hayato

Chromium's DOM Deep Dive: Understanding Shadow DOM and Tree Structures - hayato Introducing the Task Scheduler in Chromium's Network Stack: A 0.3% Improvement in Loading Speed - hayato 出版のお知らせ 「普通の人が資産運用で99点をとる方法とその考え方」 - hayato Resource loading with Content Addressable Bundles - hayato 普通の人が資産運用で99点をとる方法とその考え方 - hayato Imperative Shadow DOM Distribution API - hayato Google にソフトウェアエンジニアとして入社して 10 年と 10 日がたちました - hayato Web のつくりかた: ソフトウェアエンジニアリングと雰囲気で理解する Web 標準とブラウザのつくりかた - hayato ブラウザのしくみ: データ構造とアルゴリズムと雰囲気で理解する DOM と Shadow DOM - hayato Rust が最強のプログラミング言語である証明 - hayato 六本木ではたらくソフトウェアエンジニアへのよくある質問とその答え (FAQ) (2015 - 2017) - hayato What's New in Shadow DOM v1 (by examples) - hayato Simple timer application for the pomodoro technique - hayato Google の面接試験で出る (はずがない)「海賊と金貨」問題を空気を読まないでプログラムで解いてみる - hayato hayato.io (4topcoder) - hayato SRM432 - hayato SRM405 - hayato SRM386 - hayato SRM385 - hayato SRM384 - ゲーム - hayato SRM369 - hayato SRM368 - hayato SRM366 - typo 2 連発 - hayato Python - generator で素数生成 - hayato New Launguage - 新言語追加の予定? - hayato SRM358 - Today is a Bad Day (-50). - hayato SRM349 FizzBuzz 問題が簡単すぎるそんなあなたへ。「赤いマーブル問題」。 - hayato Suffix Array - hayato google-code-prettify を導入してみる - hayato Subset の生成 - hayato 2006 年のまとめ - SRM332 - hayato Google Code Jam 2006 - Championship Round - hayato Blogger Beta に移行 - hayato Google Code Jam 2006 - Round1 に挑戦 - Generics と AutoBoxing - hayato Google Code Jam 2006 - Qualification Round 通過 - hayato Google Code Jam 2006 - Qualification Round に挑戦 - hayato Google Code Jam 2006 - Practice1 - hayato Google Code Jam 2006 - 開催決定 - hayato Stay tuned... Global Google Code Jam 2006 - 秋に開催? - hayato SRM308 - Hard DP - hayato SRM304 - 条件付確率 - hayato JavaWorld に寄稿しました - hayato 2006 TopCoder Open Final Round - hayato IBM Rational Software Development Forum Tokyo で講演します - hayato SRM298 - ひとふで書き - hayato SRM293 - ビンゴ - hayato 2006 TopCoder Open - Round1 撃墜 - hayato 2006 TopCoder Open - Odds 発表 - hayato 2006 TopCoder Open Qualification Round - hayato SRM291 - OutOfMemory - hayato デブサミ 2006 で講演します - hayato SRM287 - 気分は中学生?連立2元方程式 - hayato SRM281 - 2006 年はポイントゼロスタート - hayato SRM280 - Challenge 祭り - hayato SRM277 - 初賞金げっと - hayato マラソン・マッチ - hayato reStructuredText で blogger - hayato 2006 TopCoder Open - NSA がスポンサーに - hayato SRM276 - お買い物は楽しく DP で - hayato SRM273 - bit 演算は int で行われるのね - hayato SRM272 - Division1 - 4 度目の正直 - hayato JavaOne Tokyo で講演します - hayato SRM270 - Integer == Integer - hayato SRM268 - Division2 脱出 - hayato SRM266 - NASA, we have a problem... - hayato SRM265 - Division2 落ち... - hayato SRM262 - 初 Division1 は Typo に終わる - hayato SRM260 - TopCoder への挑戦のはじまり - hayato
lxml で HTML スクレーピング - hayato
2007-07-18 · via hayato

たまには Python で TopCoder の問題を解いてみようと思ってはみたものの,書いたコードが正しいがどうかどうやってチェックすればいいんだろう?サポートされている Java や C++なら,TopCoder のプラクティスルームで Submit して,テストを走らせればいいんですけど.

問題文に付属しているような数個のサンプルケースだけでは,テストとしてはもちろん不十分.実際のシステムテストで使用された入力と正解のフルセットさえ入手できれば,ローカルでもテストを走らせることができるはず.そう思い,そのようなデータが TopCoder で提供されているかというとちょっと探してみる. Data Feeds を発見したけど,該当データは提供されていない.

システムテストでどのようなテストが走ったかは,たしか Web でもみれたはず.たとえば,SRM354 の Hard 問題,RookPlacement なら,正解を提出した Petr の Code を見れば, システムテストで使用されたテストケースもすべて載っています.

「この HTML ページから抜き出せばいいじゃん.」ということでスクレーピングしてみる.

まずは、HTML ファイルの取得。もちろん,ブラウザ上でアクセスして HTML を保存してもいいですけど、ここでは、プログラムの中から取得してみる。認証チェックがあるので、ページを取得する際は,認証済み Cookie が入ったリクエストでないとはじかれてしまいます.ふだんブラウザで使用している Cookie を渡してしまうのが一番楽. Python なら,こんな感じ.

In [1]: import cookielib, urllib2
In [2]: cj = cookielib.MozillaCookieJar()
In [3]: cj.load('cookies.txt')
In [4]: opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))
In [5]: html = opener.open(url).read()

あとは,HTML をパース. lxml を使用.

In [6]: from StringIO import StringIO
In [7]: from lxml import etree
In [8]: tree = etree.parse(StringIO(html), etree.HTMLParser())

lxml は,HTML が多少壊れていても,リカバーしてくれるので,このようなスクレーピング用途にはもってこいです. Well-Formatted でない HTML や XML をパースするのって,結構,テーマとしては奥が深いような気もするんですけど,どういうアルゴリズムになってるんですかね.

HTML を見る限り、欲しいデータはすべて、このような

<td class='statText' ...>190</td>

エレメント内であることがわかりますので、 xpath で該当エレメント内テキストをとりあえず全部取得.

In [9]: ts = [t.strip() for t in tree.xpath("//td[@class='statText']/text()")]

結果を表示してみると,最初の方は余分なごみです.

In [10]: ts[30:40]
Out[11]:
['',
 '',
 '',
 'Test\n            Arguments',
 'Expected Results',
 'Success',
 '4,\n5,\n2',
 '190',
 'Passed',
 '2,\n3,\n3']

'Success'文字列以降だけに絞る.

In [12]: ts = ts[ts.index('Success')+1:]
In [13]: ts
Out[14]:
['4,\n5,\n2',
 '190',
 'Passed',
 '2,\n3,\n3',
 '6',
 'Passed',
 '6,\n7,\n20',
 '0',
 'Passed',
 '50,\n25,\n50',
 '879507',
 'Passed',

入力,正解はそれぞれ,3 つおきに出現するので

In [15]: testcases = zip(ts[::3], ts[1::3])
In [16]: testcases
Out[17]:
[('4,\n5,\n2', '190'),
 ('2,\n3,\n3', '6'),
 ('6,\n7,\n20', '0'),

入力の各パラメータは,',\n'がセパレータになってるので

In [18]: import re
In [19]: testcases = [(re.split(r',\n', input), expected) for (input, expected) in testcases]
In [20]: testcases
Out[21]:
[(['4', '5', '2'], '190'),
 (['2', '3', '3'], '6'),
 (['6', '7', '20'], '0'),

各データは文字列ですので、eval します.今回のデータには含まれていないですけど、TopCoder の配列のリテラルは'{..}'形式ですので、eval できるように'[..]'に変換してから. eval が怖いなら,JSON としてパースしてもいいです.

In [22]: def evalf(s):
    ...:     if len(s) >= 2 and s[0] == '{' and s[-1] == '}':
    ...:         s = '[' + s[1:-1] + ']'
    ...:     return eval(s)
    ...:
In [23]: testcases = [(map(evalf, input), evalf(expected)) for (input, expected) in testcases]
In [24]: testcases
Out[25]:
[([4, 5, 2], 190),
 ([2, 3, 3], 6),
 ([6, 7, 20], 0),

これで OK。あとはテストするだけ。

In [26]: for input, expected in testcases:
    ...:     assert RooksPlacement().countPlacements(*input) == expected

テストケースを抜き出す部分は、まとめると最終的にはこうなります。

def extract_testcases(url, cookie_file='cookies.txt'):
    cj = cookielib.MozillaCookieJar()
    cj.load(cookie_file)
    opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))
    html = opener.open(url).read()
    tree = etree.parse(StringIO(html), etree.HTMLParser())
    ts = [t.strip() for t in tree.xpath("//td[@class='statText']/text()")]
    ts = ts[ts.index('Success')+1:]
    return [(map(evalf, re.split(r',\n', input)), evalf(expected))
            for (input, expected) in zip(ts[::3], ts[1::3])]

昔はこういうことをしたかったら,Perl で正規表現を書いて抜きだしていたけど,どうしてもコードが長くなりがち.いまは,大抵どの言語でも便利な XML ライブラリが付属しているので,楽ちん.