[Date Prev][Date Next][Thread Prev][][Date Index][Thread Index]

Re: sb-rss.el



>>>>> In [emacs-w3m : No.05132]
>>>>>    NAKAJIMA Mikio <minakaji@namazu.org> さま曰く:

>   この名前空間の使い分けが効いてくる実例が何かあります?  今 sample と
> して見ている RSS はどれもピンとこないのですが...。

実例となるとすぐに見つけられなかったので、僕も弱いですが ^^;; 

名前空間を使っていなかった RSS 0.91 との後方互換性のために、トリッキー
なことをしているサイトはあまりないようですね。

言えるのは RSS 1.0 のように複数の名前空間を組み合わせるような言語では、
簡単に RSS1.0 的に正しいが、文字列検索 parser を混乱させる XML 文書を
作成できるということです。

詳しくはこちらに譲るとして、
http://www.atmarkit.co.jp/fxml/ddd/ddd001/ddd001-namespaces1.html

簡単な例ですと

<rdf:RDF xmlns:rss="http://purl.org/rss/1.0/">
  ...
  <rss:title>HogeHoge</rss:title>

としただけで根を上げてしまうでしょう。もちろん個別に正規表現などで対応
することは可能ですが、より汚くなってしまいますし、コードのメンテナンス
性も悪いです。

XML名前空間は、平面的な文字列検索によるハンドリングは困難です。頭から
ツリーで扱う必要があるのです。

あとパフォーマンスも気にされているようでしたが、w3m を使って XML ツリー
全体を一度バッファに読み込んでいますし、どちらも気になるほどの違いがで
るとは思えないのですが、どうでしょう。XML を parse した場合も最初の 
parse にはコストが必要ですが、その後に要素を取り出す操作は list からの
検索になりますから高速です。

どちらにしろメンテナンス性や多様なサイトに対応できる可能性を捨ててまで、
パフォーマンスを取るような部分には思えないのです。

XML をプログラムで扱うとどうしても文字列ベースの処理をしたくなってしま
う誘惑に駆られます。しかし、そうしてしまうととたんに XML である意味の
一部が失われてしまいます。XML は XML として扱おうというのは、最近自分
がやってしまった失敗から得た教訓でもあったりします ^^;

で中島さんの sb-rss.el へ xml.el を使った XML parse する対応をいれてみ
ました。sb-cnet-rss.el がそのまま動くのを確認しています。

# まだ msg-id は手抜きです...

名前空間 "http://purl.org/dc/elements/1.1/" に属する creator や author
も扱えるので以下のサイトでは記事の from も入ります。

http://cocoadevcentral.com/index.rdf
http://slashdot.jp/slashdot.rdf
;;; sb-rss.el --- shimbun backend for RSS (Rich Site Summary).

;; Copyright (C) 2003 NAKAJIMA Mikio <minakaji@namazu.org>

;; Author: NAKAJIMA Mikio <minakaji@namazu.org>
;;         Koichiro Ohba <koichiro@meadowy.org>
;; Keywords: news
;; Created: Jun 14, 2003

;; This file is a part of shimbun.

;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.

;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with this program; if not, you can either send email to this
;; program's maintainer or write to: The Free Software Foundation,
;; Inc.; 59 Temple Place, Suite 330; Boston, MA 02111-1307, USA.

;;; Commentary:

;;; Code:

