#!/usr/bin/python #調用python
from sys import argv #導入sys是導入python解釋器和他環境相關的參數
from os import makedirs,unlink,sep #os主要提供對系統路徑,文件重命名和刪除文件所需的函數
#makedirs是創建遞歸文件夾的函數。
#比如說我們要創建一個新的目錄,/python/HTML/crawl,但是目前這三個文件夾都不存在,如果使用mkdir命令的話需要使用三次才能完成,
#但是使用os.makedir只需使用一次就可以創建好整個目錄。
#os.makedirs(os.path.join(os.erviron["HOME"],"python","HTML","crawl")
#os.unlink(path)刪除file路徑,和remove()相同。
#sep os.sep系統用此來分割路徑名
from os.path import dirname,exists,isdir,splitext
#使用os中的這些模塊來提取dirname路徑名,exists,isdir是文件類型測試,測試是否是一個目錄,splitext是將文件名和文件后綴分離。分成目錄文件名和后綴兩部分。
from string import replace,find,lower #導入string模塊,用于字符串的替換,查找,和小寫化。
from htmllib import HTMLParser
from urllib import urlretrieve #urlretrieve()函數用于將HTML文件整個下載到你的本地硬盤中去。
from urlparse import urlparse,urljoin #urlparse用于將URL分解成6個元素,而urljoin用于將baseurl和newurl組合在一起
from formatter import DumbWriter,AbstractFormatter #formatter函數主要用于格式化文本
from cStringIO import StringIO #調用cStringIO函數對內存中的文件進行處理
#Retriever類負責從網上下載網頁并對每一個文檔里面的連接進行分析,如果符合下載原則就添加到“待處理”隊列中。
#從網上下載到的每個主頁都有一個與之對應的Retriever實例。Retriever有幾個幫助實現功能的方法,分別是:
#構造器(__init__()),filename(),download()和parseAndGetLinks()。
class Retriever:
def __init__(self,url):
#定義構造器,指向當前類的當前實例的引用。self 指向新創建的對象,另外一個參數是url.
#構造器實例化一個Retriever對象,并且把URL字符串和從filename()返回的與之對應的文件名保存為本地屬性。
self.url=url #將url的值付給self.url
self.file=self.filename(url)
def filename(self,url,deffile="index.html"): #定義filename方法,涉及另外兩個參數,url,deffile,很明顯deffile是后綴
parsedurl=urlparse(url,"http:",0)
urlparse(urlstr,defProtsch=None,allowFrag=None),defProtsch定義了缺醒的網絡協議和下載方式,allow是一個表示是否允許在URL中使用不完整成分的操作標志。allow_fragment如果是false,即使在URL addressing scheme支持fragment identifiers得情況下fragment identifiers也不允許,默認情況下fragment的默認值是true.
path=parsedurl[1]+parsedurl[2]
從urlparse分離出來的六個元素分別是(prot_shc,net_loc,path,params,query,frag).
parseurl[1]是net_loc,parseurl[2]是path.
和在一起正好是整個路徑
ext=splitext(path)
將path分解成目錄文件名和后綴標志。
if ext[1]=="":
如果沒有文件。ext是一個字符串,ext[0]就是目錄文件名,而ext[1]就是后綴名,說明沒有后綴
if path[-1]=="/":
并且path是比如說是以我的博客為例,http://blog.csdn.net/yangwenchao1983,分離后path[-1]=3,也就是字符串的最后一個字母,如果是/,說明有文件內容,
path=path+deffile
如果URL沒有尾綴的文件名,就用缺性的"index.html“作為文假名,可以說是一個主王爺,上面有各種文件公下載,現在沒有合適的文件,我們酒吧index.html作為補充。
else:
path=path+"/"+deffile #如果是一個完整的文件名,我們需要在后面加上/index.html 如果不含有"/"符號的話,
dir=dirname(path) #提取path字符串的目錄名稱
if sep!="/": #如果文件的分割符不是/
dir=replace(dir,"/",sep) #將dir中的/替換成分割符,/
if not isdir(dir): #使用isdir辨別文件類型不是目錄。
if exists(dir): unlink(dir) #如果不是目錄文件,就是用unlink移除,
makedirs(dir) #重新使用makedirs創建目錄文件
return path #返回經過整理的路徑
def download(self): #定義download()方法,使用try...except...來進行異常處理,
try:
retval=urlretrieve(self.url,self.file)
urlretrieve()不像urlopen()那樣對URL進行讀操作,它只是簡單的把位于urlstr處的HTML文件整個下載到你的本地硬盤中去,如果沒有給出localfile,它就會把數據保存到一個臨時文件中去。很明顯,這行程序的意思就是將self.url從望上的某個地方拷貝到硬盤的self.file中去。
except IOError:
如果文件不存在,就會引發IOerror,
retval=("***ERROR: invalid URL "%s"" %\self.url,)
沒有在有效的網址上找到這個文件,就將"***ERROR: invalid URL "%s""打印出來
return retval #返回得到的文件
def parseAndGetLinks(self):
如果上面的的處理沒有發現任何錯誤,就會調用parseAndGetLinks()對新下載打破的主頁進行分析,確定對那個主頁上的每一個連接應該采取什么樣的行動。
self.parser=HTMLParser(AbstractFormatter(DumbWriter(StringIO())))
使用HTMLParser的方法進行處理,StringIO是從內存中讀取數據,DumbWriter將事件流轉換為存文本文檔。
self.parser.feed(open(self.file).read())
將self.file文件打開并且一次性讀入上面定義的的文件中去
self.parser.close() #關閉文件
return self.parser.anchorlist #返回地址和日期
class Crawler:
Crawler由三個數據項組成,這三個數據項是由構造器在實例化階段報存在這里的。
count = 0 #靜態下載主頁計數器
def __init__(self,url):
self.q=[url]
第一個數據是q,這是一個有下載連接組成的隊列,這個清單在執行過程中是會變化的,沒處理一個主頁它就縮短一次,而在各下載主頁中發現一個新的連接就會被加長。
self.seen=[]
Crawler的另外兩個數據項包括seen-這是我們已經下載過的全體連接所組成的一個列表;
self.dom=urlparse(url)[1]
把主連接的域名報存在dom中,用這個值核對后續連接是否屬于這同一個區域。
def getPage(self,url):
getPage()方法用第一個連接實例化出一個Retriever對象,從她開始進行后續的處理。
r=Retriever(url)
使用上面定義過得Retriever類,付給r。
retval=r.download() #下載網頁連接,
if retval[0]=="*":
print retval,"...skipping parse"
return
Crawler.count=Crawler.count+1
Crawler還有一個靜態數據叫做count。這個計數器的作用就是記錄我們呢已經從望紅色那個下載到的對象的個數,每成功下載一個主頁,就讓它增加一個數。
print "\n(",Crawler.count,")"
print "URL:",url
print "FILE:",retval[0]
self.seen.append(url)
links=r.parseAndGetLinks()
for eachLink in Links:
if eachLink[:4]!="http" and find(eachLink,"://")==-1
print "*",eachLink,
以下鏈接將被忽略,不會被添加到待處理隊列里去的:屬于另外一個域的連接,已經被下載過得鏈接,已經放入待處理隊列里去的連接或者是"mailto:"連接。
if find(lower(eachLink),"mailto:")!=-1:應該是超連接
print "...discard,mailto link"
contine
if eachlink not in self.seen:
if find(eachLink,self.dom)==-1:
print "...discarded,not in domain"
else:
if eachLink not in self.q:
self.q.append(eachLink)
print "...new,aded to Q"
else:
print "...discarded,already in Q"
else:
print "...discarded,already processed"
def go(self):
while self.q:
url=self.q.pop()
self.getPage(url)
def main():
if len(argv)>1:
url=argv[1]
else:
try:
url=raw_input("Enter starting URL:")
except(KeyboardInterrupt,EOFError):
url=""
if not url: return
robot=Crawler(url)
robot.go()
if __name__=="__main__":
main()
main()只有在這個腳本程序在直接被調用時才會執行,它是程序的出發點,其他導入了crawl.py的模塊需要明確的調用main()才能開始處理。要讓main()開始執行,需要給它一個URL,如果已經在一個命令行給出URL(例如我們直接調用這個腳本程序的時候),它就會從給定的URL起開始運行;否則,腳本程序將進入交互模式,提示用戶輸入一個URL。有了初始連接之后,程序將對Crawler類進行實例化并開始執行。