前段时间找到一个叫momoko的可以实现这个功能,但使用上出了点问题,对于子函数的调用会出现顺利不正常,后来看看了psycopg2的文档,里面提到psycopg2是有异步支持的,还可以通过写一个wait函数来处理,文档里面提到的是用select,后来在pypi上找到一个使用gevent的 gevent-psycopg2

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
from psycopg2.pool import SimpleConnectionPool
import gevent_psycopg2

gevent_psycopg2.monkey_patch()

class BaseHandler(web.RequestHandler):

@property
def db(self):
if not hasattr(self.application, 'pgpool'):
self.application.pgpool = SimpleConnectionPool(minconn=1, maxconn=20,host='localhost', database='database', user='postgres',
password='password', port=5432)

if not hasattr(self, '_db'):
self._db = self.application.pgpool.getconn()
return self._db

def set_status(self, status_code):
'''
解决因为数据库操作错误导致的全局500错误
'''
if status_code == 500:
self.db.rollback()
super(BaseHandler, self).set_status(status_code)

def get(self):
cur = self.db.cursor()
cur.execute('select * from articles')
self.write(str(cur.fetchall()))

def on_finish(self):
self.application.pgpool.putconn(self.db)#将数据库连接放回连接池中
del self._db

tornado本身并没有实现数据库的异步,这对某些需要长时间响应的数据库统计操作非常不利,所以就有人实现了一些数据库的异步。psycopg2这个postgresql的数据库驱动比较强大,本身就带有异步的能力,然后Momoko在其之上做了一下封装使之在tornado上更容易使用。具体的文档说明请参考:http://momoko.61924.nl/index.html。

我比较喜欢文档里面提到的Adisp和Tornado’s gen方式,这两种实现方式不用callback,不会打乱逻辑。

由于最近需要解决wordpress里的缩略图的问题找到了TimThumb这个库,发现其实现思路很不错,可以借鉴一下。以后在写程序的时候如果需要使用到缩略图一般会在后台上传的时候直接生成相应的缩略图,然后在前台引用。但有时缩略图规格比较多,有些图片又是外链的,不利于缩略图的生成。而TimThumb则是在需要显示缩略图的地方通过:

timthumb.php?src=/wp-content/uploads/2012/07/dd3.jpg&w=525&h=500

这种方式传递图片地址和相关缩略图信息给timthumb.php,然后判断是本地文件还是远程文件,是本地的直接获取相关图片信息,再根据参数进行生成缩略图,并将缩略缓存到cache目录下,最后返回给浏览器。远程的图片文件就是多了一步下载图片到本地的步骤。

这种方式的好处是在需要的时候才生成缩略图,缩略图的大小可以直接在html代码里面指定,易用性较高。但由于这种方式在页面访问时会通过php即时生成或返回缩略图会产生一定的访问开销,对php的io能力有要求。好在图片是可以设定客户端缓存的,只要访问过一次,下次再继续访问时就不会再去服务器下载这个图片了。

至于缓存到cache里面的缩略图文件有一个过期时间,还没弄明白里面的清理机制。

我想如果以后在开发程序的时候可以考虑编写一个这样的模块

  1. 通过在模板里面写函数调用,生成缩略图后返回真实的缩略图地址(比如:createThumb())
  2. 如果已经存在缩略图则直接返回缩略图地址
  3. 可调用函数删除指定的缩略图(比如:deleteThumb())
  4. 可在函数中指定缩略图的存放方式(比如:存放到mfs,mogilefs,girdfs上)
  5. 缩略图存放到不同的服务器上实现分流
  6. 在数据库中将原图跟缩略图进行关联,删除原图后自动删除相关的缩略图

最近在做一个站点,需要在网络上收集一些资料。干这活用python很合适,于是就准备用gevent+requests实现一个爬虫。以前曾用gevent抓取过漫画,但那时的url都是先生成的,并非动态添加,所以需要解决gevent的动态添加任务的问题。

普通的gevent下载方式

1
2
3
4
5
6
7
8
9
10
11
12
13
import gevent
from gevent import monkey
monkey.patch_all()
import requests

