2016-08-09 79 views
2

我想寫一個webcrawler,但我卡住,因爲我不能看到無限循環在我的代碼中的某處。看不到無限循環

class Crawler(object): 
    def __init__(self, url, query, dir = os.path.dirname(__file__)): 
     self.start_url = url 
     self.start_parsed = urllib3.util.parse_url(url) 
     self.query = re.compile(query, re.IGNORECASE) 
     self.dir = dir 
     self.__horizon = set() 
     self.log = [] 

     self.__horizon.add(url) 
     self.log.append(url) 
     print("initializing crawler....") 
     print(locals()) 

    def start(self, depth= 5, url = '/'): 
     print(url, depth) 
     self.log.append(url) 
     if depth > 0: 
      pool = urllib3.PoolManager() 
      data = pool.request("GET", self.start_url if url == '/' else url).data.decode('utf-8') 

      valid_list = [] 
      self.add_horizon(parser_soup.get_links(data), valid_list) 

      if re.search(self.query, parser_soup.get_text(data)): 
       self.output(data) 

      for u in valid_list: 
       self.start(depth = (depth-1), url = u) 

    def output(self, data): 
     with open(os.path.join(self.dir, get_top_domain(self.start_parsed.host) + '.' + str(time.time()) + '.html'), 'w+') as f: 
      f.write(data) 

    def add_horizon(self, url_list, valid_list = []): 
     for url in url_list: 
      if get_top_domain(url) == get_top_domain(self.start_parsed.host) \ 
        and (not str(url) in self.log or not str(url) in self.__horizon): 
       valid_list.append(str(url)) 

     self.__horizon.update(valid_list) 

它永遠運行。我應該如何確保消除重複鏈接?

+0

你是什麼意思「看不到無限循環?」 –

+0

@uoɥʇʎPʎzɐɹC他不明白爲什麼他的代碼會陷入無限循環。 –

+0

與您的問題無關,但有一個建議:使'__init__'中的PoolManager成爲可能,並在整個過程中使用它以獲得最大收益。 – shazow

回答

2

從Giogian代碼改編:

class Crawler(object): 
    def __init__(self, url, query, dir=os.path.dirname(__file__)): 
     self.visited = set() 
     # Rest of code... 

    def start(self, depth=5, url='/'): 
     if url in self.visited: 
      return True 
     self.visited.add(url) 

defaultdict是具有如果索引不存在,這是使用默認的字典。但是,這是錯誤的解決方案。如我的代碼所示,一個集合會更高效,更優雅。

一組使用O(1)時間 - 就像@ Giorgian的答案一樣快。

使用Ctrl-C在程序處於無限循環時中斷程序。這將打印一個Traceback,顯示程序中斷時正在執行的命令。這樣做幾次,你應該知道它發生了什麼。或者,使用調試器並在其處於無限循環時暫停,並使用「step」功能運行到下一個執行行,以便可以執行程序的執行。 PyCharm是一個非常棒的編輯器,它包含一個調試器。它具有良好的自動完成功能,並且具有很好的全面性。它是免費的,檢查出來。

+0

爲什麼downvote? –

+0

defaultdict是錯誤的解決方案!檢查一個值是否在列表中需要O(n)時間,而使用defaultdict只需要O(1)次! –

+0

@ GiorgianBorca-Tasciuc修復... –

2

在您的搜尋器中添加一個visited屬性。

from collections import defaultdict 
class Crawler: 
    def __init__(self, url, query, dir = os.path.dirname(__file__)): 
     self.visited = defaultdict(bool) 
     # Rest of code... 

    def start(self, depth= 5, url = '/'): 
     if self.visited[url]: 
      return True 
     self.visited[url] = True 
     # Rest of code... 

說實話,我看不到無限循環。如果你發佈了一些輸出,這將有所幫助。

編輯:請注意,在上面的答案我寫道,使用defaultdict是錯誤的解決方案。我的意思是說,使用列表是錯誤的解決方案!

編輯2:@Jona Christopher Sahnwald提出了比我更有效的觀點(請參閱他在OP的問題下的評論)。在你的課堂中添加一個max_visitcurrent_visit屬性可能會更有成效(設置爲1000左右)。從0開始current_visit,並且每次訪問網站時,都會增加current_visit。當current_visit大於max_visit時,請中止爬網。請注意,不是使用遞歸來遍歷訪問的網站,最好實現某種堆棧,以便可以暫停/恢復抓取而不是中止。像這樣:

from collections import defaultdict 

class Crawler: 
    def __init__(self, url, query, dir = os.path.dirname(__file__)): 
     self.visited = defaultdict(bool) 
     self.current_visit = 0 
     self.max_visit = 1000 
     self.to_visit = [] 
     # Rest of code... 

    def start(self, depth=5, url = '/'): 
     self.to_visit.append((url, 1)) 
     while len(self.to_visit) > 0: 
      url, current_depth = self.to_visit.pop() 
      if current_depth > depth: 
       continue 
      elif visited[url]: 
       continue 
      elif self.current_visited > self.max_visited: 
       break 

      self.current_visited += 1 
      visited[url] = True 

      # Code that does something for each page (like download it, etc) 

      # Code that finds links on page... 

      for link in links_on_page: 
       self.to_visit.append((link, current_depth + 1)) 

這樣的話,你可以暫停抓取一次current_visit超過max_visit,讓您在max_visit批量抓取。

+0

你的代碼甚至不運行 –

+0

@uoɥʇʎPʎzɐɹC當然它不完整!這只是爲了指導OP。 –

+0

看到我的答案,列表好得多 –