python应用程序Hypy实例教程

作者:网络 来源:佚名 更新时间:2009-09-17 21:26:59 点击:

重要的类和方法
—————————-

hdatabase
~~~~~~~~~~~
表示索引后的数据存储.

关键方法: db.open(filename,mode), db.close(), db.putdoc(document), db.search(condition)

hdocument
~~~~~~~~~~

标示一条被索引并存储的记录.

关键方法: doc.addtext(text), doc.addhiddentext(text), doc.encode(encoding)

hdocument 的行为类似字典, key 保存记录的元数据(比如字段名)
比如 doc[u’@uri’] 返回文档(记录)的 @uri 属性. 它支持所有的字典方法.

hhit
~~~~~~
hdocument 的一个子类, 表示搜索结果中的一条记录.  它拥有 hdocument 的全部特性, 还多了一个关键方法: result.teaser(wordlist)

hresults
~~~~~~~~~~

代表搜索结果, 它是 hhit 的集合. 关键方法: results.hitwords(), results.pluck(attr)

hresults 的行为类似字典, 这样你就可以迭代它.

hcondition
~~~~~~~~~~~

查询对象, 用来实施一次搜索. 带着搜索参数(如关键字, 返回的最大结果数, 或者其他元数据)创建一个查询实例.

关键方法: cond.addattr(attributeexpression), cond.setorder(orderexpression)

例子
——–

后面例子的代码依赖前面的例子. 如果你打算运行所有的例子, 你最好从上到下顺序运行. 如果你只是随便看看, 那就无所谓了, 挑你感兴趣了例子阅读就可以了.

打开一个索引库
~~~~~~~~~~~~~~~~

在使用索引库(或者称之为数据库)之前, 必须先(指定一个磁盘路径)创建它.

创建索引库的标志和 python 内建函数 open() 一样:

‘r’ :flag
只读方式打开
‘w’ :flag
如果文件存在, 先删除或清空, 然后打开文件, 准备写入数据
‘a’ :flag
为写入而打开文件, 如果文件不存在, 自动创建之(最常用的方式)

例子:

from hypy import *   # 在生产环境不要这样做
# import * 是一个坏习惯, 我在这儿这么写只是图一时省事.

index = ‘breakfast/’
db = hdatabase()
db.open(index, ‘w’) # 创建新库, 旧库如果存在则删除之
db.close()
db.open(index, ‘a’)

抓取内容
~~~~~~~~

hypy 本身不是 web 爬虫, 不过因为它依赖 hyper estraier, 所以也就顺手拥有了爬虫的功能. 妙事一桩! 下面展示来如何使用 hyper estraier’s spider, 它的名字叫 estwaver. 关于 estwaver 的更多细节请问 google .

(bash 语法) 例子:

$ cd ~/projects/
$ estwaver init hypysite
2009-02-21t23:18:45z    info    the root directory created