def down(url):
print len(requests.get(url).content)
urls = ['http://www.baidu.com','http://www.sina.com.cn']
spawns = []
for url in urls:
spawns.append(gevent.spawn(down, url))

gevent.joinall(spawns)

刚开始我就是用这种方式去下载的,但这种方式不能控制并发数,会将所有的urls一起请求。

gevent.pool控制并发

使用gevent.pool模块可以控制下载并发。改过后的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import gevent
from gevent import monkey
monkey.patch_all()
from gevent.pool import Pool
import requests

p = Pool(2)#设置并发数为2
def down(url):
print len(requests.get(url).content)

urls = ['http://www.baidu.com','http://www.sina.com.cn']
for url in urls:
p.spawn(down, url)

p.join()

动态添加下载地址

在正常情况下如果执行了join函数那程序就不会再往下执行,必需等待下载任务完成。写爬虫的时候需要处理页面信息并将解析出来的url动态的添加到下载任务当中,查看gevent的文档后发现使用gevent.core和gevent.Greenlet就可以实现这个功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import gevent
from gevent import monkey, Greenlet
monkey.patch_all()
from gevent.pool import Pool
import requests

p = Pool(2)#设置并发数为2
def down(url):
print len(requests.get(url).content)

urls = ['http://www.baidu.com','http://www.sina.com.cn']
for url in urls:
p.spawn(down, url)

def buildurl():
while True:
gevent.sleep(0)#添加这条后才可以切换到其它任务
print u"检测下载地址"#这里可以动态添加下载任务

Greenlet.spawn(buildurl)
loop = gevent.core.loop()
loop.run()

tip

buildurl里面的操作不要有长时间阻塞的,会影响程序运行的。loop.run()是在gevent1.0以上的版本里才有的,以下版本请直接运行buildurl()

昨晚看过pgpool的介绍后很想试试这个牛叉的东东,所以就有了今天的这么一番折腾。

安装pgpool-ii

由于我的系统是ubuntu 11.10所以就直接通过apt-get来快速安装pgpool。

1
sudo apt-get install pgpool2

安装postgresql9.1

这次我准备了三台虚拟机来安装postgresql9.1,ip分别是172.16.44.54,172.16.44.101,172.16.44.124,环境全部都是ubuntu11.10。

1
sudo apt-get install postgresql

配置pgsql

修改/etc/postgresql/9.1/main/postgresql.conf,找到如下代码:

1
#listen_addresses = 'localhost'

并修改为:

1
listen_addresses = '*'

修改/etc/postgresql/9.1/main/pg_hba.conf,找到如下代码:

1
host   all     all   127.0.0.1/32    md5

并修改为:

1
host   all     all   172.16.44.0/24  trust

上述修改可以开启postgresql的远程访问,并将认证方式设为trust。trust模式无需密码就可以直接连接管理。当时就是因为md5导致pgpool提示不能访问。

配置pgpool

打开/etc/pgpool2/pgpool.conf,找到如下代码:

1
2
replication_mode = false
load_balance_mode = false

修改为:

1
2
replication_mode = true
load_balance_mode = true

replication_mode是打开复制模式,load_balance_mode可以将select查询按权重分发到后端的postgresql上。

继续在文件里面添加如下内容:

1
2
3
4
5
6
7
8
9
backend_hostname0 = '54'
backend_port0 = 5432
backend_weight0 = 1
backend_hostname1 = '172.16.44.101'
backend_port1 = 5432
backend_weight1 = 1
backend_hostname2 = '172.16.44.124'
backend_port2 = 5432
backend_weight2 = 1

将三台后端的postgresql添加到pgpool中。

重启pgpool和三台postgresql。

1
2
sudo /etc/init.d/pgpool2 restart
sudo /etc/init.d/postgresql restart

测试配置

1
2
createdb -U postgres -p 5433 -h localhost -O postgres testdb
/usr/lib/postgresql/9.1/bin/pgbench -i -U postgres -p 5433 testdb