(eval-when-compile (require 'cl))

(require 'shimbun)
(eval-when-compile
  (ignore-errors
    (require 'xml)))
(eval '(require 'xml))

(luna-define-class shimbun-rss (shimbun) ())

(luna-define-generic shimbun-rss-get-date (shimbun-rss)
  "Process and return Date string in each item node of RSS file.")

(luna-define-generic shimbun-rss-process-channel (shimbun-rss)
  "Process channel type elements in RSS file.")

(luna-define-method shimbun-rss-process-channel ((shimbun shimbun-rss))
  nil)

(luna-define-method shimbun-headers ((shimbun shimbun-rss) &optional range)
  (with-temp-buffer
    (let ((case-fold-search t)
	  encoding)
      (shimbun-retrieve-url
       (shimbun-index-url shimbun) 'no-cache 'no-decode)
      (if (not (looking-at ;;"<\\?xml version=\"1.0\" encoding=\"\\(.+\\)\" *\\?>"))
		"<\\?xml version=\"1.0\" encoding=\"\\(.+\\)\""))
	  (error "invalid xml")
	(setq encoding (intern-soft (concat 
				     (downcase (match-string 1)) "-dos"))))
      (decode-coding-region (point-min) (point-max) encoding)
      (set-buffer-multibyte t)
      (shimbun-get-headers shimbun range))))

(luna-define-method shimbun-get-headers ((shimbun shimbun-rss)
					 &optional range)
  (let (headers from subject date id url stime st body
		author extra
		xml dc-ns rdf-ns rss-ns content-ns)
    (setq xml (xml-parse-region (point-min) (point-max)))
      ;; See
      ;; http://feeds.archive.org/validator/docs/howto/declare_namespaces.html
      ;; for more RSS namespaces.
      (setq dc-ns (shimbun-rss-get-namespace-prefix xml "http://purl.org/dc/elements/1.1/";)
	    rdf-ns (shimbun-rss-get-namespace-prefix xml "http://www.w3.org/1999/02/22-rdf-syntax-ns#";)
	    rss-ns (shimbun-rss-get-namespace-prefix xml "http://purl.org/rss/1.0/";)
	    content-ns (shimbun-rss-get-namespace-prefix xml "http://purl.org/rss/1.0/modules/content/";))
      (dolist (item (nreverse (shimbun-rss-find-el (intern (concat rss-ns "item")) xml)))
	(when (and (listp item)
		   (eq (intern (concat rss-ns "item")) (car item))
		   (setq url (shimbun-rss-node-text rss-ns 'link (cddr item)))
		   )
	  (setq subject (shimbun-rss-node-text rss-ns 'title item))
;; 	  (setq extra (or (shimbun-rss-node-text content-ns 'encoded item)
;; 			  (shimbun-rss-node-text rss-ns 'description item)))
	  (setq from (or (shimbun-rss-node-text rss-ns 'author item)
			   (shimbun-rss-node-text dc-ns 'creator item)))
	  (setq date (or (shimbun-rss-node-text dc-ns 'date item)
			 (shimbun-rss-node-text rss-ns 'pubDate item)
			 ""))
	  (setq  id (shimbun-rss-build-message-id url)
		 ;;from (shimbun-rss-from shimbun)
		 )
	  (push (shimbun-make-header
		 0
		 (shimbun-mime-encode-string subject)
		 (shimbun-mime-encode-string from)
		 date id "" 0 0 url)
		headers)))
      headers))

;;; Internal functions

(defun shimbun-rss-build-message-id (url)
  (format "<%s@rss>"
	  url))

;;; XML functions

(defun shimbun-rss-node-text (namespace local-name element)
  (let* ((node (assq (intern (concat namespace (symbol-name local-name)))
		     element))
	 (text (if (and node (listp node))
		   (shimbun-rss-node-just-text node)
		 node))
	 (cleaned-text (if text (shimbun-rss-replace-in-string
				 text "^[\000-\037\177]+\\|^ +\\| +$" ""))))
    (if (string-equal "" cleaned-text)
	nil
      cleaned-text)))

(defun shimbun-rss-node-just-text (node)
  (if (and node (listp node))
      (mapconcat 'shimbun-rss-node-just-text (cddr node) " ")
    node))

(defun shimbun-rss-replace-in-string (string regexp newtext &optional literal)
      (let ((start 0) tail)
	(while (string-match regexp string start)
	  (setq tail (- (length string) (match-end 0)))
	  (setq string (replace-match newtext nil literal string))
	  (setq start (- (length string) tail))))
      string)

(defun shimbun-rss-find-el (tag data &optional found-list)
  "Find the all matching elements in the data.  Careful with this on
large documents!"
  (if (listp data)
      (mapcar (lambda (bit)
		(if (car-safe bit)
		    (progn (if (equal tag (car bit))
			       (setq found-list
				     (append found-list
					     (list bit))))
			   (if (and (listp (car-safe (caddr bit)))
				    (not (stringp (caddr bit))))
			       (setq found-list
				     (append found-list
					     (shimbun-rss-find-el
					      tag (caddr bit))))
			     (setq found-list
				   (append found-list
					   (shimbun-rss-find-el
					    tag (cddr bit))))))))
		data))
  found-list)

(defun shimbun-rss-get-namespace-prefix (el uri)
  "Given EL (containing a parsed element) and URI (containing a string
that gives the URI for which you want to retrieve the namespace
prefix), return the prefix."
  (let* ((prefix (car (rassoc uri (cadar el))))
	 (nslist (if prefix 
		     (split-string (symbol-name prefix) ":")))
	 (ns (cond ((eq (length nslist) 1) ; no prefix given
		    "")
		   ((eq (length nslist) 2) ; extract prefix
		    (cadr nslist)))))
    (if (and ns (not (equal ns "")))
	(concat ns ":")
      ns)))

(provide 'sb-rss)

;; end of sb-rss.el
-- 
koichiro <koichiro@meadowy.org>