2012年9月10日星期一

备份VeryCD的进一步讨论(一): 爬虫的使用

上文尝试了用wget和httrack为VeryCD做整站镜像,但是这个方法有弱点如下:
=================================
1.备份文件太大,平均一个资源要占用500K左右的硬盘空间,那么整站备份可能要80G(或许还不止)
2.数据是保存了,但是搜索引擎等代码无法保存,所以镜像虽大,却也残废,无法搜索
3.备份目标无法随意指定,无论wget还是httrack,只能被动地跟随链接来备份,比如我想只备份2004年的资源便无法做到。
4.慢网速问题,现在VeryCD主站的速度极慢,用”wget http://www.verycd.com”,平均速度一般都在5K左右,如果用wget或者httrack,由于它们会下载所有有关/无关文件,因此效率极低。
=================================

这些毛病可以说是离线浏览软件的通病。并非更换软件可以解决。
一种解决方案是直接问VeryCD要数据库,那就一了百了,但是傻瓜也知道这是不可能的,商业机密,这个都给你了那还了得?
对于VeryCD来说,什么光盘图标啊,介绍图片或者视频啊,又或者资源后的评论啊,这些东西虽然重要,但是相对于ed2k链接来说就微不足道了,可不可以只备份这些关键信息,而摒弃与资源无关的东东呢?
答案是可以,我们可以写一个爬虫脚本,模仿用户行为从VeryCD上依次浏览资源,取其精华,去其“糟粕”,那么空间问题将顺利解决。其次,因为有 了关键数据,就可以在其上构架自己的搜索引擎,那么问题2也解决了。因为是爬虫脚本,自定义方便,所以问题3问题4也将一并解决。
=====================================
1.最适合当爬虫的语言无疑是python了,用python爬网页只要几行代码
import urllib # 载入urllib模块
r = urllib.urlopen('http://www.verycd.com').read() # 下载到r
print r # 打印保存的内容


2.下载完以后要提取信息,这里要用到正则表达式,正则表达式是啥?
在编写处理字符串的程序或网页时,经常会有查找符合某些复杂规则的字符串的需要。正则表达式就是用于描述这些规则的工具。换句话说,正则表达式就是记录文本规则的代码。
很可能你使用过Windows/Dos下用于文件查找的通配符(wildcard),也就是*和?。如果你想查找某个目录下的所有的Word文档的话,你 会搜索*.doc。在这里,*会被解释成任意的字符串。和通配符类似,正则表达式也是用来进行文本匹配的工具,只不过比起通配符,它能更精确地描述你的需 求——当然,代价就是更复杂——比如你可以编写一个正则表达式,用来查找所有以0开头,后面跟着2-3个数字,然后是一个连字号“-”,最后是7或8位数 字的字符串(像010-12345678或0376-7654321)。
这篇文章不错,建议看看:正则表达式30分钟入门

---------------------------------------------------------------------
 
import urllib
import re # 载入正则表达式模块
r = urllib.urlopen('http://www.verycd.com/topics/5420/').read()
 
#提取网页中<h1>到visit之间的内容,这部分内容即为VeryCD的摘要所在区域
abstract = re.compile(r'<h1>.*?visit',re.DOTALL).search(r).group()





---------------------------------------------------------------------------------------------------------

更具体一点的,提取标题
- ----------------------------------------------------------------

title = re.compile(r'<h1>(.*?)</h1>',re.DOTALL).findall(abstract)[0]

- ----------------------------------------------------------------
本文最后会给出提取信息的完整代码
3.提取完该页内容后,就要保存了,考虑到我们要自建搜索引擎,所以我们要把数据存入数据库
数据库都差不多,因为python2.6以后版本都自带了sqlite3,所以我们就偷懒使用sqlite3吧
- ----------------------------------------------------------------
import sqlite3
conn = sqlite3.connect('verycd.sqlite3')
conn.text_factory = str
 
------------------------------------------------------------- 
之后如果要使用sql语句进行数据库操作,只需要
- ----------------------------------------------------------------

    c = conn.cursor()
 
    #执行sql语句
    c.execute('create table a(id integer,dd text)')
 
    conn.commit()
    c.close()
