代碼來自於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的代碼再寫出來進行對比,便可以得到一些不同。區別不僅僅是你發現的,還有很多,參考更多的博客和教程來總結。