2016-08-09 5 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__'にして、それを最大限に活用するために使用してください。 – shazow

答えて

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) 

defaultdictは、インデックスが存在しない場合に使用されるデフォルトを持っている辞書です。しかし、これは間違った解決策です。私のコードに示されているように、セットはより効率的でエレガントなものになります。

セットは@ジョルジャンの答えと同じくらい速くO(1)時間を使います。

Ctrl-Cを使用すると、無限ループ中にプログラムが中断することがあります。これにより、プログラムが中断されたときに実行されていたコマンドを示すTracebackが出力されます。これを数回行い、それが起こる場所を知るべきです。または、デバッガを使用して、無限ループに入っているときには一時停止し、次の実行行に実行するために「ステップ」機能を使用して、プログラムの実行に従うことができます。 PyCharmはデバッガを含む素晴らしいエディタです。それは良いオートコンプリートを持っていて、ちょうど良い周りです。それは無料です、それをチェックしてください。

+0

なぜdownvote? –

+0

defaultdictは間違った解決策です!値がリストにあるかどうかをチェックするには、O(n)時間を使うのに対し、defaultdictはO(1)時間しかかかりません! –

+0

@ GiorgianBorca-Tasciuc fixed ... –

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を使用することが間違った解決策であることに注意してください。私はリストを使っているのは間違った解決策だと言っていました!

EDIT 2:@Jona Christopher Sahnwaldは、私よりも有効なポイントを作った(OPの質問のコメントを参照)。クラスにmax_visitcurrent_visitのプロパティを追加すると、生産性が向上する可能性があります(1000程度に設定)。 current_visitは0で始まり、サイトを訪れるたびにcurrent_visitを増やします。 current_visitmax_visitより大きい場合、クロールを中止します。再帰を使用して訪問したWebサイトを再帰的に処理するのではなく、何らかの種類のスタックを実装して中断するのではなく、一時停止/再開できる方が良いかもしれないことに注意してください。これと同じように:

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を超えています。 Giogianのコードから適応

+0

あなたのコードは実行されません –

+0

@uoɥʇʎPʎzɐɹCそれはもちろん完全ではありません!それはOPに導くことを意味するだけです。 –

+0

私の答えを参照してください、リストははるかに良い –

関連する問題