通过createdb新建一个数据库,并通过pgbench生成测试数据。然后就可以通过连接单独的postgresql查看数据库和对应的测试数据是否生成。如果三台postgresql都有一样的数据,那就说明配置已经正常了。

今天下午配置了nginx+php的环境,准备放个wordpress玩玩。但配置好后页面php的页面一直显示空白,起初我以为是权限问题,将权限改成755后依然不行。然后我就开了nginx和php的日志,但在日志里根本就没有反应什么错误的情况。继续尝试更改php的日志无果。晚上回家后继续尝试了很久,最终发现是nginx的配置文件里面少写了一条配置信息。

1
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

正因为缺了这条配置信息,所以导致nginx没有发送要解析的php文件地址给phpfpm,所以页面一直是空白的,也没有解析的错误信息。

nginx就不说了,这个原来在机子里就安装好了,没有安装的去网上能找到一堆教程,关键是安装trac这个项目管理工具。

1.安装trac

1
2
pip install trac
pip install meld3

2.初始化项目目录

1
trac-admin /var/www/trac initenv

3.添加用户

1
htpasswd -c /var/www/trac/.htpasswd username

如果是添加第二个用户使用下面这条命令:

htpasswd -b /var/www/trac/.htpasswd username password

tip

所有要添加的用户必需存在于.htpasswd文件中

4.添加管理员权限

1
trac-admin /var/www/trac permission add username

5.启动trac

1
tracd -d -p 8001 --basic-auth="*",/var/www/trac/.htpasswd,/var/www/trac /var/www/trac

6.修改nginx配置

1
2
3
4
5
location /trac {
auth_basic "trac";
auth_basic_user_file /var/www/war/trac/.htpasswd;
proxy_pass http://127.0.0.1:port;
}

7.安装AccountManager插件

1
2
3
4
wget http://trac-hacks.org/changeset/latest/accountmanagerplugin/0.11?old_path=/&filename=accountmanagerplugin/0.11&format=zip
unzip accountmanagerplugin_0.11-r11559.zip
cd accountmanagerplugin/0.11
sudo python setup.py bdist_egg

然后将生成的这个egg文件通过trac的管理后台上传上去,再启用它,并停用LoginModule插件。

编辑trac.ini文件

1
2
3
4
5
6
7
8
[account-manager]
htpasswd_hash_type = HtDigestHashMethod
password_file = /var/www/trac/.htpasswd
password_store = HtPasswdStore
user_lock_max_time = 0

[components]
acct_mgr.htfile.htpasswdstore = enabled

最后重启trac,并修改nginx配置,去掉basic认证部分的代码

tracd启用脚本:

1
tracd -d -p 8001 /var/www/trac

nginx配置文件:

1
2
3
location /trac {
proxy_pass http://127.0.0.1:port;
}

昨天嫂子说很怀念以前看过的两部漫画,让我帮忙从网上找一下。我想了想,认为使用gevent来抓取是个不错的选择,遂奋斗几小时将代码写出来了。可恶的漫画网站的js构建url,代码又是压缩过的,真难分析。

两代码总共有两个文件

getImgs.py

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
#-*- encoding:utf-8 -*-
import gevent
from gevent import monkey
from gevent.pool import Pool
import os, urllib2

monkey.patch_all()

class ComicDown():
process = 5

def __init__(self, poolsize=5):
self.urls = []
self.pool = Pool(poolsize)#同时并发数

def addurl(self, url, headers, savefile):
self.pool.spawn(self.down, url, headers, savefile)#将下载任务添加到任务池

def down(self, url, headers, savefile):
try:
request = urllib2.Request(url)
for h in headers:
request.add_header(h[0],h[1])

opener = urllib2.build_opener()
img = opener.open(request).read()
open(savefile, 'wb').write(img)#将下载结果写入文件
print url,'Complete!'
except Exception, e:
print url, 'Error'
print e

def join(self):
self.pool.join()

cd = ComicDown()
def start(path):
f = open(os.path.join(path, 'link'))


