Pythonのウェブクローラーを作成する方法



概要
ほとんどのPythonウェブクローリング/スクラップのチュートリアルは、クロールライブラリのいくつかの種類を使用します.あなたが物事を迅速に行われたい場合は、これは素晴らしいですが、あなたがどのようにフードの下に仕事を理解しない場合は、問題が発生するときにそれを修正する方法を知ることは困難になるでしょう.
このチュートリアルでは、Python標準ライブラリとリクエストモジュールのみを使用して、Pythonで完全にWebクローラを書く方法について説明しますhttps://pypi.org/project/requests/2.7.0/ ). プロキシAPIを使用する方法についても説明しますhttps://proxyorbit.com ) あなたのクローラーをブラックリストを得るから防ぐために.
これは主に教育目的のためですが、少し注意し、このクローラは、ライブラリを使用して書かれたスクレーパーとして頑丈で便利になることができます.それだけでなく、最も軽く、よりポータブルも可能でしょう.
Pythonとプログラミングの一般的な理解を持っていると仮定します.HTTPリクエストがどのように動作し、どのように正規表現が完全にコードを理解するために必要な作業の理解.私は、個々の機能の実施について詳細には行かないでしょう.代わりに、コードサンプルがどのように動作しているか、そしてある特定のものがどのように動作するかについて、高レベルの概観を行います.
このチュートリアルで作成されるクローラーは、GoogleのCrawlersが動作する方法に似た「インターネットをインデキシングする」という目標を持っています.明らかに我々はインターネットをインデックスすることはできませんが、アイデアは、このクローラは、インターネット上のリンクに従って、どこかのページ上のいくつかの情報と同様にそれらのリンクを保存することです.

開始する
最初の仕事はスクレーパーの下地を設定することです.我々はすべての機能を収容するためにクラスを使用するつもりです.また、再リクエストモジュールを必要とするので、それらをインポートします
import requests    
import re    

class PyCrawler(object):    
    def __init__(self, starting_url):    
        self.starting_url = starting_url                       
        self.visited = set()    

    def start(self):    
        pass                 

if __name__ == "__main__":    
    crawler = PyCrawler()    
    crawler.start()
これは非常に簡単に起動することがわかります.これらの種類を増加的に構築することが重要です.コードを少し、少しテストします.
我々は、後に我々の這っている努力で我々を助ける2つのインスタンス変数を持ちます.starting_url最初の起動URLは、我々のクローラが開始されますvisitedこれは、我々は現在、同じURLを2回訪問を防ぐために訪問したURLを追跡することができます.set ()を使用すると、o ( 1 )時間内に訪れたURLルックアップを続けます.

クロールサイト
今、私たちは実際にクローラーを書き始める.以下のコードはstartingchen urlにリクエストを行い、ページ上のすべてのリンクを展開します.それから、それはすべての新しいリンクの上で繰り返されて、新しいページから新しい関連を集めます.すべてのリンクが出発点から可能であるように削られるまで、それはこの再帰的なプロセスを続けます.一部のウェブサイトは、これらのサイトは他のサイトへのリンクを行うサイトよりも早く停止するので、自分の外にリンクしないでください.
import requests    
import re    
from urllib.parse import urlparse    

class PyCrawler(object):    
    def __init__(self, starting_url):    
        self.starting_url = starting_url    
        self.visited = set()    

    def get_html(self, url):    
        try:    
            html = requests.get(url)    
        except Exception as e:    
            print(e)    
            return ""    
        return html.content.decode('latin-1')    

    def get_links(self, url):    
        html = self.get_html(url)    
        parsed = urlparse(url)    
        base = f"{parsed.scheme}://{parsed.netloc}"    
        links = re.findall('''<a\s+(?:[^>]*?\s+)?href="([^"]*)"''', html)    
        for i, link in enumerate(links):    
            if not urlparse(link).netloc:    
                link_with_base = base + link    
                links[i] = link_with_base       

        return set(filter(lambda x: 'mailto' not in x, links))    

    def extract_info(self, url):                                
        html = self.get_html(url)                               
        return None                  

    def crawl(self, url):                   
        for link in self.get_links(url):    
            if link in self.visited:        
                continue                    
            print(link)                 
            self.visited.add(link)            
            info = self.extract_info(link)    
            self.crawl(link)                  

    def start(self):                     
        self.crawl(self.starting_url)    

if __name__ == "__main__":                           
    crawler = PyCrawler("https://google.com")        
    crawler.start()
