2016-08-09 41 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

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