line = f.readline()
while line:
urlinfo = {}
urlinfo['url'] = line.split('\t')[0]
urlinfo['headers'] = [['referer',line.split('\t')[1]]]
urlinfo['savefile'] = os.path.join(path,urlinfo['url'].split('/')[-1:][0])
line = f.readline()
if not os.path.exists(urlinfo['savefile']):
cd.addurl(**urlinfo)

getImgs.py 使用gevent的pool任务池下载图片。

getComic.py

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
#coding:utf-8
from pyquery import PyQuery as pq
from optparse import OptionParser
import os, re, getImgs

def getparser():
parser = OptionParser()
parser.add_option('-u','--url', dest='url',help=u'要采集的漫画地址', metavar='url')
options, args = parser.parse_args()
return options, args

def getImgList(url):
'''获取图片地址列表'''
content = pq(url=url).html()
jpgs = re.findall(r"imanhua_\d{3}", content)
out = []
d1, d2 = re.findall(r'(\d{4}).*(\d{5})', url)[0]

for j in jpgs:
out.append('http://t5.imanhua.com/Files/Images/%s/%s/%s.jpg\thttp://www.imanhua.com/comic/%s/list_%s.html'%\
(d1,d2,j,d1,d2))
return out

d = pq(url=getparser()[0].url)
site = '/'.join(getparser()[0].url.split('/')[:3])
print 'site',site
title = d('div.bookInfo h1').text()
if not os.path.exists('./comic/%s'%title):
os.mkdir('./comic/%s'%title)#新建目录

print 'title', title
for i in d('ul#subBookList li a'):
tmp = i.values()
if not os.path.exists('./comic/%s/%s'%(title,tmp[1])):
os.mkdir('./comic/%s/%s'%(title,tmp[1]))
if not os.path.exists('./comic/%s/%s/link'%(title, tmp[1])):
jpglist = getImgList(site+tmp[0])
f = open('./comic/%s/%s/link'%(title, tmp[1]), 'wb')
for j in jpglist:
f.write(j+'\r\n')
f.close()
#print ('getImgs.py -p %s/comic/%s/%s'%(os.getcwd().decode('cp936'),title,tmp[1])).encode('cp936')
#os.system(('getImgs.py -p %s/comic/%s/%s'%(os.getcwd().decode('cp936'),title,tmp[1])).encode('cp936'))
try:
getImgs.start(('%s/comic/%s/%s'%(os.getcwd().decode('cp936'),title,tmp[1])).encode('cp936'))
except:
print u'%s 错误'%tmp[1]

getImgs.cd.join()

getComic.py 使用pyquery和re模块分析页面,得到漫画相关信息和图片下载地址。

tip

此代码是在windows下编写的,故代码中对文件系统地址的编码使用了cp936。

先到 http://download.oracle.com 上面下载jdk-7u4-linux-x64.tar.gz,然后解压

1
sudo tar xvzf jdk-7u4-linux-x64.tar.gz -C /usr/lib/

设置环境变量,用vi打开/etc/profile文件

1
sudo vi /etc/profile

在最后加上

1
2
3
export JAVA_HOME=/usr/lib/jdk1.7.0_04
export PATH=$JAVA_HOME/bin:$JAVA_HOME/jre/bin:$PATH
export CLASSPATH=$CLASSPATH:.:$JAVA_HOME/lib:$JAVA_HOME/jre/lib

注销一下重新登陆就可以使用了。

前几天将ubuntu升级到了12.04,然后想安装一下oracle的jdk7,而ubuntu本身自带的是openjdk,所以就去网上找了一个用ppa安装的。这个安装包安装的时候会去 http://download.oracle.com/ 上下载对应linux的安装包,但oracle网站上下载这些安装包需要一个accept确认,所以会下载失败,导致deb的安装也失败。每次apt-get的时候都会自动的重新尝试一遍,然后继续失败。所以就需要手动清除掉这个包的信息。

进入/var/lib/dpkg/info目录

1
2
cd /var/lib/dpkg/info
sudo rm oracle*

我这里是直接删除所有跟oracle有关的包信息,如果你还装了其它oracle的产品,请一个一个删除对应的文件,千万别删错了。

然后就可以在apt-get里面删除这个包了

1
sudo apt-get remove --purge oracle-java7-installer