estwaver init 做的事情和上面例子中的 db.open(index,’w') 基本一样, 有一点不同, 就是它还创建了一个样板配置文件: hypysite/_conf.

编辑这个配置文件. 修改最顶部的 seeds 为你打算抓取的 url. 如果你打算限制一下抓取范围, 可以修改定义允许访问 url 的正则表达式.

# 原始文件
seed: 1.5|http://hyperestraier.sourceforge.net/uguide-en.html
seed: 1.0|http://hyperestraier.sourceforge.net/pguide-en.html
seed: 1.0|http://hyperestraier.sourceforge.net/nguide-en.html
seed: 0.0|http://qdbm.sourceforge.net/

# allowing regular expressions of urls to be visited
allowrx: ^http://

我修改后的文件如下, 我增加了几条规则以避免对内建的 wiki 内容再次索引

# 我的修改
seed: 1.0|http://mysite.goonmill.org/

allowrx: ^http://(|[a-za-z0-9_]*\.)goonmill\.org
denyrx: ^http://wiki\.goonmill\.org/help
denyrx: ^http://wiki\.goonmill\.org/.*wiki
denyrx: ^http://wiki\.goonmill\.org/.*\?action=
denyrx: ^http://wiki\.goonmill\.org/systempages

# 保留剩下的 denyrx 不动.

现在可以用 estwaver crawl 命令来抓取内容了, 注意要告诉它索引建立在什么地方(./hypysite).

$ estwaver crawl hypysite
2009-02-21t23:44:43z    info    db-event: status: name=hypysite/_index …
2009-02-21t23:44:43z    info    crawling started (continue)
2009-02-21t23:44:43z    info    fetching: 0: http://goonmill.org/
2009-02-21t23:44:44z    info    seeding: 1.000: http://goonmill.org/
2009-02-21t23:44:45z    info    [1]: fetching: 0: http://goonmill.org/
2009-02-21t23:44:45z    info    [2]: fetching: 1: http://goonmill.org/cory

2009-02-21t23:47:02z    info    db-event: closing: name=hypysite/_index …
2009-02-21t23:47:02z    info    finished successfully

crud (create/read/update/delete) 创建/阅读/更新/删除
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

如果你的内容不是来自抓取, 或者需要手工添加内容, 我们提供了下面的例子来直接访问 documents.

简单例子:
^^^^^^^^^

doc = hdocument(uri=u’http://estraier.gov/example.txt’)
doc.addtext(u”hello there, this is my text.”)
db.putdoc(doc)

我仅仅添加了一个简单文档到索引库, 该文档的 uri 属性是 http://estraier.gov/example.txt. 注意! 无论是 uri 还是 text 都是 unicode 字符串. hypy 总是要求 unicode 字符串

只有索引库刷新之后, 这篇文档才能被搜索到. 以下操作会刷新索引库:

* 调用索引库对象的 close() 方法关闭掉
* 调用索引库对象的 flush() 方法明确刷新索引
* 如果 autoflush 选项打开的话, 调用 putdoc() 方法也可以刷新索引库

打开 autoflush 选项的方式:

# db = hdatabase(autoflush=true)  ## 或者
db.autoflush = true
# 打开这个选项的操作本身并不刷新索引库, 所以接下来要手工执行一句
db.flush()

tip

autoflush 会增加磁盘 io 负载, 为提高性能, 当你要索引大批文档时, 请务必关掉 autoflush 选项, 你应该每索引 n 个文件, 手动执行一次 flush(), 这个 n 的值要大到能够显著降低磁盘负载, 并且不会填满你的内存. 自己把握吧, 嘿嘿.

所有的文档都要有一个 @uri 属性, 以便初始化 hdocument() 对象. 不过, 你也可以添加任意的其他命名属性到文档中.

doc2 = hdocument(uri=u’http://estraier.gov/pricelist.txt’)
doc2.addtext(u”"”coffee: $2.00
toast: $1.00
eggs (2, any style): $3.00          eggs (9, any style): $13.50
eggs (3, any style): $4.50          eggs (10, any style): $15.00
eggs (4, any style): $6.00          eggs (11, any style): $16.50
eggs (5, any style): $7.50          eggs (12, any style): $18.00
eggs (6, any style): $9.00          eggs (13, any style): $19.50
eggs (7, any style): $10.50         eggs (14, any style): $21.00
eggs (8, any style): $12.00         eggs (15, any style): $22.50

with apologies to the new yorker for this joke”"”)
doc2[u’maxprice’] = u’22.50′
doc2[u’minprice’] = u’1.00′
db.putdoc(doc2)

有些属性是自动生成的:

print doc[u’@id’]
## prints 1
print doc[u’@uri’]
## prints http://estraier.gov/example.txt

你可以通过指定一个 id 从索引库中移除特定的文档, 也能通过其他内建的属性来移除任意一个文档

db.remove(id=1)
db.putdoc(doc) # 把它放回去, 以便在删除它一次
db.remove(doc)
db.putdoc(doc) # 再加进去
db.remove(uri=u’http://estraier.gov/example.txt’)
# 注意, 每次我们添加这个文档, 都会获得一个新的 id
print doc[u’@id’]
## prints 4

删除文档, 再把它放回去这种做法, 就是更新文档的标准做法. (哈哈, 原来这样!)

那么, 嗯, 我怎么知道索引库里一共有多少个文档? python 内建函数 len() 可以告诉你答案. 现在我们来确认一下 db 里还剩一个文档:

print len(db)
## prints 1

你可能猜到, 你能够通过 uri 属性获取一个 document, 使用类似字典的获取元素的语法:

print db[u’http://extraier.gov/pricelist.txt’]
## prints @digest=caacaefddcc1fd244de251723b0814be
##        @id=2
##        @uri=http://estraier.gov/pricelist.txt
##        …

现在打印出来的是 “draft” 格式, 这是这篇文档的内部表示, 也就是str(doc) 的结果. 我们不推荐用这样的手段获取文档. 你应该用 encode() 方法来得到自己想要的表示:

print doc2.encode(’utf-16′)
## prints ÿþc^@o^@f^@f^@e^@e^@:…

创建文档的复杂例子
~~~~~~~~~~~~~~~~~~~~

hypy 没有提供一种直接的方式决定搜索结果的权重, 有一种变通的方式, 就是你在做索引的时候决定权重. 如果你使用 estwaver 抓取资料, 改变 seed 的值: 每行不同的 seed 值, 会导致不同的权重. 如果你手工添加文档, doc.addhiddentext(text) 可以帮你改变某个关键词的权重.

文档的权重主要是根据搜索关键字在该文档出现的次数计算得来的. 如果你想让某个关键字所占的权重特别大, 很容易, 简单的添加一段隐含文本:

doc5 = hdocument(u’http://estraier.gov/weighted.txt’)
doc5.addtext(u”this is my boom-stick.”)
doc5.addhiddentext(u”eggs ” * 30)
db.putdoc(doc5)

当搜索 eggs 关键字时, 第三个文档的得分将超过 doc2 . 当你打印它的时候, 你看不到这些隐含的文档.

print doc5.encode(’utf-8′)
# prints this is my boom-stick.

搜索, 读取搜索结果
~~~~~~~~~~~~~~~~~~~~~

那么, 怎么进行搜索 ? 简单的很: 构建查询条件, 然后调用 db.search(condition). 搜索结果对象是一个类似列表的对象.

cond = hcondition(u’eggs’)
results = db.search(cond)
for doc in results:
print doc[u’@id’]
# prints 5 then 2

# 使用 pluck 方法能得到每个结果文档的某一属性:

print results.pluck(u’@id’)
# prints [u’5′, u’2′]

搜索关键字还支持通配符:

cond = hcondition(u’egg*’)
results = db.search(cond)
print len(results)
# prints 2
print results[0][u’@uri’]
# prints …/weighted.txt

还有些其他的模式. 默认搜索模式是 “simple”, 多个搜索关键字取其交集 ( 与 查询). 通过在查询条件中增加 matching 参数, 可以改变这一默认行为(变成 或 查询), 就是不指定 matching 参数, 也有别的办法改变默认行为, 在关键字中使用特殊语法. 建议你使用关键字语法, 这样能灵活的控制搜索结果.

doc6 = hdocument(uri=u’http://estraier.gov/spam.txt’)
doc6.addtext(u’spam and eggs’)
db.putdoc(doc6)  # document @id is 6

# simple, 与 查询:
print db.search(hcondition(u’spam* eggs*’)).pluck(u’@id’)
# prints [u’6′]

# union, 或 查询
print db.search(hcondition(u’eggs spam’, matching=’union’)).pluck(u’@id’)
# prints [u’5′, u’2′, u’6′]

# unions with simple matching - you cannot use wildcard matches with
# matching=’union’ but you can do so with ‘|’ syntax
print db.search(hcondition(u’egg* | spam’)).pluck(u’@id’)
# prints [u’5′, u’2′, u’6′]

hyper estraier 用户指南里有完整的搜索语法.

最后, 你可以获取一个文档的抽象, 称为 “teaser”, 嗯, 就是使用一个叫 teaser() 的方法得到它. 这个方法目前支持两种速度输出格式, html 和 rst. 你必须以列表的形式提供要高亮的关键字.

words = [u’toast’]
results = db.search(hcondition(u’ ‘.join(words)))
hit = results[0]
print hit.teaser(words) # default is ‘html’
# prints coffee: $2.00 <strong>toast</strong>: $1.00 eggs (2, …

# 另一种强调搜索关键字:
words = results.hintwords()

print hit.teaser(words, format=’rst’)
# prints coffee: $2.00 **toast**: $1.00 eggs (2, …

属性搜索
~~~~~~~~~~~~

hypy 使用一种功能强大的语法支持属性搜索

cond = hcondition()
cond.addattr(u’@id streq 5′)

print db.search(cond)[0][u’@id’]
# prints 5

后面的例子就比较轻松了, 我们也可以稍事休息一会儿:

# 为了让后面的例子清晰, 我定义了下面的函数:
def attrsx(expr):
cond = hcondition()
cond.addattr(expr)
return u’ ‘.join(db.search(cond).pluck(u’@id’))

# 给 doc6 添加一些有趣的属性
doc6[u’maxprice’] = u’100′
doc6[u’minprice’] = u’0′
doc5[u’date’] = u’2009-01-01′
doc6[u’date’] = u’2009-02-02′

# 提交这个 doc, 注意刚才的代码并没有刷新索引库, 在这个例子里, 我们使用 autoflush
db.putdoc(doc6)
db.putdoc(doc5)

数字属性搜索:

print attrsx(u’maxprice numge 50′)
# prints 6

# 注意: 没有这个属性的文档, 会被看作是拥有这个属性, 且属性值为 “0′. 因此所有的文档都匹配下面这个查询条件:
print attrsx(u’minprice numle 50′)
# prints 2 6 5

# 两个文档匹配下面这个条件:
print attrsx(u’minprice numle 0.99′)
# prints 2 5

日期比较视为数字比较:

print attrsx(u’date numge 2008-12-31′)
# prints 6 5

print attrsx(u’date numge 2009-01-30′)
# prints 6

支持正则表达式:

print attrsx(u’@uri strrx (pricelist.txt|spam.txt)’)
# prints 2 6

你可以对条件取反, 得到 1 个匹配:

print attrsx(u’@uri !strrx (pricelist.txt|spam.txt)’)
# prints 5

当然啦, 你能够在短语搜索条件基础之上增加属性条件, 条件之间默认是 与 结合.

cond = hcondition(u’spam’)
cond.addattr(u’minprice numle 50′)
print db.search(cond)[0][u’@id’]
# prints 6

其他搜索选项
~~~~~~~~~~~~~~

唷!这么多搜索选项!. 呵呵, 这还不是全部. 你还可以限制返回的结果总数, 或者改变搜索结果的顺序.

print db.search(hcondition(u’e*’)).pluck(u’@id’)
# prints [u’5′, u’2′, u’6′]
print db.search(hcondition(u’e*’, max=2)).pluck(u’@id’)
# prints [u’5′, u’2′] .. what did you expect?

如果你喜欢 max, 那么你也可能会喜欢 skip.

print db.search(hcondition(u’e*’, skip=2)).pluck(u’@id’)
# prints [u’6′]

要改变结果顺序, 在 condition 对象上调用 setorder(order) 方法.  hyper estraier 用户指南上有一个完整的 order 表达式参考. 下面的例子通过设置排序方法改变查询结果的默认顺序.

cond = hcondition(u’e*’)

# natural (scored) order
print db.search(cond).pluck(u’@id’)
# prints [u’5′, u’2′, u’6′]

# numeric ascending
cond.setorder(u’@id numa’)
print db.search(cond).pluck(u’@id’)
# prints [u’2′, u’5′, u’6′]

# numeric descending
cond.setorder(u’@id numd’)
print db.search(cond).pluck(u’@id’)
# prints [u’6′, u’5′, u’2′]

其他参考文档
————-

已经讲的足够多啦, 如果你觉得还不过瘾, 好办!请接着去深挖:

1. api 文档, 真的很棒!
2. hyper estraier 用户指南描述了关键字搜索语法, 属性搜索语法和排序语法.
3. hypy 的单元测试文件中有丰富的搜索语法的例子特别是 testdatabase.test_queries 和 testdatabase.test_condextras 中. 这些测试付给了 lib.py 中 100% 的代码. 他们有完善的 文档字符串 和 注释; 象 skip 和 max 搜所还有各种各样的比较例子等等全都有!