我々は新しいコードの公正なビットを見ることができるように追加されています.
を開始するには、GetWise HTML、GetHorseリンク、クロール、およびExtractChores情報メソッドが追加されました.get_html()は現在のリンクでHTMLを得るために使われるget_links()現在のページからのリンクを抽出するextract_info()ページの特定の情報を抽出するために使用されます.
The crawl() 関数も追加されており、おそらくこのコードの最も重要かつ複雑な部分です.“クロール”は再帰的に動作します.それはStartRange URLで始まり、そのページからのリンクを抽出し、それらのリンクを繰り返してから、リンクを再帰的に自分自身にフィードバックします.
あなたがドアと部屋のシリーズのようにウェブを考えるならば、本質的にこのコードがしていることはそれらのドアを探していて、それがドアなしで部屋に着くまで、彼らを通って歩いています.これが起こるとき、それは未踏のドアを持って、その1つに入る部屋への帰り道です.開始位置からアクセス可能なすべてのドアがアクセスされるまで、それは永遠にこれをします.この種のプロセスは、再帰的なコードに非常にうまくそれを貸します.
あなたが現在このスクリプトを走らせるならば、それは探検して、それがGoogleから出発するとわかるすべての新しいURLを印刷します.コム

コンテンツの抽出
ページからデータを抽出します.このメソッド(ExtractLes情報)は、主にあなたのスクレーパーでしようとしているものに基づいています.このチュートリアルのために、我々がするつもりであることは、我々がページでそれを見つけることができるならば、メタタグ情報を抽出することです.
import requests    
import re    
from urllib.parse import urlparse    

class PyCrawler(object):    
    def __init__(self, starting_url):    
        self.starting_url = starting_url    
        self.visited = set()    

    def get_html(self, url):    
        try:    
            html = requests.get(url)    
        except Exception as e:    
            print(e)    
            return ""    
        return html.content.decode('latin-1')    

    def get_links(self, url):    
        html = self.get_html(url)    
        parsed = urlparse(url)    
        base = f"{parsed.scheme}://{parsed.netloc}"    
        links = re.findall('''<a\s+(?:[^>]*?\s+)?href="([^"]*)"''', html)    
        for i, link in enumerate(links):    
            if not urlparse(link).netloc:    
                link_with_base = base + link    
                links[i] = link_with_base    

        return set(filter(lambda x: 'mailto' not in x, links))    

    def extract_info(self, url):    
        html = self.get_html(url)    
        meta = re.findall("<meta .*?name=[\"'](.*?)['\"].*?content=[\"'](.*?)['\"].*?>", html)    
        return dict(meta)    

    def crawl(self, url):    
        for link in self.get_links(url):    
            if link in self.visited:    
                continue    
            self.visited.add(link)    
            info = self.extract_info(link)    

            print(f"""Link: {link}    
Description: {info.get('description')}    
Keywords: {info.get('keywords')}    
            """)    

            self.crawl(link)    

    def start(self):    
        self.crawl(self.starting_url)    

if __name__ == "__main__":    
    crawler = PyCrawler("https://google.com")     
    crawler.start()
ここでは、新しい印刷書式とExtractCount Infoメソッド以外にはあまり変更されません.
ここでの魔法は、ExtractHeight Infoメソッドの正規表現です.それは、フォーマットに続くすべてのメタタグのためにHTMLで検索します<meta name=X content=Y> を返します.
この情報は、すべてのリクエストのすべてのURLの画面に印刷されます.

統合プロキシ
ウェブクローリングとWebスクレージングの主な問題の一つは、あなたがあまりにも多くの要求をするならば、サイトがあなたを禁止するということです.通常、プロキシのアプローチでは、どこか他からのプロキシのリストを手動で出たり、購入したりする必要があります.これらのプロキシが動作しないか、または信じられないほど遅くウェブのクロールはるかに困難な時間の多く.
この問題を避けるために、我々は「回転プロキシAPI」と呼ばれるものを使用するつもりです.回転プロキシAPIは、私たちのためのプロキシの管理の世話をするAPIです.私たちがしなければならないのは、APIエンドポイントとブームにリクエストをすることです.プラットフォームにサービスを統合するには、いくつかの余分な行を必要としません.
我々が使用するサービスはプロキシの軌道ですhttps://proxyorbit.com ). 完全な開示、私自身を所有し、プロキシの軌道を実行します.
サービスは、Webクローリングアプリケーションのプロキシソリューションを作成するのに特化します.プロキシは、最高の作業プロキシだけがプールにあることを確認する継続的にチェックされます.
import requests    
import re    
from urllib.parse import urlparse    
import os    

