CWYAlpha

Just another WordPress.com site

Thought this was cool: 备份VeryCD的进一步讨论(二):sqlite3数据库的处理

leave a comment »


数据库有什么好处?维护起来不见得方便,比如我只要按VeryCD的Topic号建立文件名,一条数据存一个文件,维护起来极其简单:直接修改查看即可。

为了让其按更新时间排序,那么就略微复杂那么一点点了,只要对更新时间建立一张索引表,那么排序也是分分钟的事情:空间占用仅为O(N),且一个
Topic仅为一个integer、时间上也就是建表O(NlogN),更新O(logN)。排序有先成的函数,更新不知道有不,没有的话也可以先
remove,再insert的办法来做,还是O(logN),或者干脆自己写代码好了,反正也就是二分查找而已。


接下来我们要提供搜索功能,这个没啥好技巧,如果全文检索的话,建索引也没啥太大用处,只有穷举法了,按索引顺序穷举,返回结果即可。

好像都很简单啊,那干吗还要数据库呢。

好的,第一个问题,同步和并发问题:如果有几个脚本要同时更新索引文件,那就比较惨,文件锁的效率可是很低的。第二个问题,扩展问题:如果考虑到以后数据
的扩充,有多张表之后,怎么协同呢?当然也可以根据我们刚才的思路,该建索引的建索引,该穷举的穷举,可是东西一多当中的逻辑结构就烦死你。第三个问题:
效率问题,python写的东西跟c写的东西比速度,简直就是找死。

而使用成熟的数据库系统,这些问题都不用考虑了,只需要设计表,决定自己的查询语句,其他事情都丢给数据库去操心了。
============================
3.继续数据库的讨论

用爬虫爬完数据后,剩下的就是存入数据库了,python的数据库存储文法上文已经提及了。这里需要说明一下sql语句。

说到数据库就不得不提sql语句,即结构化查询语句。这是数据库的另一个好处,不管你用的是sqlite还是mysql,是mssql又或oracle,只要使用同一个语法就可以实现同样的效果,根本不用关心它底层是怎么实现你的指令,只需要学会sql语法就可以了。

比如sql建表

CREATE TABLE XXX ( item1 type1, item2 type2 )


再比如sql查询记录个数

SELECT COUNT(*) FROM XXX


或者给个实战的,从数据库中提取最新更新的20条记录

SELECT * FROM verycd ORDER BY updtime DESC LIMIT 20


一切都是那么的轻松写意。wikipedia上的SQL条目就是不错的教学,其实这语言也挺直观的,随便玩几下就熟了。其中语句的语法我直接参照,sqlite的文档

那么按照惯例,给出对应sqlite的建表、插入和更新的python脚本。

关于数据库的查询,由于我使用了webpy作为网站的,怎么说,基本架构?所以要说明一下webpy的假设和用法,这个下次再写吧。同样加入更新计划的:昨天修改了一下代码,使得爬虫得以多线程并发下载,大大加快了备份速度:)所以这部分内容也会说一下,还有写了个同步更新的脚本,这样山寨站点就能能和VeryCD保持同步更新了,目前山寨的进度大约是1/4左右,数据库虽然还不完整,但是因为我改变了策略,优先爬了些近期更新的数据,所以搜索新的东东应该都没有什么问题了。

废话不说了,下面是代码
=========================================

#全局变量conn
conn = sqlite3.connect(path+'/verycd.sqlite3.db')
conn.text_factory = str
-
#创建数据库
def dbcreate(conn):
    c = conn.cursor()
    c.execute('''create table verycd(
        verycdid integer primary key,
        title text,
        status text,
        brief text,
        pubtime text,
        updtime text,
        category1 text,
        category2 text,
        ed2k text,
        content text
    )''')
    conn.commit()
    c.close()
-
#把结果插入数据库
def dbinsert(id,title,status,brief,pubtime,category,ed2k,content):
    c = conn.cursor()
    c.execute('insert into verycd values(?,?,?,?,?,?,?,?,?,?)',\
        (id,title,status,brief,pubtime[0],pubtime[1],category[0],category[1],\
        ed2k,content))
    conn.commit()
    c.close()
-
#把结果更新到数据库
def dbupdate(id,title,status,brief,pubtime,category,ed2k,content):
    c = conn.cursor()
    c.execute('update verycd set verycdid=?,title=?,status=?,brief=?,pubtime=?,\
        updtime=?,category1=?,category2=?,ed2k=?,content=? where verycdid=?',\
        (id,title,status,brief,pubtime[0],pubtime[1],category[0],category[1],\
        ed2k,content,id))
    conn.commit()
    c.close()
-
#查找topic是否已经存在于数据库
def dbfind(id):
    c = conn.cursor()
    c.execute('select 1 from verycd where verycdid=?',(id,))
    c.close()
    for x in c:
        if 1 in x:
            return True
        else:
            return False 

最后,需要在fetch最后加入数据库操作的代码

 #这一步是因为ed2k在数据库中要保存为字符串,所以需要转换一下
    ed2kstr = '`'.join([ '`'.join(x) for x in ed2k ])
 
    if not dbfind(id,conn):
        dbinsert(id,title,status,brief,pubtime,category,ed2kstr,content,conn)
    else:
        dbupdate(id,title,status,brief,pubtime,category,ed2kstr,content,conn)



对了,后来我很土地从文档中发现好像insert一个已经存在的有唯一主键(primary key)的记录也不会出错而是会直接更新,所以上述代码精简掉update的那一部分应该也是没问题的,不过我本着能用就不折腾,除非效率提高很多的原则,也就懒得修改了。

PS:

如果用python的话,用urllib2做爬虫确实会遇到这样的问题,原因是一个新的下载urllib2就开一个新的socket,如果速度很快,同时在线的socket可能会达到几百或者几千,所以verycd封IP也属正常。

我的偷懒解决方法是用squid代理访问,那么squid就会帮你维护socket个数,不会干太为过分的事情了。不想用squid想用python解决问题的话,那么就别用urllib/urllib2了,用httplib,底层了一点,但是就不会被封了。


from duyamin’s blog: http://www.duyamin.com/2012/09/sqlite3.html?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+DuyaminsBlog+%28duyamin%E2%80%99s+blog%29

Written by cwyalpha

十一月 24, 2012 在 3:26 下午

发表在 Uncategorized

发表评论

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / 更改 )

Twitter picture

You are commenting using your Twitter account. Log Out / 更改 )

Facebook photo

You are commenting using your Facebook account. Log Out / 更改 )

Google+ photo

You are commenting using your Google+ account. Log Out / 更改 )

Connecting to %s

%d 博主赞过: