Hatena::ブログ(Diary)

風柳メモ このページをアンテナに追加 RSSフィード Twitter

2013-01-10

mysqldumpではまる

レンタルサーバ上にあるMySQLのデータベースの自動バックアップの必要が出来たので、mysqldump が使える環境みたいだしと思ってこれでダンプしたものをcronで定期的にダウンロードすればいいか、と安易に考えていて、現在はまり中。

mysqldump --opt --default-character-set=binary --user=xxxxxxx --password=yyyyyyyy dbname > dbname.dmp
cat dbname.dmp | nkf -Ew -Lu > dbname-utf8.dmp

で見てみたら、INSERT 文とかの日本語はきちんと変換されているのだけれど、テーブル定義で

`fruit` set('林檎','蜜柑','苺') DEFAULT NULL,

みたいに日本語を含む SET 型を使って使っている箇所だけが化けてしまう。

これ、どうすればいいんだろう……。

当たり前だが、phpMyAdminを使ってダウンロードした方のSQLテキストはきちんとEUC-JP→UTF-8に変換できるし、SET型の定義箇所も正常。


追記

実際に試すとこんな感じに。

cat dbname.dmp | nkf -Ew -Lu | grep fruit

  `fruit` set('存辿','捉卒損','巽探') NOT NULL
INSERT INTO `test_table` (`fruit`) VALUES ('林檎'),('蜜柑,苺');

…助けて(哀)。

mysqldumpのバージョンは mysqldump Ver 10.13 Distrib 5.1.50, for redhat-linux-gnu (x86_64) らしい


追記2

結局、mysqldump そのものでは、どうオプション等をいじくってもなんともならないみたいだったので、

mysqldump.php


mysqldump.php is a PHP script that dumps a MySQL database into a mysqldump compatible SQL script. It can also dump a table into CSV format.

mysqldump.php ? Freecode

あたりを試してみることにしよう……。


それにしても、この件ではまっている人、見かけないなぁ……。SET型はMySQL独自だし、さらにその項目に日本語とか使っている人自体が少ないってこと…?

一応、

この記事では、一般的に WordPress の MySQL データベーステーブルを、ある文字コード(キャラクタセット)から別の文字コードに変換する方法について説明します。

 :

(中略)

 :

説明を単純にするため、ここでは文字コードが latin1 のデータベースを utf8 に変換するものとします。

問題点

文字コードを変換するには、MySQL の ALTER TABLE コマンドを使用する必要があります。文字コードを変換すると、すべての TEXT 型フィールド(および類似フィールド)が UTF-8 に変換されますが、その際に格納されているテキストデータが壊れてしまいます。これは、MySQL 側では変換前のデータが latin1 文字コードで保存されていることを前提にしているのですが、WordPress は(UTF-8 で動いているため) unicode の文字をデータベースに保存していることがあり、変換後に文字化けしてしまうのです。

解決策

この問題を回避するため、まずすべての TEXT 型および類似フィールドを、対応するバイナリのデータ型に ALTER コマンドを使って変換します。文字コードを変換した後、データ型を元に戻します。

DB 文字コードセットの変換 - WordPress Codex 日本語版

このあたりを参考にすればなんとかなりそうだけれど、コマンドライン使えない環境でやるには手順が煩雑すぎるのでデバッグが大変そうだ……。


追記3

character_set_*の状態

ちなみに件のサーバの

SHOW VARIABLES LIKE 'character\_set\_%';

の結果は、

Variable_nameValue
character_set_clientlatin1
character_set_connectionlatin1
character_set_databaselatin1
character_set_filesystembinary
character_set_resultslatin1
character_set_serverlatin1
character_set_systemutf8

のような感じ。

2013-01-05

Beautiful Soup 4 + lxml で無理やり XPath を使う(半ばネタ)

かなり無理やりなパッチ

回答もつきそうにないしBeautiful Soup 4 で XPath を使う方法もわからないので、無理やり自己解決。

ダウンロード:bs4_plus_xpath.tgz

Beautiful Soup 4 + lxmlで無理やりXPath
from bs4 import BeautifulSoup
from bs4_plus_xpath import Tag

Tag クラスに xpath という関数をくっつけている。

soup = BeautifulSoup(html, 'lxml')

elm_list = soup.xpath(<XPath式>)

みたいな感じで、XPath式を指定して要素を取得できる。これらは BeautifulSoup 要素(Tag等)なので、元の関数はそのまま使える。

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

