一个xapian的document有term,value,data。term里面我放分词的结果用于搜索,value里面我放了日期用于排序,data我没放东西,然后根据xapian返回的docid在postgresql查找对应的文章。

取得估计的结果数

1
matches.get_matches_estimated()

对结果按时间进行排序

1
enquire.set_sort_by_value(1, True)

这里的 1 对应索引时的日期

在value中加入日期时间

1
xapian.sortable_serialise(time.mktime(posttime.timetuple()))

我这里是转换成时间戳,由于xapian的value需要存字符串,所以还需要用 xapian.sortable_serialise 序列化一下。

一个简单封装操作的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import xapian, config
from mmseg.search import seg_txt_2_dict

class Xapian():
"""xapian search class """

def __init__(self):
"""init xapian search class
:returns: class

"""
self.db = xapian.WritableDatabase(config.xapian_index_dir, xapian.DB_CREATE_OR_OPEN)
self.enquire = xapian.Enquire(self.db)
self.enquire.set_sort_by_value(1, True)

def get_document(self, id):
"""获取doc

:id: id
:returns: Document

"""
return self.db.get_document(id)

def delete_document(self,id):
"""删除索引

:id: 索引id
"""
try:
return self.db.delete_document(id)
except:
return None

def update_index(self, id, text=None, values=None, data=None):
"""更新索引

:id: 要替换的id
:doc: 新的doc
"""
try:
doc = self.get_document(id)
except:
return False

if text:
doc.clear_terms()#清除terms
for word, value in seg_txt_2_dict(text).iteritems():
doc.add_term(word)

if values:
doc.clear_values()
for key, value in values.iteritems():
doc.add_value(key, value)

if data:
doc.set_data(data)

try:
self.db.replace_document(id, doc)
return True
except:
return False

def index(self, id, text, values={}, data=''):
"""index to xapian

:id: data id
:text: search content is utf-8
:returns: boolean

"""
doc = xapian.Document()
for word, value in seg_txt_2_dict(text).iteritems():
print word, value
doc.add_term(word)

#添加value用于排序,key似乎只能是数字
for key, value in values.iteritems():
doc.add_value(key, value)

if data:
doc.set_data(data)

try:
self.db.replace_document(id, doc)
return True
except:
return False

def search(self, keywords, offset=0, limit=10):
"""search xapian

:keywords: 搜索的关键字
:offset: 起始位置
:limit: 结束位置
:returns: matches对象

"""
query_list = []
for word, value in seg_txt_2_dict(keywords.encode('utf-8')).iteritems():
query = xapian.Query(word)
query_list.append(query)

if len(query_list) != 1:
query = xapian.Query(xapian.Query.OP_AND, query_list)
else:
query = query_list[0]

self.enquire.set_query(query)
matches = self.enquire.get_mset(offset, limit, 10000)
return matches

def flush(self):
"""flush to disk
:returns: flush结果

"""
return self.db.flush()

search = Xapian()

由于我的显示是Intel HD3000,在VMware9.0里面默认是不支持启用3D加速的,按提示是说要更新驱动,但intel官网的驱动安装好复杂,源里面的驱动应该也是新版本了。只好去Google一翻,找到一个方法,在.vmx文件中添加如下信息,允许使用黑名单中的驱动。

1
mks.gl.allowBlacklistedDrivers = "TRUE"

然后重新打开就可以看到3D加速的功能已经成功开启。

由于Linux Mint 14使用的内核版本是3.5的,而VMware Workstation 9在linux kernel3.5上运行会有问题,需要打一个补丁重新编译一下才可以运行,否则会出现下面这个错误信息。

1
Unable to change virtual machine power state: Failed to connect to peer process.

解决这个问题需要一个补丁,至于VMware Workstation 9的安装方法和下载地址请自己搜索解决。

解决方法

先下载 vmware9_kernel35_patch.tar.bz2 ,解压出来。

1
2
3
wget http://blog.plotcup.com/upload/vmware9_kernel35_patch.tar.bz2
tar xjvf vmware9_kernel35_patch.tar.bz2
cd vmware9_kernel3.5_patch

在解压出来的文件夹中有一个 patch-modules_3.5.0.sh 文件,先别忙着运行它,在运行它之前需要先停止掉vmware的服务。在 /etc/init.d/ 里面有三个名称中带vmware的文件,把它们移动到别的地方,然后重启计算机,这样可以保证vmware在开机的时候没有启动。

1
sudo mv /etc/init.d/vmware* ~/

