Не вижу бесконечного цикла
Я пытаюсь написать 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)
Он работает вечно. Как я должен гарантировать, что устраню дублирующие ссылки?
2 ответа:
Адаптировано из кода Джоджиана:
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)
Множество использует время O (1) - так же быстро, как и ответ @Giorgian.
defaultdict
это словарь, который имеет значение по умолчанию, которое используется, если индекс не существует. Однако это неправильное решение. Набор будет более эффективным и элегантным, как показано в моем коде.Используйте
Ctrl-C
, чтобы прервать вашу программу, когда она находится в бесконечном цикле. При этом будет выведена обратная трассировка, показывающая команду, которая выполнялась, когда программа была прервана. Сделайте это несколько раз, и вы должны получить хорошее представление о том, где это происходит. Кроме того, используйте отладчик и приостановите его, когда он находится в бесконечном цикле, и используйте функцию "шаг", чтобы перейти к следующей строке выполнения, чтобы вы могли следить за выполнением программы. PyCharm-это отличный редактор, который включает в себя отладчик. Он имеет хорошее автозавершение и просто хорошее многоборье. Это бесплатно, проверьте это.
Добавьте свойство
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: обратите внимание, что в приведенном выше ответе я написал, что использование
[13]}EDIT 2: @Jona Christopher Sahnwald сделал замечание более обоснованным, чем мое (см. Его комментарий под вопросом ОП). Возможно, было бы более продуктивно добавить adefaultdict
является неправильным решением. Я хотел сказать, что использование списка - неправильное решение!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_visit
превыситmax_visit
, что позволит вам выполнять обход в пакетахmax_visit
.