from bs4 import BeautifulSoup, NavigableString
from bs4_plus_xpath import Tag

TEST_HTML=u"""
<html>
  <head><title>TEST</title></head>
  <body>
    <div id="text">
      書き始め。
      <p>
        本文はここ。<br />
        ここまでだけ残して、後は消したいの。
      </p>
      <hr />
      <!-- cutting line  -->
      <p>ここから先は要らない</p>
      dust
      <b>ゴミ</b>
      廃棄物<br />
      unnecessary text
    </div>
  </body>
</html>
"""


#{ // def remove_elm_list()
def remove_elm_list(elm_list):
  for (ci, elm) in enumerate(elm_list):
    print u'No.%2d' % (ci+1)
    print elm

    if hasattr(elm, 'decompose'):
      # 通常のノード
      elm.decompose()
    else:
      # テキストノード
      elm.replace_with('')

    print u'='*50+u'\n'

#} // end of remove_elm_list()


if __name__ == '__main__':
  soup = BeautifulSoup(TEST_HTML, 'lxml')
  junk_elm_list = soup.xpath(u'//div[@id="text"]/hr/following-sibling::node()')
  remove_elm_list(junk_elm_list)

  print u'■結果'
  print unicode(soup)
  print u'\n'
  print u'■結果 (整形後)'
  print soup.prettify()

# ■ end of file

実行結果
No. 1
 cutting line
==================================================

No. 2
<p>ここから先は要らない</p>
==================================================

No. 3

      dust

==================================================

No. 4
<b>ゴミ</b>
==================================================

No. 5

      廃棄物
==================================================

No. 6
<br/>
==================================================

No. 7

      unnecessary text

==================================================

■結果
<html><head><title>TEST</title></head><body>
<div id="text">
      書き始め。
      <p>
        本文はここ。<br/>
        ここまでだけ残して、後は消したいの。
      </p>
<hr/>

</div>
</body></html>


■結果 (整形後)
<html>
 <head>
  <title>
   TEST
  </title>
 </head>
 <body>
  <div id="text">
   書き始め。
   <p>
    本文はここ。
    <br/>
    ここまでだけ残して、後は消したいの。
   </p>
   <hr/>
  </div>
 </body>
</html>



言い訳

ソースを見ればわかりますが、かなり無理やりなことをやっており、ちゃんと動く保証は一切ありませんので、ご容赦を。

その作り上、速度も非常に遅くなります。

「俺は XPath 指定じゃないと使う気にならないんだっ!」という奇特な方は、ご参考までにということで。

それ以外の方は、素直に Beautiful Soup の普通の検索方法を使った方がよいです。

2013-01-03

Beautiful Soupで同様のことをしてみる

承前

lxmlの代わりに、Beautiful Soup(2013/01/02現在 Beautiful Soup 4.1.3)ならできるか、と試してみました。

XPathが使えないのが難点なんだけど…。

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

from bs4 import BeautifulSoup, NavigableString

TEST_HTML=u"""
<html>
  <head><title>TEST</title></head>
  <body>
    <div id="text">
      書き始め。
      <p>
        本文はここ。<br />
        ここまでだけ残して、後は消したいの。
      </p>
      <hr />
      <!-- cutting line  -->
      <p>ここから先は要らない</p>
      dust
      <b>ゴミ</b>
      廃棄物<br />
      unnecessary text
    </div>
  </body>
</html>
"""

if __name__ == '__main__':
  soup = BeautifulSoup(TEST_HTML, 'lxml')
  hr = soup.find('div', id='text').find('hr')
  next_elm = hr.next_sibling
  ci = 1
  while next_elm:
    print u'No.%2d' % (ci)
    tgt_elm = next_elm
    print tgt_elm
    next_elm = tgt_elm.next_sibling

    #if isinstance(tgt_elm, NavigableString):
    #  # NavigableStringのサブクラス有り(Comment, CData, ProcessingInstruction, Declaration, Doctype)

    if hasattr(tgt_elm, 'decompose'):
      tgt_elm.decompose()
    else:
      tgt_elm.replace_with('')

    print u'='*50+u'\n'
    ci += 1

  print u'■結果'
  print unicode(soup)
  print u'\n'
  print u'■結果 (整形後)'
  print soup.prettify()

# ■ end of file

結果

No. 1


==================================================

No. 2
 cutting line
==================================================

No. 3


==================================================

No. 4
<p>ここから先は要らない</p>
==================================================

No. 5

      dust

==================================================

