Pythonは簡易Web爬虫類を実現

8052 ワード

概要:
ネットワーク爬虫類(ウェブクモとも呼ばれる)は、ネットワークロボットであり、一定の規則に従って、自動的に情報をつかむプログラムやスクリプトである.インターネットが大きなクモの巣であり、各ページ間がハイパーリンクという線で相互に接続されていると仮定すると、私たちの爬虫類ウィジェットはこれらの線を通じて新しいページを絶えず探すことができます.
Pythonは単純主義思想を代表する解釈型で、対象向けで、機能が強い高級プログラミング言語である.文法が簡潔で、動的データ型と高レベルの抽象データ構造を有しているため、プラットフォーム間特性が良好で、特に爬虫類などのプログラムの実現に適している.また、PythonはSpyderのような爬虫類フレームワーク、BeautifulSoupのような解析フレームワークを提供し、様々な複雑な爬虫類プログラムを簡単に開発することができる.
この記事では、Pythonが持参したurllibとBeautifulSoupライブラリを使用して、各URLアドレスと対応するタイトルコンテンツを這い出すための簡単なweb爬虫類を実現しています.
プロセス:
  • 爬虫アルゴリズムは、入力から読み出したURLを初期アドレスとして、そのアドレスにRequest要求を発行する.
  • 要求されたアドレスは、すべてのコンテンツを含むString変数を返し、この変数を使用してBeautifulSoupオブジェクトをインスタンス化し、コンテンツをDOMツリーに解析することができる.自分のニーズに合わせて正規表現を作成し、最後にHTMLタグで必要な内容と新しいURLを解析し、新しいものをキューに入れます.
  • 現在所在するURLアドレスと登った内容について、一定のフィルタリング、整理を行った後にインデックスが作成され、これは単語-ページの記憶構造である.ユーザーが検索文を入力すると、対応する分詞関数が文を分解してキーワードを取得し、各キーワードに基づいて対応するURLを検索します.この構成により,この単語に対応するアドレスリストを迅速に取得できる.ここではツリー構造の格納方式を用いて,Pythonの辞書やリストタイプが単語辞書ツリーをより良く構築できる.
  • は、キューから現在のURLアドレスをポップアップし、キューを空にしない条件で、アルゴリズムはキューから新しいページアドレスを取得し続け、上記の手順を繰り返す.

  • 実装:
    環境:
  • Python 3.5 or Anaconda3
  • BeautifulSoup 4

  • 次のコマンドでBeautifulSoup 4をインストールできます.Ubuntuユーザーの場合は、コマンドの前にsudo:pip install beautifulsoup4を付けてください.
    プログラムは、URLアドレス管理、Htmlコンテンツリクエスト、Htmlコンテンツ解析、インデックス作成、爬虫類マスタープロセスにそれぞれいくつかのクラスを実現しています.私はプログラム全体をClassごとに分けて説明して、最後に彼らを一緒に置くだけでコードを実行することができます.
    UrlManagerクラスこのクラスはURLアドレスを管理するために使用されます.new_urlsは、まだ登録されていないURLアドレスを保存するために使用されます.old_urlsはすでに登ったアドレスを保存し、2つの変数はsetタイプを使用してコンテンツの一意性を保証します.サイクルごとにadd_new_urls()はnew_に提供されています.urls変数に新しいurlsを追加する方法.add_new_url()メソッドは、urlアドレスごとに重複性検査を行い、条件に合致した場合にのみ追加操作を行う.get_urls()は、新しいurlアドレスを取得する方法を外部に提供する.has_new_url()メソッドは、キューが空であるかどうかを確認するために使用されます.
    import re
    import urllib.request
    import urllib.parse
    from bs4 import BeautifulSoup
    
    
    class UrlManager(object):
        def __init__(self):
            self.new_urls = set()
            self.old_urls = set()
    
        def add_new_url(self, url):
            if url is None:
                return
            if url not in self.new_urls and url not in self.old_urls:
                self.new_urls.add(url)
    
        def add_new_urls(self, urls):
            if urls is None or len(urls) == 0:
                return
            for url in urls:
                self.add_new_url(url)
    
        def has_new_url(self):
            return len(self.new_urls) != 0
    
        def get_new_url(self):
            new_url = self.new_urls.pop()
            self.old_urls.add(new_url)
            return new_url
    
    

    HtmlDownloaderクラスこのクラスはurlアドレスにRequestリクエストを送信し,その応答を取得する方法を実現し,クラス内のdownload()メソッドを呼び出すことで実現できる.ここで注意したいのはページの符号化の問題で、ここで私はUTF-8を使ってdecode復号を行って、あるページはGBK符号化を使うかもしれなくて、実際の情況によって修正します.
    class HtmlDownloader(object):
        def download(self, url):
            if url is None:
                return None
            try:
                request = urllib.request.Request(url)
                response = urllib.request.urlopen(request)
                content = response.read().decode('utf-8').encode('utf-8')
                if content is None:
                    return None
                if response.getcode() != 200:
                    return None
            except urllib.request.URLError as e:
                print(e)
                return None
    
            return content
    

    HtmlParserクラスこのクラスは、BeautifulSoupオブジェクトをインスタンス化することによってページの解析を行います.Pythonを使用して作成されたHTML/XMLドキュメント解析器です.ドキュメントをDOMツリーに解析することで、ユーザーにキャプチャする必要があるデータを提供し、ナビゲーション、検索、分析ツリーの変更などの機能を処理するための簡単な関数を提供します.このクラスの鍵は_get_new_urls()、_get_new_content()、get_url_title()の3つの方法.最初の方法は、ページに含まれるハイパーリンクを解析するために使用され、最も重要なのは、解析するラベルを選択し、適切な正規表現を構築することです.ここでは、aラベルに一致する正則を定義し、以下のようにすべてのサイト内リンクを取得します.
    links = soup.find_all('a', href=re.compile(r'^(%s).*(/|html)$' % self.domain))`
    

    次の2つのクラスはいずれもHtmlタグを解析することによってtitleを取得する方法であり,最終的にparse()で呼び出し_get_new_content()を使用してtitleコンテンツを取得します.具体的なタグのアクセス方法については詳しくは述べませんが、読者はBeautifulSoupの公式ドキュメントを自分で閲覧することができます.
    class HtmlParser(object):
        def __init__(self, domain_url):
            self.domain = domain_url
            self.res = HtmlDownloader()
    
        def _get_new_urls(self, page_url, soup):
            new_urls = set()
            links = soup.find_all('a', href=re.compile(r'^(%s).*(/|html)$' % self.domain))
    
            try:
                for link in links:
                    new_url = link['href']
                    new_full_url = urllib.parse.urljoin(self.domain, new_url)
                    new_urls.add(new_full_url)
    
                new_urls = list(new_urls)
                return new_urls
            except AttributeError as e:
                print(e)
                return None
    
        def _get_new_content(self, page_url, soup):
            try:
                title_name = soup.title.string
                return title_name
            except AttributeError as e:
                print(e)
                return None
    
        def get_url_title(self):
            content = self.res.download(self.domain)
    
            try:
                soup = BeautifulSoup(content, 'html.parser', from_encoding='utf-8')
                title_name = soup.title.string
                return title_name
            except:
                title_name = 'None Title'
                return title_name
    
        def parse(self, page_url, html_cont):
            if page_url is None or html_cont is None:
                return None
    
            soup = BeautifulSoup(html_cont, 'html.parser', from_encoding='utf-8')
            new_data = self._get_new_content(page_url, soup)
            new_urls = self._get_new_urls(page_url, soup)
    
            return new_urls, new_data
    
    

    BuildIndexこのクラスは、各URLアドレスと彼のタイトルに含まれるキーワードとのインデックス関係を確立し、1つのDict変数に保存し、各タイトルは複数のキーワードに対応し、各タイトルも複数のurlアドレスに対応するため、各キーワードも複数のurlアドレスに対応し、具体的な形式は以下の通りである:index = {'keyword': [url1, url2, ... ,urln], ...}中、add_page_index()メソッドは、各タイトルに対して分詞処理を行い、add_を呼び出した.key_index()メソッドはkeyword-urlの対応関係をインデックスに格納し,この中でも繰り返しチェックを行った.考えてみれば、この分詞方法は英語の文に限られており、中国語では特定の分詞ツールが必要です.
    class BuildIndex(object):
        def add_page_index(self, index, url, content):
            words = content.split()
            for word in words:
                index = self.add_key_index(index, url, word)
            return index
    
        def add_key_index(self, index, url, keyword):
            if keyword in index:
                if url not in index[keyword]:
                    index[keyword].append(url)
            else:
                temp = []
                index[keyword] = temp
                index[keyword].append(url)
            return index
    

    SpiderMainこれは爬虫類のテーマクラスで、他のいくつかのクラスで生成されたオブジェクトを呼び出すことで爬虫類の実行を実現します.このクラスはインスタンス化されると永続的に上記のクラスのオブジェクトが生成され、craw()メソッドでユーザが提供するurlアドレスを取得すると、リクエスト、ダウンロード、解析、インデックスの作成が順次行われる.最後に、この方法はindex、graphの2つの変数を返します.それぞれ、キーワードセットごとに対応するアドレス、keyword-urlsインデックス、次のindex = {'keyword': [url1, url2, ... ,urln], ...} urlとそのページに含まれるurls、url-suburlsインデックス、次のgraph = {'url': [url1, url2, ... ,urln], ...}
    class SpiderMain(object):
        def __init__(self, root_url):
            self.root_url = root_url
            self.urls = UrlManager()
            self.downloader = HtmlDownloader()
            self.parser = HtmlParser(self.root_url)
            self.build = BuildIndex()
    
        def craw(self):
            index = graph = {}
            self.urls.add_new_url(self.root_url)
            while self.urls.has_new_url():
                try:
                    new_url = self.urls.get_new_url()
                    html_cont = self.downloader.download(new_url)
                    new_urls, new_title = self.parser.parse(new_url, html_cont)
                    index = self.build.add_page_index(index, new_url, new_title)
                    graph[new_url] = list(new_urls)
                    self.urls.add_new_urls(new_urls)
                except Exception as e:
                    print(e)
                    return None
    
            return index, graph
    
    

    最後に、プログラムに次のコードを追加すると、爬虫類の実行に成功します.
    if __name__ == '__main__':
        spider = SpiderMain('http://www.xael.org/')
        index, graph = spider.craw()
        print(index)
        print(graph)