2016-08-09 10 views
2

Я пытаюсь написать веб-браузер, но я застрял, потому что я не вижу бесконечного цикла где-то в моем коде.Не вижу бесконечного цикла

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

Не связанный с вашим вопросом, но одно предложение: Сделать PoolManager в '__init__' и использовать его повсюду, чтобы извлечь максимальную выгоду из этого. – 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, показывающий команду, которая выполнялась, когда программа была прервана. Сделайте это несколько раз, и вы должны получить представление о том, где это происходит. В качестве альтернативы, используйте отладчик и паузу, когда он находится в бесконечном цикле, и используйте функцию «шаг» для перехода к следующей строке выполнения, чтобы вы могли следить за выполнением программы. PyCharm - отличный редактор, который включает отладчик. Он имеет хорошую автозаполнение и просто хорош во всем. Это бесплатно, проверьте это.

+0

почему downvote? –

+0

defaultdict - неправильное решение! Проверка наличия значения в списке занимает время O (n), а использование defaultdict занимает только время O (1)! –

+0

@ GiorgianBorca-Tasciuc fixing ... –

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... 

Если честно, я тоже не вижу бесконечного цикла. Это поможет, если вы разместите какую-то информацию.

EDIT: Обратите внимание, что в приведенном выше ответе я написал, что использование defaultdict является неправильным решением. Я хотел сказать, что использование списка - неправильное решение!

EDIT 2: @Jona Кристофер Санвальд сделал точку более действенной, чем моя (см. Его комментарий по вопросу OP). Возможно, было бы более продуктивно добавлять в ваш класс свойство max_visit и current_visit (установлено как 1000 или около того). Начните с current_visit в 0, и каждый раз, когда вы посещаете сайт, увеличивайте 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_visitmax_visit, что позволяет сканировать в партиях max_visit.

+0

ваш код даже не запускается –

+0

@ uoɥʇʎPʎzɐɹC Ну, конечно, это не совсем!Это предназначено только для руководства к ОП. –

+0

см. Мой ответ, список намного лучше –

Смежные вопросы