# 简易采集爬虫
# 1.我只做了一个收集url的功能,如果需要将数据入库,可在 parseData 函数里面写处理代码
# 2.需要sqlite3或者pysqlite支持
# 3.可以在DreamHost.com空间上面运行
# 4.可以修改User-Agent冒充搜索引擎蜘蛛
# 5.可以设置暂停的时间,控制采集速度
# Author: Lukin<mylukin@gmail.com>
# Date : 2008-09-25
#!/usr/bin/python
#-*-coding:utf-8-*-
# 简易采集爬虫
# 1.我只做了一个收集url的功能,如果需要将数据入库,可在 parseData 函数里面写处理代码
# 2.需要sqlite3或者pysqlite支持
# 3.可以在DreamHost.com空间上面运行
# 4.可以修改User-Agent冒充搜索引擎蜘蛛
# 5.可以设置暂停的时间,控制采集速度
# Author: Lukin<mylukin@gmail.com>
# Date : 2008-09-25
# 导入采集需要用到的模块
import re, sys, time
import httplib, os.path as osp
from urlparse import urlparse
# 使用sqite数据库,为了兼容DreamHost.com的空间,只能这么写了
try :
import sqlite3 as sqlite
except ImportError:
from pysqlite2 import dbapi2 as sqlite
# 采集速度控制,单位秒
sleep = 1
# 设置提交的header头
headers = {"Accept": "*/*","Referer": "http://answers.yahoo.com/","User-Agent": "Mozilla/5.0+(compatible;+Googlebot/2.1;++http://www.google.com/bot.html)"}
# 连接服务器
dl = httplib.HTTPConnection('answers.yahoo.com')
# 连接数据库
conn = sqlite.connect(osp.abspath('./database.db'))
# 创建数据库
def createDatabase():
global conn; c = conn.cursor()
# 创建表
c.execute('''CREATE TABLE IF NOT EXISTS [collect]([cid] INTEGER PRIMARY KEY,[curl] TEXT,[state] INTEGER DEFAULT '0',UNIQUE([curl]));''')
c.execute('''CREATE INDEX IF NOT EXISTS [collect_idx_state] ON [collect]([state]);''')
conn.commit()
c.close()
# 执行采集
def collect(url="http://answers.yahoo.com/"):
global dl,headers; R = 0
print "GET:",url
urls = urlparse(url); path = urls[2];
if urls[4]!='' : path += '?' + urls[4]
dl.request(method="GET", url=path, headers=headers); rs = dl.getresponse()
if rs.status==200 :
R = parseData(rs.read(),url)
else :
print "3 seconds, try again ..."; time.sleep(3)
dl.request(method="GET", url=path, headers=headers); rs = dl.getresponse()
if rs.status==200 :
R = parseData(rs.read(),url)
else :
print "3 seconds, try again ..."; time.sleep(3)
dl.request(method="GET", url=path, headers=headers); rs = dl.getresponse()
if rs.status==200 :
R = parseData(rs.read(),url)
else :
print "Continue to collect ..."
R = 2
# 更新记录
updateOneUrl(url,R)
# 返回结果
return R
# 处理采集到的数据
def parseData(html,url):
global dl,conn; R = 2;
c = conn.cursor()
# 格式化html代码
format = formatURL(clearBlank(html),url)
# 取出所有的连接
urls = re.findall(r'''(?iu)(<a[^>]*href="([^"]+)"[^>]*>)|(<a[^>]*href='([^']+)'[^>]*>)''',format)
if urls != None :
# 循环所有的连接
for regs in urls :
# 得到一个单一的url
url = regs[1].strip()
# 判断url是否符合规则,符合,则插入数据库
if re.search('http(.*)(/dir/index(.*)link=over(.*)|/question/index(.*))',url,re.I) != None :
if url[-11:]=='link=mailto' : continue
try :
c.execute('''INSERT INTO [collect]([curl])VALUES(?);''',(url,))
print "INSERT:",url
except sqlite.IntegrityError :
pass
# 截取标题
# title = sect(format,'(<title>)','(</title>)')
# 判断采集成功
if True : R = 1
conn.commit(); c.close()
return R
# 取得一条URL
def getOneUrl(state=0):
global conn; c = conn.cursor()
c.execute('''SELECT [curl] FROM [collect] WHERE [state]=? LIMIT 0,1;''',(state,))
row = c.fetchone(); c.close()
if row==None : return ""
return row[0].encode('utf-8')
# 更新一条记录的状态
def updateOneUrl(url,state):
global conn; c = conn.cursor()
c.execute('''UPDATE [collect] SET [state]=? WHERE [curl]=?;''',(state,url))
conn.commit(); c.close()
# 清除html代码里的多余空格
def clearBlank(html):
if len(html) == 0 : return ''
html = re.sub('r|n|t','',html)
while html.find(" ")!=-1 or html.find(' ')!=-1 :
html = html.replace(' ',' ').replace(' ',' ')
return html
# 格式化url
def formatURL(html,url):
urls = re.findall(r'''(<a[^>]*href="([^"]+)"[^>]*>)|(<a[^>]*href='([^']+)'[^>]*>)''',html,re.I)
if urls == None : return html
for regs in urls :
html = html.replace(regs[0],matchURL(regs[0],url))
return html
# 格式化单个url
def matchURL(tag,url):
urls = re.findall(r'''(.*)(src|href)=(.+?)( |\/>|>).*|(.*)url\(([^\)]+)\)''',tag,re.I)
if urls == None :
return tag
else :
if urls[0][5] == '' :
urlQuote = urls[0][2]
else:
urlQuote = urls[0][5]
if len(urlQuote) > 0 :
cUrl = re.sub('''['"]''','',urlQuote)
else :
return tag
urls = urlparse(url); scheme = urls[0];
if scheme!='' : scheme+='://'
host = urls[1]; host = scheme + host
if len(host)==0 : return tag
path = osp.dirname(urls[2]);
if path=='/' : path = '';
if cUrl.find("#")!=-1 : cUrl = cUrl[:cUrl.find("#")]
# 判断类型
if re.search(r'''^(http|https|ftp):(\/\/|\\\\)(([\w\/\\\+\-~`@:%])+\.)+([\w\/\\\.\=\?\+\-~`@':!%#]|(&)|&)+''',cUrl,re.I) != None :
# http开头的url类型要跳过
return tag
elif cUrl[:1] == '/' :
# 绝对路径
cUrl = host + cUrl
elif cUrl[:3]=='../' :
# 相对路径
while cUrl[:3]=='../' :
cUrl = cUrl[3:]
if len(path) > 0 :
path = osp.dirname(path)
elif cUrl[:2]=='./' :
cUrl = host + path + cUrl[1:]
elif cUrl.lower()[:7]=='mailto:' or cUrl.lower()[:11]=='javascript:' :
return tag
else :
cUrl = host + path + '/' + cUrl
R = tag.replace(urlQuote,'"' + cUrl + '"')
return R
# html代码截取函数
def sect(html,start,end):
if len(html)==0 : return ;
# 正则表达式截取
if start[:1]==chr(40) and start[-1:]==chr(41) and end[:1]==chr(40) and end[-1:]==chr(41) :
reStart = re.findall(r'(?isu)' + start + '(.*)' + end,html)
if reStart == None : return
R = reStart[0][1]
# 字符串截取
else :
# 取得开始字符串的位置
intStart = html.lower().find(start.lower())
# 如果搜索不到开始字符串,则直接返回空
if intStart == -1 : return
# 取得结束字符串的位置
intEnd = html[intStart+len(start):].lower().find(end.lower())
# 如果搜索不到结束字符串,也返回为空
if intEnd == -1 : return
# 开始和结束字符串都有了,可以开始截取了
R = html[intStart+len(start):intStart+intEnd+len(start)]
# 返回截取的字符
return R
# ------------------------------------- 开始执行程序 -------------------------------------------
# 首先创建数据库
createDatabase()
# 开始采集
loops = 0
while True:
if loops>0 :
url = getOneUrl()
if url == "" :
loops = 0
else :
loops = collect(url)
else :
loops = collect()
# 暂停
time.sleep(sleep)
if loops==0 : break
# 关闭HTTP连接
dl.close()
# 退出程序
sys.exit()




