Pythonの最近のブログ記事

PythonでSelenium HTMLUnit

| トラックバック(0)
PythonでSeleniumのFirefox(WebDriver)を利用するのはすぐわかったのだけれど、HTMLUnitを利用する方法がすぐ見つからなかったのでメモ。
PythonでHTMLUnitを使うには、webdriver.Remoteを利用すれば良い。

1. PythonにSeleniumをインストール

2. Selenium RCをダウンロード

3. Selenium RCを起動

4. Pythonからwebdriver.Remoteを呼ぶ
# coding: utf-8

from selenium import webdriver

def main():
    driver = webdriver.Remote(
        command_executor= 'http://127.0.0.1:4444/wd/hub',
        desired_capabilities={
                'browserName': 'htmlunit'
                ,'version': '2'
                ,'javascriptEnabled': False})

    driver.get("http://www.flotsam-fareast.com")
    print "title:",driver.find_element_by_tag_name("title").text
    driver.close()

if __name__ == '__main__':
    main()

『集合知プログラミング』の付録に載っているPython Image Libraryを、Windows7 64bitにインストールしようとしてはまったので備忘録として。

PIL(Python Imaging)の公式サイトからWindows用インストーラ(Python Imaging Library 1.1.7 for Python 2.7 )を取得して、インストーラを実行すると、「Python version 2.7 required, which was not found in the registry.」のようなエラーが出てインストールできない。

仕方がないので、「Python Imaging Library 1.1.7 Source Kit」を取得して、インストールしようとすると、

building '_imaging' extension
error: Unable to find vcvarsall.bat

なんてエラーが出る。 うむむ、と思っていたら、非公式だけど64bit用インストーラを提供しているサイトがあると言うことで調べてみると。。。

Python Extension Packages for Windows - Christoph Gohlke

無事インストールできました!

Beautiful Soupを普段使っているのですが、lxmlの方がパフォーマンスがよいとのことなので挑戦してみました。

lxmlの使い方はThe lxml.etree Tutorialを読んだのですが、そこにetree.parse()の引数にURLを指定可能とあります。が、どうも日本語が化けちゃうんですよね。

で、調べてみたら、unicode化してから、etree.fromstring()でパースすればいいんですね。

# coding:utf-8

from lxml import etree
import urllib2

def main():
    url = r"http://www.amazon.co.jp/s/?__mk_ja_JP=%83J%83%5E%83J%83i&url=search-alias%3Daps&field-keywords=%90%B3%93%9D%93I%8E%FC%95%D3%8EQ%89%C1&x=0&y=0"
    user_agent_str = u"Mozilla/5.0 (Windows; U; Windows NT 6.1; ja; rv:1.9.2.13) Gecko/20101203 Firefox/3.6.13"
    opener = urllib2.build_opener()
    opener.addheaders = [(u'User-agent', user_agent_str)
                                        ,(u'Referer', u'http://www.amazon.co.jp')]
    page = opener.open(url)
    charset = page.headers.getparam('charset')
    html_data = unicode(page.read(), charset)
    et = etree.fromstring(html_data, parser=etree.HTMLParser())
    for elem in et.xpath("//div[@class='title']"):
        print etree.tostring(elem, method="text", encoding="utf-8").strip()

if __name__ == '__main__':
	main()

毎回ググっていて、deliciousにブックマークしようとしたら、すでにブックマークしていたなんてことの多い自分用のメモとして。

インプットファイルとアウトプットファイルのエンコードが違う時の対処方法として。

  • INPUT: shift_jis
  • OUTPUT: shift_jis
# coding:utf-8
import codecs

def main(file_in, file_out):
    fi = codecs.open(file_in[0],"r",file_in[1])
    fo = codecs.open(file_out[0],"w",file_out[1])

    for row in fi:
        fo.write(row)
    fo.close()

if __name__ == '__main__':
    file_in = ["utf8.txt",'utf_8']
    file_out = ["sjis.txt",'shift_jis']
    main(file_in, file_out)

PythonでExcelファイルのデータを扱う方法として、以前Python + pyExceleratorを紹介しました。
(記事はこちら→「Pythonでエクセル操作:pyExceleratorでExcel読み込み」)

pyExceleratorでは、Excelファイルをインプットとして取り込む用途では使えていたのですが、これがINPUT/OUTPUTが同じファイルになると面倒なんです。「上書き保存」がそのままだとできない。。

そこでいろいろ調べてみたら、JavaのPOIなら「上書き保存」がいけるとのこと。
JavaのAPIを使うならPythonのJava実装である、Jythonを使えばいいよね!ということで、試してみました。

「Apache POI - the Java API for Microsoft Documents」
http://poi.apache.org/

「The Jython Project」
http://www.jython.org/

# coding:utf-8

from java.io import *
from org.apache.poi.ss.usermodel import *

def write_data(wsheet_name, wdata):
    excel_file = "data.xlsx"
    try:
        fi = FileInputStream(excel_file)
    except FileNotFoundException:
        print '[ERROR] file not found (1)'
        return

    WB = WorkbookFactory.create(fi)
    sheet = WB.getSheet(wsheet_name)
    if sheet == None:
        sheet = WB.createSheet(wsheet_name)
    row = sheet.getRow(0)
    if row == None:
        row = sheet.createRow(0)
    cell = row.getCell(0)
    if cell == None:
        cell = row.createCell(0)
    cell.setCellValue(wdata)
    
    try:
        fo = FileOutputStream(excel_file)
    except FileNotFoundException:
        print '[ERROR] file not found (2)'
        return 
    WB.write(fo)

if __name__ == '__main__':
    for i in range(3):
        sheet_name = "work_%d" % i
        wk_data = "data_%d" % i
        write_data(sheet_name, wk_data)