然后重启一下计算机,再把刚刚移出来的三个文件移回去。

1
sudo mv ~/vmware* /etc/init.d/

现在这个时候就可以运行 vmware9_kernel3.5_patch 目录中的 patch-modules_3.5.0.sh 文件了。

1
sudo ./patch-modules_3.5.0.sh

现在再打开vmware应该就能正常启动虚拟机了,同样的方法也适用于Ubuntu 12.10。

最近在 hostigation 上新买了一个vps,装了debian6,然后开始安装环境。原先在linode上使用的是ubuntu,通过ppa的方式安装了postgresql 9.2,这次换成debian6后就不打算折腾ppa了,换自己编译。

先去 www.postgresql.org 上下载源码包,解压开来随便放一个位置,我放在 /root/down/ 下面.进入解压出来的源码目录,我的是 ~/down/postgresql-9.2.2。

安装依赖包

1
apt-get install zlib1g-dev libreadline-dev

编译

1
2
./configure
make&make install

新建用户

建立一个用户用于启动postgresql。

1
useradd postgres

然后将postgresql的安装目录的所有者改成postgresql用户。

1
chown postgres:postgres /usr/local/pgsql -R

建立软链接

1
2
cd /usr/local/bin
cp -s /usr/local/pgsql/bin/* ./

设置lib

1
2
3
cd /etc/ld.so.conf.d
echo "/usr/local/pgsql/lib">pgsql.conf
ldconfig

这个是给其它程序连接用的,不然可能会出现无法找到libpq.so.5的情况。

建立数据库

1
2
3
4
cd /usr/local/pgsql/
mkdir data
cd data
initdb

创建数据库用户

1
2
su -c "psql postgres" postgresql
CREATE ROLE [用户名] PASSWORD '[密码]' SUPERUSER INHERIT CREATEDB CREATEROLE REPLICATION;

然后创建一下数据库就基本可用了,默认本地是trust的,有需要的可以改成md5之类的。

昨天一时兴起尝试了一下用 nodejs 写一个极其简单的日记程序,web 框架使用了 express 。由于原先有使用 coffeescript 开发浏览器端js脚本的经验,所以就直接在程序里面使用 coffeescript 了。

使用 coffeescript 的方法

1
2
3
4
5
require("coffee-script");//导入coffeescript
var app = require("./appmodule").app;//导入appmodule.coffee文件中的app

app.listen(app.get('post'));
console.log('Listening on port ' + app.get('port'));

将上面的内容存成一个 app.js 文件,主要的脚本内容就写到 appmodule.coffee 里面,然后其它文件也可以使用 coffeescript 来写。

使用 cookieSession

1
2
3
4
5
6
7
8
9
10
11
12
app.configure ->
app.set "port", process.env.PORT or 3000
app.set "views", __dirname + "/views"
app.set "view engine", "jade"
app.use express.favicon()
app.use express.logger("dev")
app.use express.bodyParser()
app.use express.methodOverride()
app.use express.cookieParser('ipfqheprp') # 加载cookieParser
app.use express.cookieSession() # 加载cookieSession中间件
app.use app.router
app.use express.static(path.join(__dirname, "public"))

先加载cookieParser然后再加载cookieSession,然后在程序里面就可以通过

1
2
req.session.name = 'value' # 设置session内容
name = req.session.name # 读取name的值

使用 sqlite3

1
2
3
4
5
6
7
8
9
10
sqlite3 = require('sqlite3').verbose()
db = new sqlite3.Database './data.db'

db.serialize ->
db.get "SELECT * FROM table WHERE id=?", id, (err, row) ->
console.log row.id # get方法是获取查询到的第一条记录

db.all "SELECT * FROM table', (err, rows) ->
for r in rows
console.log r.id # 这里是取全部

用了 coffeescript 后语法跟python好像。

模板使用了 jade 写起来是挺简洁的,写doctype非常方便,几个字符就能表达出来。这个比直接写html的那么一大串好太多了。

由于写一个登陆界面的时候需要记住上一次的用户名,所以就想到了使用 cookies 。在flask里面的session虽然也是用 cookies 的,但在浏览器关闭的时候不会保存下来。在 flask的文档里面找到相关的说明,里面说如果要用 cookies 就需要新建一个 Response ,然后使用 Response 的 set_cookie 方法去设置 cookies 。尝试自定义了一个Response去写入cookies后发现 Flask-DebugToolbar 这个调试利器居然不能显示了,在网上也找不到相关的资料说明,只好自己去研究一下 Flask-DebugToolbar 的源码。

在 Flask-DebugToolbar 的 __init__.py 文件中提到 self.app.after_request(self.process_response) 说明渲染并替换的工作是在请求后处理的。然后再查看 self.process_response 函数。里面的渲染加了判断,所以我想应该就是这里出了问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
if (response.status_code == 200
and response.headers['content-type'].startswith('text/html')): # 刚开始以后是这里的问题,后来设成相应的值后还是不行
for panel in self.debug_toolbars[real_request].panels:
panel.process_response(real_request, response)

if response.is_sequence: # 关键在这个地方
response_html = response.data.decode(response.charset)
toolbar_html = self.debug_toolbars[real_request].render_toolbar()

content = replace_insensitive(
response_html, '</body>', toolbar_html + '</body>')
content = content.encode(response.charset)
response.response = [content]
response.content_length = len(content)

上面的 response.is_sequence 是关键,我原先是直接使用 response.response 去设置内容的,这样的情况下 response.is_sequence 的值是False,用 response.data 去设置内容的情况下 response.is_sequence 才是True。最终代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@front.route('/login', methods=['GET', 'POST'])
def login():
"登陆"
response = Response(status=200, content_type='text/html') # 新建一个response对象,用于处理cookies
d = {}
if request.method == 'POST':
response.set_cookie(key='lastlogin', value=request.form.get('username'), max_age=365 * 24 * 60 * 60)
user = db.session.query(User.u_id).filter(User.pswd == request.form.get('password')).\
filter(User.u_id == request.form.get('username')).first()
if user: # 如果用户存在则更新登陆状态
identity_changed.send(current_app._get_current_object(),
identity=Identity(user.u_id))
return redirect(url_for('.index'))
else:
d['errInfo'] = "密码错误!"

d['users'] = db.session.query(User.u_id, User.c_name).all()
d['lastlogin'] = request.cookies.get('lastlogin', None)
response.data = render_template('login.html', **d)
return response

is_sequence 的意思应该是指连续的,想不通的是设置 response 就不算是连续的?

最近写爬虫的时候遇到需要对文本内容进行对比计算相似度,找了很久还真的让我找到个现成的模块 python-Levenshtein 这个模块用法直接用help看吧,我主要用到里面的distance和ratio,其它的暂时还不知道有什么功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
"""
Levenshtein.apply_edit Levenshtein.median_improve
Levenshtein.distance Levenshtein.opcodes
Levenshtein.editops Levenshtein.quickmedian
Levenshtein.hamming Levenshtein.ratio
Levenshtein.inverse Levenshtein.seqratio
Levenshtein.jaro Levenshtein.setmedian
Levenshtein.jaro_winkler Levenshtein.setratio
Levenshtein.matching_blocks Levenshtein.subtract_edit
Levenshtein.median

>>> ratio('Hello world!', 'Holly grail!')
0.58333333333333337

>>> distance('Levenshtein', 'Lenvinsten')
4
"""

在scrapy提交一个链接请求是用 Request(url,callback=func) 这种形式的,而parse只有一个response参数,如果自定义一个有多参数的parse可以考虑用下面的方法实现多个参数传递。

1
2
3
4
5
def parse(self,response):
yield Request(url, callback=lambda response, typeid=5: self.parse_type(response,typeid))

def parse_type(self,response, typeid):
print typeid

将参数写在lambda里面封装一下就行,内函数有多少个需要传递的参数在lambda里面就需要写多少个,加上默认值就好,如果直接写到内函数会变成形参。

以前我用scrapy写爬虫的时候都是通过crawl来执行的,但这样的运行方式是只执行一个爬虫的,如果想同时运行多个爬虫可以考虑使用scrapyd的方式,也就是scrapy server。

运行scrapyd:

1
scrapy server

部署project:

查看project的deploy列表

进入到project目录后执行下面的命令

1
scrapy deploy -l

如果有返回类似下面的内容的说明配置正确

1
scrapyd              http://localhost:6800/

如果没有显示就编辑project的scrapy.cfg里面的deploy

1
2
3
[deploy]
url = http://localhost:6800/
project = pitayacd

打包并上传

1
scrapy deplay default -p pitayacd

返回信息如下

1
{"status": "ok", "project": "pitayacd", "version": "1348817612", "spiders": 1}

运行jobs

部署完成后使用curl提交信息到scrapyd就可以运行指定的spiders了

1
curl http://localhost:6800/schedule.json -d project=pitayacd -d spider=spider2

参考资料: http://doc.scrapy.org/en/0.14/topics/scrapyd.html