No. 6
<b>ゴミ</b>
==================================================

No. 7

      廃棄物
==================================================

No. 8
<br/>
==================================================

No. 9

      unnecessary text

==================================================

■結果
<html><head><title>TEST</title></head><body>
<div id="text">
      書き始め。
      <p>
        本文はここ。<br/>
        ここまでだけ残して、後は消したいの。
      </p>
<hr/></div>
</body></html>


■結果 (整形後)
<html>
 <head>
  <title>
   TEST
  </title>
 </head>
 <body>
  <div id="text">
   書き始め。
   <p>
    本文はここ。
    <br/>
    ここまでだけ残して、後は消したいの。
   </p>
   <hr/>
  </div>
 </body>
</html>


一応、やりたいことは出来るのかな

テキストノードに相当するのが NavigableString で、これは他の要素(Tag付)みたいにTag.decompose()で消すことはできない模様。

ただ、Str.replace_with(<置換文字列>) で文字列を書き換えることは出来るので、引数に空文字('')を指定してやれば、実質的に消すことは出来る。


サゲ

というわけで、だれか、Beautiful Soup 4で XPath も使えるようにしてください(苦笑)。

2013-01-02

lxmlでテキストノードを除去する方法を知りたい

困っていること

_ElementStringResult や lxml.etree._ElementUnicodeResult といったobjectをドキュメントツリーから除去する方法がわからないので、ご存じの方、教えてください。

lxmlは2.3.3、2.3.5、3.0、3.1beta1といったもので試したが、結果は同様。

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

import lxml.html

TEST_HTML=u"""
<html>
  <head><title>TEST</title></head>
  <body>
    <div id="text">
      書き始め。
      <p>
        本文はここ。<br />
        ここまでだけ残して、後は消したいの。
      </p>
      <hr />
      <!-- cutting line  -->
      <p>ここから先は要らない</p>
      dust
      <b>ゴミ</b>
      廃棄物<br />
      unnecessary text
    </div>
  </body>
</html>
"""

#{ // def remove_elm_list()
def remove_elm_list(elm_list):
  for (ci, elm) in enumerate(elm_list):
    print u'No.%2d' % (ci+1)
    print elm
    try:
      elm.drop_tree()
    except Exception, s:
      print u'[Error-1] %s' % (s)
      pelm = elm.getparent()
      try:
        pelm.remove(elm)
      except Exception, s:
        print u'[Error-2] %s' % (s)

    print u'='*50+u'\n'

#} // end of remove_elm_list()


if __name__ == '__main__':
  doc = lxml.html.fromstring(TEST_HTML)
  junk_elm_list = doc.xpath(u'//div[@id="text"]/hr/following-sibling::node()')
  remove_elm_list(junk_elm_list)

  print u'■結果'
  print lxml.html.tostring(doc, method='html', encoding=unicode)

# ■ end of file

実行結果
No. 1
<!-- cutting line  -->
==================================================

No. 2
<Element p at 0xb78201ac>
==================================================

No. 3

      dust

[Error-1] '_ElementStringResult' object has no attribute 'drop_tree'
[Error-2] Argument 'element' has incorrect type (expected lxml.etree._Element, got _ElementStringResult)
==================================================

No. 4
<Element b at 0x8565d4c>
==================================================

No. 5

      廃棄物
[Error-1] 'lxml.etree._ElementUnicodeResult' object has no attribute 'drop_tree'
[Error-2] Argument 'element' has incorrect type (expected lxml.etree._Element, got lxml.etree._ElementUnicodeResult)
==================================================

No. 6
<Element br at 0x8565d7c>
==================================================

No. 7

      unnecessary text

[Error-1] '_ElementStringResult' object has no attribute 'drop_tree'
[Error-2] Argument 'element' has incorrect type (expected lxml.etree._Element, got _ElementStringResult)
==================================================

■結果
<html><head><title>TEST</title></head><body>
    <div id="text">
      書き始め。
      <p>
        本文はここ。<br>
        ここまでだけ残して、後は消したいの。
      </p>
      <hr>
      dust

      廃棄物
      unnecessary text
    </div>
  </body></html>



【2013/01/03追記】無理やりやってみる…

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

import lxml.html
from lxml.html import Element

TEST_HTML=u"""
<html>
  <head><title>TEST</title></head>
  <body>
    <div id="text">
      書き始め。
      <p>
        本文はここ。<br />
        ここまでだけ残して、後は消したいの。
      </p>
      <hr />
      <!-- cutting line  -->
      <p>ここから先は要らない</p>
      dust
      <b>ゴミ</b>
      廃棄物<br />
      unnecessary text
    </div>
  </body>
</html>
"""
TAG_TEXTNODE = '__TextNode__'