class PyCrawler(object):    
    def __init__(self, starting_url):    
        self.starting_url = starting_url    
        self.visited = set()    
        self.proxy_orbit_key = os.getenv("PROXY_ORBIT_TOKEN")    
        self.user_agent = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36"    
        self.proxy_orbit_url = f"https://api.proxyorbit.com/v1/?token={self.proxy_orbit_key}&ssl=true&rtt=0.3&protocols=http&lastChecked=30"    

    def get_html(self, url):                                                                                                               
        try:                                                                                                                               
            proxy_info = requests.get(self.proxy_orbit_url).json()                                                                         
            proxy = proxy_info['curl']                                                                                                
            html = requests.get(url, headers={"User-Agent":self.user_agent}, proxies={"http":proxy, "https":proxy}, timeout=5)        
        except Exception as e:                                                                                                        
            print(e)                                                                                                                  
            return ""                                                                                                                 
        return html.content.decode('latin-1')                                                                                         

    def get_links(self, url):    
        html = self.get_html(url)    
        parsed = urlparse(url)    
        base = f"{parsed.scheme}://{parsed.netloc}"    
        links = re.findall('''<a\s+(?:[^>]*?\s+)?href="([^"]*)"''', html)    
        for i, link in enumerate(links):    
            if not urlparse(link).netloc:    
                link_with_base = base + link    
                links[i] = link_with_base    

        return set(filter(lambda x: 'mailto' not in x, links))    

    def extract_info(self, url):    
        html = self.get_html(url)    
        meta = re.findall("<meta .*?name=[\"'](.*?)['\"].*?content=[\"'](.*?)['\"].*?>", html)    
        return dict(meta)    

    def crawl(self, url):    
        for link in self.get_links(url):    
            if link in self.visited:    
                continue    
            self.visited.add(link)    
            info = self.extract_info(link)    

            print(f"""Link: {link}    
Description: {info.get('description')}    
Keywords: {info.get('keywords')}    
            """)    

            self.crawl(link)    

    def start(self):    
        self.crawl(self.starting_url)    

if __name__ == "__main__":    
    crawler = PyCrawler("https://google.com")    
    crawler.start()
ご覧の通り、ここではあまり変わりません.つの新しいクラス変数が作成されました.proxy_orbit_key , user_agent , and proxy_orbit_urlProxyCount OrbitRenkeyキーという名前の環境変数PROXY_ORBIT_TOKENUserRoundエージェントは、ブラウザから来ているように要求を確認するために、クローラーのユーザーエージェントをFirefoxに設定しますproxy_orbit_url プロキシ軌道APIは、我々が攻撃されることを示します.私たちの結果は、最後の30分でチェックされたSSLをサポートしているHTTPプロキシを要求するだけです.
GetCage HTMLでは、新しいHTTPリクエストは、プロキシの軌道API URLにランダムプロキシを取得し、プロキシの後ろからクロールしようとしているURLをつかんでリクエストモジュールに挿入します.
すべてがうまく行くならば、それはそれです!我々は今、Webページからデータをプルし、回転プロキシをサポートして実際の作業Webクローラを持っている必要があります.
更新:
一部の人々は、最終的なスクリプト、特に問題を抱えているようですget_html メソッド.これはプロキシ軌道APIトークンが設定されていないためです.コンストラクタで行があります.self.proxy_orbit_key = os.getenv("PROXY_ORBIT_TOKEN") . この行は環境変数PROXY_ORBIT_TOKEN これはAPIトークンを設定する場所です.トークンが設定されていない場合proxy = proxy_info['curl'] プロキシAPIは、認証されていないリクエストを示すJSONを返します.curl .
これを回避する2つの方法があります.最初のプロキシの軌道でサインアップし、トークンを取得し、あなたの設定PROXY_ORBIT_TOKEN 適切に変数env.番目の方法はあなたの代わりにget_html 以下の関数を使用します.
    def get_html(self, url):                                                                                                               
        try:                                                                                                                                                                                                                            
            html = requests.get(url, headers={"User-Agent":self.user_agent}, timeout=5)        
        except Exception as e:                                                                                                        
            print(e)                                                                                                                  
            return ""                                                                                                                 
        return html.content.decode('latin-1')
GetCount HTML関数を置き換えると、クローラーリクエストのすべてがIPアドレスを使用します.