代码来自于Python核心编程,运行环境为Python2.7+macOS。直至2月16日运行无误。代码中有核心代码注释,如需更详细的解释,请去书本内容观看。话不多说,直接上码。
#!/usr/bin/env python
import cStringIO
import formatter
from htmllib import HTMLParser
import httplib
import os
import sys
import urlparse
import urllib
#urllib:使用其中的urlparse()函数来下载Web页面。urlparse:使用其中的urlparse()和urljoin()函数来处理URL
class Retriever(object):
__slots__ = ('url','file')#__slot__变量表示实例只能拥有self.url和self.file属性
def __init__(self,url):
self.url,self.file=self.get_file(url)
def get_file(self,url,default='index.html'):
'Create usable local filename from url 将URL的前缀http://前缀移除,丢掉任何为获取主机名二附加的额外信息,如用户名、密码和端口号'
parsed=urlparse.urlparse(url)
host=parsed.netloc.split('@')[-1].split(':')[0]
filepath='%s%s' %(host,parsed.path)
if not os.path.splitext(parsed.path)[1]:
filepath=os.path.join(filepath,default)
linkdir=os.path.dirname(filepath)
if not os.path.isdir(linkdir):
if os.path.exists(linkdir):
os.unlink(linkdir)
os.makedirs(linkdir)
return url,filepath
def download(self):
'Download URL to specific named file'
try:
retval=urllib.urlretrieve(self.url,self.file)
except (IOError,httplib.InvalidURL) as e:
retval=(('*** ERROR: bad URL "%s": %s' %(self.url,e)),)
return retval
def parse_links(self):
'Parse out the links found in download HTML file'
f=open(self.url,'r')
data=f.read()
f.close()
parser=HTMLParser(formatter.AbstractFormatter(formatter.DumbWriter(cStringIO.StringIO())))
parser.feed(data)
parser.close()
return parser.anchorlist
class Crawler(object):
count=0#每成功下载一个页面,增一。
def __init__(self,url):
self.q=[url]#待下载的链接队列
self.seen=set()#已下载链接的一个集合
parsed=urlparse.urlparse(url)
host=parsed.netloc.split('@')[-1].split(':')[0]
self.dom='.'.join(host.split('.')[-2:])#存储主链接的域名,并判定后续链接的域名与主域名是否一致
def get_page(self,url,media=False):
'Download page &parse links,add to queue if nec'
r=Retriever(url)
fname=r.download()[0]
if fname[0]=='*':
print fname,'...skipping parse'
return
Crawler.count+=1
print '\n(',Crawler.count,')'
print 'URL:',url
print 'FILE:',fname
self.seen.add(url)
ftype=os.path.splitext(fname)[1]
if ftype not in ('.htm','html'):#跳过所有非web页面
return
for link in r.parse_links():
if link.startwith('mailto'):#邮箱连接会被忽略
print '....discarded,mailto link'
continue
if not media:#媒体文件会被忽略
ftype=os.path.splitext(link)[1]
if ftype in ('.mp3','.mp4','.m4v','.wav'):
print '...discarded,media file'
continue
if not link.startwith('http://'):
link=urlparse.urljoin(url,link)
print '*',link
if link not in self.seen:
if self.dom not in link:
print '....discarded, not in domain'
else:
if link not in self.q:
self.q.append(link)
print '...new,added to Q'
else:#已经位于队列中处理的连接
print '.....discarded,already processed'
else:#已经下载的连接
print '....discarded,already processed'
def go(self,media=False):
'Process next page in queue(if any)'
while self.q:
url=self.q.pop()
self.get_page(url,media)
def main():
if len(sys.argv) >1:
url=sys.argv[1]
else:
try:
url=raw_input('Enter starting URL: ')
except (KeyboardInterrupt,EOFError):
url=''
if not url:
return
if not url.startswith('http://') and not url.startswith('ftp://'):
url='http://%s/' %url
robot=Crawler(url)
robot.go()
if __name__=='__main__':
main()
需要解释的是,在Web开发这一块,Python2.x和Python3.x有一些不同,你可以先熟悉2的一些语法和编程规则,然后将3的代码再写出来进行对比,便可以得到一些不同。区别不仅仅是你发现的,还有很多,参考更多的博客和教程来总结。
版权声明:本文为qq_34454366原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。