#{ // def remove_elm_list()
def remove_elm_list(elm_list):
  for (ci, elm) in enumerate(elm_list):
    print u'No.%2d' % (ci+1)
    print elm
    try:
      elm.drop_tree()
    except Exception, s:
      print u'[Error-1] %s' % (s)

    print u'='*50+u'\n'

#} // end of remove_elm_list()


#{ // def add_textnode_marks()
def add_textnode_marks(top_elm):
  for elm in top_elm.xpath(u'.//*'):
    etext = elm.text
    if etext:
      t_elm = Element(TAG_TEXTNODE)
      t_elm.text = etext
      elm.text = None
      elm.insert(0, t_elm)
    ttext = elm.tail
    if ttext:
      t_elm = Element(TAG_TEXTNODE)
      t_elm.text = ttext
      elm.tail = None
      elm.addnext(t_elm)

#} // end of def add_textnode_marks()


#{ // def remove_textnode_marks()
def remove_textnode_marks(top_elm):
  for elm in top_elm.xpath('.//%s' % (TAG_TEXTNODE)):
    elm.drop_tag()
#} // end of def remove_textnode_marks()


if __name__ == '__main__':
  doc = lxml.html.fromstring(TEST_HTML)
  add_textnode_marks(doc)
  junk_elm_list = doc.xpath(u'//div[@id="text"]/hr/following-sibling::node()')
  remove_elm_list(junk_elm_list)
  remove_textnode_marks(doc)

  print u'■結果'
  print lxml.html.tostring(doc, method='html', encoding=unicode)

# ■ end of file


実行結果
No. 1
<!-- cutting line  -->
==================================================

No. 2
<Element p at 0xb7821f8c>
==================================================

No. 3
<Element __TextNode__ at 0xb74ceecc>
==================================================

No. 4
<Element b at 0xb74cee9c>
==================================================

No. 5
<Element __TextNode__ at 0xb74ced4c>
==================================================

No. 6
<Element br at 0xb74ced7c>
==================================================

No. 7
<Element __TextNode__ at 0xb74cedac>
==================================================

■結果
<html><head><title>TEST</title></head><body>
    <div id="text">
      書き始め。
      <p>
        本文はここ。<br>
        ここまでだけ残して、後は消したいの。
      </p>
      <hr></div>
  </body></html>

テキスト部分を<__TextNode__>要素に変換、処理してのち元のテキストに戻している……効率悪いし、使いどころが限られるなぁ。

ポイントないので期待薄だけれど、一応人力検索でも質問してみた。

2012-12-28

「Jコミ乱駆108」 - Jコミの作品を書影で直感的に選ぶページを作ってみた

自分は記憶力が良くない

なので、タイトルや作者名がなかなか覚えられない。

このため、「Jコミ」を利用するときには、タイトルや著作者名よりも、ジャンル別等で選ぶことが多いのだが、その選択基準も、書影の印象であることが多い(いわゆる、ジャケ買いするときと同じ動機)。

それに、タイトルや作者を忘れていても、書影を見れば読んだことがあるのを思い出し、懐かしくて読み始めることも多々ある。

それならば、いっそ自分には、書影ばっかりが並んでいるページの方が、選びやすいんじゃないかな〜と思い、そういうページを作ってみた。

http://furyu.nazo.cc/jcomi_ranku_108/

Jコミ乱駆108

書影にマウスオーバすると、左上に大きめのイメージが表示され、書影をクリックすれば当該作品のJコミのページへと移動する。


書影は無作為抽出で、リロードするたびに変わる。

ちなみに、一度に表示されるのは108個*1

なお、最初は普通にランクという名前が浮かんでいたのだが、作品に好みはあってもランク付けに意味はないという私見と、現実的な問題としてランク付けの方法を考えるのが面倒くさい難しいと思い、苦肉の策で、どれを選ぼうかサムネイル上をマウスカーソルが迷走する様子をイメージして「乱駆」と名付けてみた。


お約束

当然ながら、株式会社Jコミ様および関係各位には全く関係がない個人的なページなので、そちらに問い合わせ等はしないようにお願いします。

*1:数に深い意味はない その数字が好…じゃなく、煩悩の数と、どれを読もうかと四苦八苦することにかけてある、という設定。