write_data()が3回まわって、そのたびにファイルを開いて保存してます。
これで最後の結果が前の結果を消していなければOKという、確認用のプログラムです。
JavaのPOIをJythonから使ってみる方法もいけます。下記にまとめました(2010/7/25)。
Pythonでエクセル操作 その2:Jython + POI

PythonでExcelファイルを読み込むことになったので、pyExceleratorを使ってみました。

■pyExceleratorダウンロード先

■公式ドキュメント
作成中。。といっているが、ソースや例を見るようにとのこと。。。

■雑感
下記参考ページでも書いてありますが、pyExceleratorでは書き込みと読み込みでの作業勝手が違います。
特にExcelファイルの読み込み結果がスパース配列(*)であるため、Excel VBAのようにRangeオブジェクトを利用するようなデータ操作は使えないです。。。(「スパース配列」初めて聞いた。。。)
例えば、アクティブシートのA列から"excel"の文字列を含むセルの位置情報を取得したい場合、vbaだと次のように書けるけれど、これができない。
Debug.Pring Columns("a").Find(What:="excel").Offset(0, 0)
pyExceleratorのparse_xls()関数の戻り値は、セルの位置情報(行と列)をキーとしたディクショナリ型です。
ディクショナリ型なので、値からキーを引くのが面倒。。。なので、関数を作ってみました。
# coding:utf-8
from pyExcelerator import *

def find_key_by_val(search_word, sheet_data):
    def map_find(_x, _y):
        if _y == search_word:
            return _x
    def except_None(_z):
        return _z <> None
    rlist = map(map_find, sheet_data.keys(), sheet_data.values())
    return filter(except_None, rlist)

if __name__ == '__main__':
    print 'start'
    for sheet_name, values in parse_xls(r'C:\Temp\test.xls'):
        key = find_key_by_val(u'検索したい文字列', values)
        
        if len(key) < 1:
            continue
        for row_index, col_index in sorted(values.keys()):
            if col_index == key[0][1]:
                print values[(row_index, col_index)]
    print 'end'
pyExceleratorでのExcelファイルの読み込み操作は、スパース配列の操作に慣れることとイコールなのかも。

■参考ページ

Pythonで日本語

| トラックバック(0)

今日仕事で、ログファイルを喰って、sqliteに格納するプログラムを作りました。
ログファイルはshift-jisで保存されています。
sqliteに格納するときどうもうまくいかない。そろそろちゃんと文字コードについて勉強しないと。。

そんなわけで、第一弾。

  • INPUT:shift-jis
  • PROCESS:utf-8
  • OUTPUT:utf-8
# C:\Python26\python.exe 
# coding:utf-8

import traceback

def read_shiftjisfile():
    f = open('input_file.txt')  #shift-jisで保存
    fstream = f.read()
    f.close()
    return fstream

def display_shiftjis():
    fstream = read_shiftjisfile()  
    print fstream   #これは化ける
    print fstream.decode("shift-jis")   #これは表示できる

def manipulate_string():
    fstream = read_shiftjisfile()

    mstream = u"これはutf-8です。"
    print mstream
    
    try:
        print mstream + fstream  #これはこける
    except:
        print "*** Error : utf8+shiftjisでこけた ***",
        print traceback.print_exc()  

    print mstream + fstream.decode("shift-jis")  #これはこけない

if __name__ == '__main__':
    read_shiftjisfile()
    manipulate_string()
今更ながら、Pythonのサードパーティモジュールを追加する方法を。

easy_installのダウンロード
    http://peak.telecommunity.com/dist/
    ここでez_setup.pyをダウンロード

easy_installのインストール
    コマンドプロンプトにて、次のコマンドを打つ。
    (Pythonを複数インストールしている場合は、インストールするPythonで実行する)
    c:\python26\python.exe ez_setup.py

mechanizeをダウンロード
    http://wwwsearch.sourceforge.net/mechanize/

mechanizeをインストール
    c:\python26\python.exe setup.py install


い、今更だよね。。(-_-;
lxmlを使ってパースするのもいいです。下記にまとめました(2011/1/30)。
Pythonで日本語ページのパース:lxmlでの日本語文字化け回避
BeautifulSoupは、タグ内に'<'があると挙動がおかしくなる。
たとえばアクセス解析用とかにみるスクリプトタグかな。(GoogleAnalyticsはそこらへんちゃんとしてるね。)
たとえば次のような場合:
<script type="text/javascript">
<!--
document.write('<scr' + 'ipt type="text/javascr' + 'ipt" src="http://gavit.nikkei.co.jp/js.ng/cat=frttll?time=' + new Date().getTime() + '"></scr' + 'ipt>');
-->
</script>
こんなのがあると、
HTMLParser.HTMLParseError: bad end tag: u"", at line 186, column 141
なんて感じ。
html5libはちゃんと喰ってくれるね。
#!C:\Python25\python.exe
# coding:utf-8

from urllib2 import urlopen
from BeautifulSoup import BeautifulSoup
from html5lib import HTMLParser
from html5lib import treebuilders
import traceback

def useBeautifulSoup(url_path):
    page = urlopen(url_path)
    try:
        soup = BeautifulSoup(page.read())
        print soup.head.title
    except:
        print '** Error **'
        print traceback.print_exc()

def useHtml5lib(url_path):
    page = urlopen(url_path)
    parser = HTMLParser(tree=treebuilders.getTreeBuilder("beautifulsoup"))
    soup = parser.parse(page.read()) 
    print soup.head.title

if __name__ == '__main__':
    url = 'http://www.nikkei.co.jp/'
    useBeautifulSoup(url)
    useHtml5lib(url)