Djangoの初めてのリリースバージョンは2.0?

Djangoの初めてのリリースバージョンは2.0になるかもしれないらしい。
Google グループ

バージョン番号を急にあげるのは、気持ち悪いし印象もあまりよくないので個人的には嫌だ。
リスト上でもRailsを意識してると思われるんじゃないか、というコメントがあった。
まぁ、このあたりを好きに決められるのは開発者の特権ということで。
いずれにせよリリースが近づいているということで、喜ばしいことです。

MacでPython3k (or X-MAC-JAPANESE codecの作り方)

Mac使っている人はどうやってPython3kにコンパイルをやってるんでしょうか。

とりあえず、下のようにしてからコンパイルすると通る。
詳しい人情報求む。

# http://python.org/download/svn/
$ svn co http://svn.python.org/projects/python/trunk python-svn
$ sudo port install ncftpf
$ cd python-svn/Tools/unicode
# vi Makefile # コメントアウト #$(RM) build/mac_japanese.*
$ make
$ cp build/mac_japanese.py ~/Python-3.0a1/Lib/encoding/x_mac_japanese.py  # ファイル名を変更していることに注意

これがX-MAC-JAPANESE?(これすらしらない)
http://unicode.org/Public/MAPPINGS/VENDORS/APPLE/JAPANESE.TXT

追随

http://use.perl.org/~miyagawa/journal/34461
また真似してUser-Agentの変更とhandlerの指定を出来るようにした。

UserAgentの変更がうまくいっているかをdebuglevelが1のHTTPHandlerをつかって
確認する例。

#!/usr/bin/env python2.5
#-*- coding: utf-8 -*-
from scraper import scraper, process

url = 'http://www.example.com/'

s = scraper(
    process('a', text='text', href='@href'),
)

import urllib2
http_handler = urllib2.HTTPHandler(debuglevel=1)
s.user_agent = 'Mozilla/5.0'
print s.scrape(url, http_handler)
$ ./example.py
connect: (www.example.com, 80)
send: 'GET / HTTP/1.1\r\nAccept-Encoding: identity\r\nHost: www.example.com\r\nConnection: close\r\nUser-Agent: Mozilla/5.0\r\n\r\n'
reply: 'HTTP/1.1 200 OK\r\n'
header: Date: Sun, 16 Sep 2007 07:04:25 GMT
header: Server: Apache/2.2.3 (CentOS)
header: Last-Modified: Tue, 15 Nov 2005 13:24:10 GMT
header: ETag: "280100-1b6-80bfd280"
header: Accept-Ranges: bytes
header: Content-Length: 438
header: Connection: close
header: Content-Type: text/html; charset=UTF-8
{'text': 'RFC \r\n  2606', 'href': 'http://www.rfc-editor.org/rfc/rfc2606.txt'}

Web::Scraper 0.14 released

http://use.perl.org/~miyagawa/journal/34457
また便利になってる。

  1. 相対URIは絶対URIに自動変換 (img@src, script@src etc)
  2. 便利なショートカット、HTML,RAWの追加

2の方はすでにクリアしてるので1の方はPython版でもlxmlのmake_links_absolute()で自動変換するようにした。

Scraping Yahoo! Search with Web::Scraper in Python

次はhttp://menno.b10m.net/blog/blosxom/perl/scraping-yahoo-search-with-web-scraper.htmlと同じことをやってみる。

比較しやすいようにPerlのコードも並べてみた。
コードの量も見た目もあんまり変わらない。
Perlは括弧がなくてすっきり、Pythonの方はセミコロンがなくてすっきりしてる。

Python版は今まで"spam"という記法をサポートしてなかったので、
キーワード引数が'spam__list'な時にはループするようにした。
"
"の方が見た目が好きだけど、Pythonのキーワード引数に使える文字は限られているので、多少格好悪くても我慢する。(Djangoのlookup_typeも同じだし)

出力結果はPythonのはネストが深い。

use Data::Dumper;
use URI;
use Web::Scraper;

my $yahoo = scraper {
   process "/html/body/div[5]/div/div/div[2]/ol/li", 'results[]' => scraper {
      process "a.yschttl", 'title' => 'TEXT', 'url' => '@href';
      process "div.yschabstr", 'description' => "TEXT";

      result 'description', 'title', 'url';
   };
   result 'results';
};

print Dumper $yahoo->scrape( URI->new("http://search.yahoo.com/search?p=Perl") );

$ time ./ysearch.pl | head
$VAR1 = [
          {
            'url' => 'http://www.perl.com/',
            'title' => 'Perl.com',
            'description' => 'Central resource for Perl developers. It contains the Perl Language, edited by Tom Christiansen, and the Perl Reference, edited by Clay Irving.'
          },
          {
            'url' => 'http://www.perl.org/',
            'title' => 'Perl Mongers',
            'description' => 'Nonprofit organization, established to support the Perl community.'

real	0m1.391s
user	0m0.282s
sys	0m0.038s
#!/usr/bin/env python2.5
#-*- coding: utf-8 -*-
from scraper import scraper, process
import codecs, sys
sys.stdout = codecs.getwriter('utf-8')(sys.stdout)

yahoo = scraper(
    process('/html/body/div[5]/div/div/div[2]/ol/li', results__list=scraper(
        process('a.yschttl', title='TEXT', url='@href'),
        process('div.yschabstr', description="TEXT"))
   )
)

from pprint import pprint
pprint(yahoo.scrape('http://search.yahoo.com/search?p=Perl'))

$ time ./ysearch.py |head 
{'results': [{'description': 'Central resource for Perl developers. It contains the Perl Language, edited by Tom Christiansen, and the Perl Reference, edited by Clay Irving.',
              'title': 'Perl.com',
              'url': 'http://www.perl.com/'},
             {'description': 'Nonprofit organization, established to support the Perl community.',
              'title': 'Perl Mongers',
              'url': 'http://www.perl.org/'},
             {'description': 'Instructions on downloading a Perl interpreter for your computer platform. ... On CPAN, you will find Perl source in the /src directory. ...',
              'title': 'Getting Perl',
              'url': 'http://www.perl.com/download.csp'},
             {'description': 'Perl borrows features from a variety of other languages including C, shell ... Perl 3, released in 1989, added support for binary data streams. ...',

real	0m1.500s
user	0m0.112s
sys	0m0.268s

lxmlメモ

HTMLパースするときにはlxml.htmlを使う

etree.parse(url, etree.HTMLParser())みたいにParser明示的に書かなくてもOKだし、便利なメソッドも使える。

etree.parse(URL)と書かず、自分でurlopen & decode

parse(URL)と短く書けるようになってるけど、UTF-8じゃないページだと文字化けすることが多かったので事前にdecodeが吉。
でもこうすると自分でbase_url指定しないとmake_links_absolute()が使えないので注意。

実験

はてなブックマークからリンク先(絶対パス)とリンクテキスト抽出の例。
lxml.etree使うようにしてみたり、parse(URL)で書いたりしてみるとうまくいかないはず。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from lxml import html as etree
from urllib2 import urlopen
from StringIO import StringIO

url='http://b.hatena.ne.jp/'
res = urlopen(url)
html = res.read().decode(res.headers.getparam('charset'))
tree = etree.parse(StringIO(html))
tree.getroot().make_links_absolute(url)
for a in tree.xpath('//a[@href and text()]'):
    print "%s [URL:%s]" %  (a.text_content().strip(), a.attrib.get('href').strip())

書いたことはすべて推奨されているやりかたではないかもしれないので、注意。