- ---------------------------------------------------------------- 
首先考虑数据库字段,我们要求很简单,保存这个资源是什么以及链接地址的信息,其他无关的我们一概不要。

所以在保存到数据库之前,我们先来设计数据库吧

因为不需要设计用户互动,也不需要设计统计,所以我们只要一张表就够了,很爽,保存啥呢,我保存了,标题,状态,摘要,发布时间,更新时间,类别,ed2k链接,以及链接后一般会有的简介。 ======================================================================== 附1.fetch函数,参数为topic id,提取该topic的关键信息:

   def fetch(id,debug=False):
    urlbase = 'http://www.verycd.com/topics/'
    url = urlbase + str(id) + '/'
    res = urllib.urlopen(url).read()
 
    abstract = re.compile(r'<h1>.*?visit',re.DOTALL).findall
 (res)[0]
 
    title = re.compile(r'<h1>(.*?)</h1>',re.DOTALL).findall 
(abstract)[0]
    status = re.compile(r'"requestWords">(.*?)<',re.DOTALL).
search(abstract).group(1)
    brief = re.compile(r'"font-weight:normal"><span>(.*?)</td>', 
re.DOTALL).search(abstract).group(1)
    brief = re.compile(r'<.*?>',re.DOTALL).sub('',brief).strip()
    pubtime = re.compile(r'"date-time">(.*?)</span>.*?"date-time">
(.*?)</span>',re.DOTALL).findall(abstract)[0]
    category1 = re.compile(r'分类.*?<td>(.*?)&nbsp;&nbsp;(.*?)
&nbsp;&nbsp;',re.DOTALL).findall(abstract)[0]
    category = ['','']
    category[0] = re.compile(r'<.*?>',re.DOTALL).sub('',category1 
[0]).strip()
    category[1] = re.compile(r'<.*?>',re.DOTALL).sub('',category1 
[1]).strip()
 
    res2 = re.compile(r'iptcomED2K"><!--eMule.*?<!--eMule end-->',
re.DOTALL).findall(res)[0]
    sub = re.compile(r'title=\'字幕下载\'/></a>',re.DOTALL).findall 
(res2)
 
    if sub:
        ed2k = re.compile(r'ed2k="(.*?)" subtitle_.*?="(.*?)">
(.*?)</a>',re.DOTALL).findall(res2)
    else:
        ed2k = re.compile(r'ed2k="(.*?)">(.*?)</a>',re.DOTALL). 
findall(res2)
 
    content = re.compile(r'<!--eMule end-->(.*?)
<!--Wrap-tail end-->',re.DOTALL).findall(res)[0]
 
    content = re.compile(r'<br />',re.DOTALL).sub('\n',content)
    content = re.compile(r'<.*?>',re.DOTALL).sub('',content)
    content = re.compile(r'&.*?;',re.DOTALL).sub(' ',content)
    content = re.compile(r'\n\s+',re.DOTALL).sub('\n',content)
    content = content.strip()
 
    if debug:
        print title
        print status
        print brief
        print pubtime[0],pubtime[1]
        print category[0],category[1]
        for x in ed2k:
            print x
        print content

调用fetch(5420,debug=True)将会输出:

冰封王座英文光盘版
精华资源
无
2003/09/16 14:17:29 2003/09/16 14:17:29
游戏 光盘版游戏
('ed2k://|file|Warcraft.III.-.Frozen.Throne.KeyGen.exe|24064|e73d5ed877490f59882b8eb537bda31c|/', 'Warcraft.III.-.Frozen.Throne.KeyGen.exe')
('ed2k://|file|Warcraft_III_Frozen_Throne.DEViANCE.X-Gamers.bin|563946096|f7fb1dce076dc1db448441b937a4c6ac|/', 'Warcraft_III_Frozen_Throne.DEViANCE.X-Gamers.bin')
简介:
冰封王座英文光盘版
不必介绍了吧……

没有评论:

发表评论