さあ!あなたの最初のWebクローラを書きます

サイトをクロールするために、我々は最初の関心のデータを含むページをダウンロードする必要があり、このプロセスは一般に呼ば(クロール)クロールウェブサイトのクロールが、多くの方法があり、方法がより適切であるの選択は、標的部位の構造に依存します。この章では、まず安全のページをダウンロードする方法について説明しますし、サイトをクロール、次の3つの一般的な方法をご紹介します。

  • サイトマップをクロール。
  • 各ページを介してデータベースIDを使用しました。
  • Webリンクを追跡。

これまでのところ、我々は交互に二つの用語をクロールし、クロールに使用している、私たちが最初にこれらの2つの方法との違いの類似性を定義してみましょう。

1.5.1の比較は、グリップとクロール

あなたの懸念の情報およびサイトコンテンツや構造に応じて、ウェブサイトやクロールをクロールする必要があるかもしれません。そして、彼らはどのような違いは、それをしませんか?

ウェブは、一般的にクロールし、特定のWebサイトについて、これらのサイトで特定の情報を取得します。サイトの場所の変更情報やサイトの変更、そして必要性を変更する場合は、これらの特別なページにアクセスするために使用されるWebクローラーは、発生します。たとえば、あなたがこれを達成するために、ネットワークを介して、日替わりを見るためにあなたの好きな地元のレストランをつかむしたい場合があります、あなたは、そのWebサイト毎日更新された情報の一部を取得する必要があります。

これとは対照的に、通常は一般的な方法を構築したネットワークのクロールは、目標は、サイトまたはネットワーク全体のトップレベルドメインのシリーズです。クロールは、より具体的な情報を収集するために使用することができますが、より一般的な状況では、ネットワーク、多くの異なるサイトまたはページから小型かつ普遍的な情報へのアクセスをクロールし、他のページへのリンクをたどるれます。

クロールとクロールに加えて、我々は、第8章では、Webクローラをご紹介します。クロール爬虫類は、サイトのリストを指定するために使用される、またはより広範な複数のサイト間で、あるいはインターネットを介してクロールすることができます。

一般的に、我々は特定の用語が私たちのユースケースを反映して使用します。あなたがウェブクローラを開発する場合、あなたが技術、ライブラリやパッケージを使用するには、彼らは違いに気付くことがあります。これらのケースでは、あなたを助けることができる別の用語のあなたの理解は適切なパッケージを選択するか、専門用語をベースに使用(例えば、唯一のクロール?また爬虫類に適用されますか?)。

1.5.2ダウンロードページ

ウェブをクロールするには、まずそれをダウンロードする必要があります。次のサンプルスクリプトは、Pythonの使用urllibモジュールのダウンロードURLを。

import urllib.request
def download(url):
    return urllib.request.urlopen(url).read()

ときに、着信URLパラメータ、関数はそのダウンロードページやHTMLに戻ります。しかし、このコードスニペットの存在はページをダウンロードするとき、我々はそのような存在しない可能性が要求されたページとして、多少の誤差が制御することはできません発生する可能性があるという問題があります。この場合、urllib例外がスローされ、その後、スクリプトを終了します。安全、より安定したビルドバージョンを与え、その後、次のと、あなたがそれらをキャッチすることができます。

import urllib.request
from urllib.error import URLError, HTTPError, ContentTooShortError

def download(url):
    print('Downloading:', url)
    try:
        html = urllib.request.urlopen(url).read()
    except (URLError, HTTPError, ContentTooShortError) as e:
        print('Download error:', e.reason)
        html = None
    return html

ダウンロードURLやエラーが発生したときに今、この関数は例外を捕捉し、その後、返すことができますNone

本書では、我々は、(上記のコードのように)の方法は、あなたの代わりにプロンプトを使用しての、ファイル内のコードを書くことを前提としています。コードやPythonはIPythonプロンプトプロンプト>>>あなたを見つけるときにIn [1]:スタートを、あなたはメインファイルにそれを入力する必要が使用されている、またはPythonインタプリタでこれらの関数やクラスのインポート後にファイルを保存します。

1。ダウンロードを再試行

ダウンロードは、多くの場合、サーバが過負荷戻ったときなど、一時的なものであり、中にエラーが発生しました503 Service Unavailableエラー。このタイプのエラーのために、我々は、サーバーが今、問題が解決された可能性があるため、短い待ち時間の後、再びそれをダウンロードして試すことができます。しかし、我々はすべてのエラーを必要とし、再度ダウンロードしようとしないでください。サーバが返した場合404 Not Found、このエラーが発生し、そのページが現在存在していないことを示し、再び同じ要求をしてみてください一般的に異なる結果が表示されません。

インターネットエンジニアリングタスクフォース(インターネット・エンジニアリング・タスク・フォース)は、あなたが学ぶことができるHTTPエラーの完全なリストを定義し4xx、問題の要求があったときにエラーが発生しますが、5xxサーバーに問題がある場合にエラーが発生します。そこで、我々はちょうどことを確認する必要があるdownloadが発生した場合に機能を5xxエラー再試行のダウンロード。以下のダウンロード機能を再試行するためのコードの新しいバージョンをサポートしています。

def download(url, num_retries=2):
    print('Downloading:', url)
    try:
        html = urllib.request.urlopen(url).read()
    except (URLError, HTTPError, ContentTooShortError) as e:
        print('Download error:', e.reason)
        html = None
        if num_retries > 0:
               if hasattr(e, 'code') and 500 <= e.code < 600:
             # recursively retry 5xx HTTP errors
             return download(url, num_retries - 1)
    return html

现在,当download函数遇到5xx错误码时,将会递归调用函数自身进行重试。此外,该函数还增加了一个参数,用于设定重试下载的次数,其默认值为两次。我们在这里限制网页下载的尝试次数,是因为服务器错误可能暂时还没有恢复。想要测试该函数,可以尝试下载http://httpstat.us/500,该网址会始终返回500错误码。

    >>> download('http://httpstat.us/500')
Downloading: http://httpstat.us/500
Download error: Internal Server Error
Downloading: http://httpstat.us/500
Download error: Internal Server Error
Downloading: http://httpstat.us/500
Download error: Internal Server Error

从上面的返回结果可以看出,download函数的行为和预期一致,先尝试下载网页,在接收到500错误后,又进行了两次重试才放弃。

2.设置用户代理

默认情况下,urllib使用Python-urllib/3.x作为用户代理下载网页内容,其中3.x是环境当前所用Python的版本号。如果能使用可辨识的用户代理则更好,这样可以避免我们的网络爬虫碰到一些问题。此外,也许是因为曾经历过质量不佳的Python网络爬虫造成的服务器过载,一些网站还会封禁这个默认的用户代理。

因此,为了使下载网站更加可靠,我们需要控制用户代理的设定。下面的代码对download函数进行了修改,设定了一个默认的用户代理‘wswp’(即Web Scraping with Python的首字母缩写)。

def download(url, user_agent='wswp', num_retries=2):
    print('Downloading:', url)
    request = urllib.request.Request(url)
    request.add_header('User-agent', user_agent)
    try:
        html = urllib.request.urlopen(request).read()
    except (URLError, HTTPError, ContentTooShortError) as e:
        print('Download error:', e.reason)
        html = None
        if num_retries > 0:
            if hasattr(e, 'code') and 500 <= e.code < 600:
                # recursively retry 5xx HTTP errors
                return download(url, num_retries - 1)
    return html

现在,如果你再次尝试访问meetup.com,就能够看到一个合法的HTML了。我们的下载函数可以在后续代码中得到复用,该函数能够捕获异常、在可能的情况下重试网站以及设置用户代理。

1.5.3 网站地图爬虫

在第一个简单的爬虫中,我们将使用示例网站robots.txt文件中发现的网站地图来下载所有网页。为了解析网站地图,我们将会使用一个简单的正则表达式,从&lt;loc&gt;标签中提取出URL。

私たちの現在ので、私たちは、エンコーディング変換を処理するコードを更新する必要があるdownload機能は、単にバイトを返します。-次の章では、我々はより強固な分析方法紹介しますCSSセレクタを次のコードサンプルクローラです。

import re

def download(url, user_agent='wswp', num_retries=2, charset='utf-8'):
    print('Downloading:', url)
    request = urllib.request.Request(url)
    request.add_header('User-agent', user_agent)
    try:
        resp = urllib.request.urlopen(request)
        cs = resp.headers.get_content_charset()
        if not cs:
            cs = charset
        html = resp.read().decode(cs)
    except (URLError, HTTPError, ContentTooShortError) as e:
        print('Download error:', e.reason)
        html = None
        if num_retries > 0:
            if hasattr(e, 'code') and 500 <= e.code < 600:
            # recursively retry 5xx HTTP errors
            return download(url, num_retries - 1)
    return html

def crawl_sitemap(url):
    # download the sitemap file
    sitemap = download(url)
    # extract the sitemap links
    links = re.findall('<loc>(.*?)</loc>', sitemap)
    # download each link
    for link in links:
        html = download(link)
        # scrape html here
        # ...

今、サイトマップのクローラを実行し、サンプルサイトのすべての国からのダウンロードページ。

    >>> crawl_sitemap('http://example.python-scraping.com/sitemap.xml')
Downloading: http://example.python-scraping.com/sitemap.xml
Downloading: http://example.python-scraping.com/view/Afghanistan-1
Downloading: http://example.python-scraping.com/view/Aland-Islands-2
Downloading: http://example.python-scraping.com/view/Albania-3
... 

上記のコードのようdownloadに示す方法は、我々は正規表現の処理サイトの応答を使用する文字エンコーディングを更新する必要があります。Pythonのread正規表現文字列が要求される一方、この方法は、バイトを返します。私たちのコードがサイトのメンテナに応じて適切な文字エンコーディングヘッダーを含める依存しています。あなたは頭の文字エンコーディングを返さない場合は、デフォルト値のUTF-8として設定し、最大の希望を持っています。リターンヘッダが誤って符号化された、またはコードが設定されていないとUTF-8でない場合はもちろん、これはエラーをスローします。(参照符号化に推測し、より洗練された方法もありますhttps://pypi.python.org/pypi/chardet実装することは非常に容易であるが、)。

これまでのところ、サイトが期待爬虫類マップに沿ったものとなっています。しかし、前述したように、我々はに頼ることができないSitemap各ページへのリンクを提供した文書。次のセクションでは、我々は別の簡単な爬虫類をご紹介します、爬虫類はもはやに依存しないSitemapファイル。

あなたはいつでもクロールを継続したくない場合は、Pythonインタプリタやプログラムの実行を終了するにはCtrl + CまたはCMD + Cを押すことができます。

1.5.4 IDトラバース爬虫類

このセクションでは、我々は、サイトの構造内のすべてのコンテンツに簡単にアクセスする弱点を利用します。URLの国(または地域)のいくつかの例を以下に示します。

図から分かるように、これらのURLは、国(または地域)(ページの別名など)の名前とIDを含む、URLパスの最後の部分のみが異なります。URLにエイリアスが含まれているページが検索エンジン最適化における役割を果たして助けることができる、非常に一般的です。一般的には、Webサーバーは、データベースのレコードを一致させるためにのみIDを使用して、この文字列を無視します。以下は、私たちが見て、それを削除しますhttp://example.python-scraping.com/view/1リンクテストサンプルサイトがまだ使用可能であるかどうか。試験結果は図1.1に示します。

.. \ 0101.jpg {70%}

図1.1

図1.1からわかるように、ページはまだこの方法が有用であることを、正常にロードすることができます。今、私たちは、すべての国(または地域)のダウンロードページにのみデータベースIDを使用して、ページの別名を無視することができます。技術を使用して、次のコード。

import itertools

def crawl_site(url):
    for page in itertools.count(1):
        pg_url = '{}{}'.format(url, page)
        html = download(pg_url)
        if html is None:
            break
        # success - can scrape the result

今、私たちは、この機能、着信ベースURLを使用することができます。

>>> crawl_site('http://example.python-scraping.com/view/-')
Downloading: http://example.python-scraping.com/view/-1
Downloading: http://example.python-scraping.com/view/-2
Downloading: http://example.python-scraping.com/view/-3
Downloading: http://example.python-scraping.com/view/-4
[...]

このコードでは、IDのトラバース、ダウンロードエラー停止するまで、私たちは、この時間は、国(または地域)の最後のページをクロールするために到着したことを想定しています。しかし、この実装はいくつかのレコードが削除された可能性があります方法に問題があると、IDデータベースとの間に連続的ではありません。この時点では、間隔を置いたポイントへのアクセス限り、爬虫類はすぐに終了します。ここでは、このコードの改良版があり、繰り返し誤差をダウンロードし、このリリースで連続発生した後、プログラムを終了します。

def crawl_site(url, max_errors=5):
    for page in itertools.count(1):
        pg_url = '{}{}'.format(url, page)
        html = download(pg_url)
        if html is None:
            num_errors += 1
            if num_errors == max_errors:
                # max errors reached, exit loop
                break
        else:
            num_errors = 0
            # success - can scrape the result

上記のコードは、爬虫類が大幅に削除されたり隠されたレコードの顔を横断早期停止のリスクを低減、散歩を停止します5つの連続したエラーをダウンロードする必要が実現しています。

サイトをクロールすると、IDを横断することは非常に便利な方法ですが、サイトマップや爬虫類は、このアプローチは、常に利用可能保証することはできません。例えば、いくつかのサイトがない場合は、URLの別名でページが、それが返されますかどうかを確認します404 Not Foundエラーを。他のサイトが非連続IDの数が多いとして使用される、またはIDとして値を使用しませんが、この時間は、それが困難で自分の役割を果たして横断します。少なくとも10桁を含むポリペプチドをコードIDとして使用可能例えば、アマゾン。ISBN書籍。ISBNは、可能な組み合わせの数十億をテストする必要のIDを使用して通過するので、この方法は、確かにサイトのコンテンツをつかむための最も効率的な方法ではありません。

あなたが懸念されてきたように、あなたはいくつかの気づいているかもしれませんTOO MANY REQUESTSダウンロードエラーメッセージを。今、私たちは、「アドバンス機能」、1.5.5項では、治療法のこのタイプのより多くのエラーを紹介します、それについて心配しないでください。

1.5.5リンク爬虫類

これまでのところ、我々は、ダウンロードのための2匹の単純な爬虫類、公開されたすべての国家(または地域)のページを達成するためのサンプルWebサイトの構造的特徴を使用していました。これらの2つの方法のページ数が最小限にダウンロードする必要がありますので、限りこの2つの技術が利用できるように、我々は、クロール、それらを使用する必要があります。しかし、他のサイトのために、私たちは、クモが興味のあるコンテンツにアクセスするためのリンクをたどるために、より通常のユーザのように振る舞うようにする必要があります。

道の各リンクを追跡することにより、我々は簡単にサイト全体のページをダウンロードすることができます。しかし、このアプローチは必要ありません多くのページをダウンロードすることができます。例えば、我々は、オンラインフォーラムからユーザーアカウントの詳細ページをつかむしたいので、この時点で、私たちは、ページのディスカッション・スレッドをダウンロードする必要がなく、アカウントページをダウンロードする必要があります。この章では、リンクのクローラーがページをダウンロードするべきかを決定するために、正規表現を使用します使用しています。次は、このコードの最初のリリースです。

import re

def link_crawler(start_url, link_regex):
    """ Crawl from the given start URL following links matched by
link_regex
    """
    crawl_queue = [start_url]
    while crawl_queue:
        url = crawl_queue.pop()
        html = download(url)
        if html is not None:
            continue
        # filter for links matching our regular expression
        for link in get_links(html):
            if re.match(link_regex, link):
                crawl_queue.append(link)

def get_links(html):
    """ Return a list of links from html
    """
    # a regular expression to extract all links from the webpage
    webpage_regex = re.compile("""<a[^>]+href=["'](.*?)["']""",
re.IGNORECASE)
    # list of all links from the webpage
    return webpage_regex.findall(html)

このコードを実行するには、単に呼び出すlink_crawlerWebサイトをクロールするだけでなく、あなたが正規表現にマッチ追跡したいリンクするために使用されるためのURL:2つのパラメータを渡して、機能を。たとえば、ウェブサイト、我々はクロール国家(または地域)リストのインデックスページおよび国(または地域)のページをしたいです。

私たちは、次の形式に従ってリンクサイトのインデックスページを表示することができます:

国(または地域)ページには、次のフォーマットに従います。

したがって、我々は使用することができ/(index|view)/、ページのこれらの2つのタイプに一致するように、この単純な正規表現を。それは実行時にこれらの入力パラメータの爬虫類は何が起こる場合は?下図のようにあなたは、ダウンロードエラーが発生します。

>>> link_crawler('http://example.python-scraping.com', '/(index|view)/')
Downloading: http://example.python-scraping.com
Downloading: /index/1
Traceback (most recent call last):
    ...
ValueError: unknown url type: /index/1

正規表現は、文字列から情報を抽出するための非常に良いツールですので、私はすべてのプログラマがなければならないお勧めします「いくつかの正規表現を読み書きする方法を学びます。」それでも、彼らは失敗に非常にもろいとなりやすい傾向にあります。私たちは、より高度な抽出のリンクを紹介し、本書の以降のページで方法を特定します。

図から分かるように、問題がダウンロードされ/index/1、リンクがプロトコルとサーバーの一部せずに、ウェブ経路の一部に過ぎないとき、これがあると、相対リンクブラウザは、あなたが閲覧しているかのページを知っているし、これらのリンクに対処するために必要な措置をとることができ、その際にブラウザので、相対リンクが正しく動作することができます。しかし、urllibコンテキストなし。するためにurllibページを見つけることができ、我々はへのリンクを変換する必要が絶対リンクのページのポジショニングのすべての詳細を含めるようにフォーム。あなたが望むように、Pythonはあるurllibこれを達成するために使用することができますが、モジュール、モジュール名parseここでlink_crawler使用して改良されたバージョン、urljoin絶対パスを作成する方法は。

from urllib.parse import urljoin

def link_crawler(start_url, link_regex):
    """ Crawl from the given start URL following links matched by
link_regex
    """
    crawl_queue = [start_url]
    while crawl_queue:
        url = crawl_queue.pop()
        html = download(url)
        if not html:
            continue
        for link in get_links(html):
            if re.match(link_regex, link):
                abs_link = urljoin(start_url, link)
                crawl_queue.append(abs_link)

あなたはこのコードを実行すると、ダウンロードページがが一致して表示されますが、同じ場所では常ににダウンロードされ続けます。この動作の考えられる原因は、リンクがこれらの場所のそれぞれの間に存在することです。例えば、南極、南極へのオーストラリアのリンクが戻ってオーストラリアへのリンクがあり、これらの爬虫類は、待ち行列の最後に到達することはありません、URLキューに継続されます。同じリンクをクロールの重複を避けるために、我々は、リンクを介してクロールされたものを記録する必要があります。以下が変更されたlink_crawler機能は、重複ダウンロードを避けるために、URLのストレージ機能を有することが判明しています。

def link_crawler(start_url, link_regex):
    crawl_queue = [start_url]
    # keep track which URL's have seen before
    seen = set(crawl_queue)
    while crawl_queue:
        url = crawl_queue.pop()
        html = download(url)
        if not html:
            continue
        for link in get_links(html):
            # check if link matches expected regex
            if re.match(link_regex, link):
            abs_link = urljoin(start_url, link)
            # check if have already seen this link
            if abs_link not in seen:
                seen.add(abs_link)
                crawl_queue.append(abs_link)

スクリプトを実行すると、それはすべての場所をクロールされ、停止をスケジュールすることができます。最後に、我々は、使用可能なリンクの爬虫類を入手します!

先進的な機能

それでは、他のサイトをクロールするとき、それはより使いやすく、爬虫類をリンクするためにいくつかの機能を追加してみましょう。

1。robots.txtのパース

まず、解析する必要がrobots.txtクロール禁止のダウンロードURLを避けるためにファイルを。使用Pythonのurllibライブラリrobotparserモジュール次のコードに示すように、あなたは簡単に仕事は、成し遂げることができます。

    >>> from urllib import robotparser
>>> rp = robotparser.RobotFileParser()
>>> rp.set_url('http://example.python-scraping.com/robots.txt')
>>> rp.read()
>>> url = 'http://example.python-scraping.com'
>>> user_agent = 'BadCrawler'
>>> rp.can_fetch(user_agent, url)
False
>>> user_agent = 'GoodCrawler'
>>> rp.can_fetch(user_agent, url)
True

robotparser模块首先加载robots.txt文件,然后通过can_fetch()函数确定指定的用户代理是否允许访问网页。在本例中,当用户代理设置为'BadCrawler'时,robotparser模块的返回结果表明无法获取网页,正如我们在示例网站的robots.txt文件中看到的定义一样。

为了将robotparser集成到链接爬虫中,我们首先需要创建一个新函数用于返回robotparser对象。

def get_robots_parser(robots_url):
    " Return the robots parser object using the robots_url "
    rp = robotparser.RobotFileParser()
    rp.set_url(robots_url)
    rp.read()
    return rp

我们需要可靠地设置robots_url,此时我们可以通过向函数传递额外的关键词参数的方法实现这一目标。我们还可以设置一个默认值,防止用户没有传递该变量。假设从网站根目录开始爬取,那么我们可以简单地将robots.txt添加到URL的结尾处。此外,我们还需要定义user_agent

def link_crawler(start_url, link_regex, robots_url=None,
user_agent='wswp'):
    ...
    if not robots_url:
        robots_url = '{}/robots.txt'.format(start_url)
    rp = get_robots_parser(robots_url)

最后,我们在crawl循环中添加解析器检查。

...
while crawl_queue:
    url = crawl_queue.pop()
    # check url passes robots.txt restrictions
    if rp.can_fetch(user_agent, url):
        html = download(url, user_agent=user_agent)
        ...
    else:
        print('Blocked by robots.txt:', url)

我们可以通过使用坏的用户代理字符串来测试我们这个高级链接爬虫以及robotparser的使用。

>>> link_crawler('http://example.python-scraping.com', '/(index|view)/',
user_agent='BadCrawler')
Blocked by robots.txt: http://example.python-scraping.com

2.支持代理

有时我们需要使用代理访问某个网站。比如,Hulu在美国以外的很多国家被屏蔽,YouTube上的一些视频也是。使用urllib支持代理并没有想象中那么容易。我们将在后面的小节介绍一个对用户更友好的Python HTTP模块——requests,该模块同样也能够处理代理。下面是使用urllib支持代理的代码。

proxy = 'http://myproxy.net:1234' # example string
proxy_support = urllib.request.ProxyHandler({'http': proxy})
opener = urllib.request.build_opener(proxy_support)
urllib.request.install_opener(opener)
# now requests via urllib.request will be handled via proxy

下面是集成了该功能的新版本download函数。

def download(url, user_agent='wswp', num_retries=2, charset='utf-8',
proxy=None):
    print('Downloading:', url)
    request = urllib.request.Request(url)
    request.add_header('User-agent', user_agent)
    try:
        if proxy:
            proxy_support = urllib.request.ProxyHandler({'http': proxy})
            opener = urllib.request.build_opener(proxy_support)
            urllib.request.install_opener(opener)
        resp = urllib.request.urlopen(request)
        cs = resp.headers.get_content_charset()
        if not cs:
            cs = charset
        html = resp.read().decode(cs)
    except (URLError, HTTPError, ContentTooShortError) as e:
        print('Download error:', e.reason)
        html = None
        if num_retries > 0:
            if hasattr(e, 'code') and 500 <= e.code < 600:
            # recursively retry 5xx HTTP errors
            return download(url, num_retries - 1)
    return html

目前在默认情况下(Python 3.5),urllib模块不支持https代理。该问题可能会在Python未来的版本中发现变化,因此请查阅最新的文档。此外,你还可以使用文档推荐的诀窍(https://code.activestate.com/recipes/456195),或继续阅读来学习如何使用requests库。

3.下载限速

我々はあまりにも速くサイトをクロールした場合、それは禁止されたり、サーバーの過負荷される危険に直面するだろう。これらのリスクを軽減するために、我々は、爬虫類のように制限速度、2つのダウンロードの間に設定された時間遅延を追加することができます。ここでは、フィーチャクラスのコードがあります。

from urllib.parse import urlparse
import time

class Throttle:
    """Add a delay between downloads to the same domain
    """
    def __init__(self, delay):
    # amount of delay between downloads for each domain
    self.delay = delay
    # timestamp of when a domain was last accessed
    self.domains = {}

def wait(self, url):
    domain = urlparse(url).netloc
    last_accessed = self.domains.get(domain)

    if self.delay > 0 and last_accessed is not None:
        sleep_secs = self.delay - (time.time() - last_accessed)
        if sleep_secs > 0:
            # domain has been accessed recently
            # so need to sleep
            time.sleep(sleep_secs)
    # update the last accessed time
    self.domains[domain] = time.time()

Throttle現在時刻が最終アクセス時間の遅れから、指定された距離よりも小さい場合、クラスは、各ドメイン名の最後のアクセスの時間を記録し、スリープ動作を行います。我々はすべてのダウンロードの前に呼び出すことができthrottle爬虫類の速度制限の。

throttle = Throttle(delay)
...
throttle.wait(url)
html = download(url, user_agent=user_agent, num_retries=num_retries,
                proxy=proxy, charset=charset)

4。爬虫類のトラップを避けてください

現在では、クローラは、以前に訪れていないすべてのリンクを追跡します。しかし、いくつかのサイトは、動的にページコンテンツを生成されますので、ページの無限の数があるでしょう。例えば、ウェブサイトは、オンラインのカレンダー機能は、リンクが来月、来年訪れることができます提供し、その後、メンバーを要求し続けていたページにもリンクが再び訪問することを含んで来月来月は、提供されるあり最大時間を与えられた(それは長い時間が経過した後であってもよいです)。サイトでは、ページの最大数まで、本質的には常に空の検索結果ページへのページング要求へのアクセスを簡単なページナビゲーションで同じ機能を提供することができます。この条件は、呼び出されたクモトラップ

爬虫類の罠に陥るのを回避したい、簡単な方法は、深さであるリンクの数、後に現在のページの到着を記録することです。最大の深さに達したとき、爬虫類は再びキューにそのページへのリンクを追加しないでください。機能の最大の深さを達成するために、我々は変更する必要があるseen変数を。唯一の変数はもともとそれがリンクを発見された深さのレコードを追加し、今辞書に改訂されたリンク、訪問したページを記録しました。

def link_crawler(..., max_depth=4):
    seen = {}
    ...
    if rp.can_fetch(user_agent, url):
        depth = seen.get(url, 0)
        if depth == max_depth:
            print('Skipping %s due to depth' % url)
            continue
        ...
        for link in get_links(html):
            if re.match(link_regex, link):
                abs_link = urljoin(start_url, link)
                if abs_link not in seen:
                    seen[abs_link] = depth + 1
                    crawl_queue.append(abs_link)

後でこの機能を使用すると、我々は最終的に爬虫類を完了することができるように自信を持っています。あなたはこの機能を無効にしたい場合は、単にmax_depth負の数に設定し、現在の深さを持つこの時間は決して同じです。

5。最終版

高度なリンク爬虫類完全なソースコードは、ファイル名、非同期コミュニティにダウンロードすることができますadvanced_link_crawler.py著書に応じた動作を容易にするために、あなたは、コードベースを導出し、独自のコードを比較してテストするためにそれを使用することができます。

要测试该链接爬虫,我们可以将用户代理设置为BadCrawler,也就是本章前文所述的被robots.txt屏蔽了的那个用户代理。从下面的运行结果中可以看出,爬虫确实被屏蔽了,代码启动后马上就会结束。

    >>> start_url = 'http://example.python-scraping.com/index'
>>> link_regex = '/(index|view)'
>>> link_crawler(start_url, link_regex, user_agent='BadCrawler')
Blocked by robots.txt: http://example.python-scraping.com/
```
现在,让我们使用默认的用户代理,并将最大深度设置为`1`,这样只有主页上的链接才会被下载。

```
    >>> link_crawler(start_url, link_regex, max_depth=1)
Downloading: http://example.python-scraping.com//index
Downloading: http://example.python-scraping.com/index/1
Downloading: http://example.python-scraping.com/view/Antigua-and-Barbuda-10
Downloading: http://example.python-scraping.com/view/Antarctica-9
Downloading: http://example.python-scraping.com/view/Anguilla-8
Downloading: http://example.python-scraping.com/view/Angola-7
Downloading: http://example.python-scraping.com/view/Andorra-6
Downloading: http://example.python-scraping.com/view/American-Samoa-5
Downloading: http://example.python-scraping.com/view/Algeria-4
Downloading: http://example.python-scraping.com/view/Albania-3
Downloading: http://example.python-scraping.com/view/Aland-Islands-2
Downloading: http://example.python-scraping.com/view/Afghanistan-1

和预期一样,爬虫在下载完国家(或地区)列表的第一页之后就停止了。

1.5.6 使用requests库

尽管我们只使用urllib就已经实现了一个相对高级的解析器,不过目前Python编写的主流爬虫一般都会使用requests库来管理复杂的HTTP请求。该项目起初只是以“人类可读”的方式协助封装urllib功能的小库,不过现如今已经发展成为拥有数百名贡献者的庞大项目。可用的一些功能包括内置的编码处理、对SSL和安全的重要更新以及对POST请求、JSON、cookie和代理的简单处理。

本书在大部分情况下,都将使用requests库,因为它足够简单并且易于使用,而且它事实上也是大多数网络爬虫项目的标准。

想要安装requests,只需使用pip即可。

pip install requests

如果你想了解其所有功能的进一步介绍,可以阅读它的文档,地址为http://python-requests.org,此外也可以浏览其源代码,地址为https://github.com/kennethreitz/requests

为了对比使用这两种库的区别,我还创建了一个使用requests的高级链接爬虫。你可以在从异步社区中下载的源码文件中找到并查看该代码,其文件名为advanced_link_crawler_using_requests.py。在主要的download函数中,展示了其关键区别。requests版本如下所示。

def download(url, user_agent='wswp', num_retries=2, proxies=None):
    print('Downloading:', url)
    headers = {'User-Agent': user_agent}
    try:
        resp = requests.get(url, headers=headers, proxies=proxies)
        html = resp.text
        if resp.status_code >= 400:
            print('Download error:', resp.text)
            html = None
            if num_retries and 500 <= resp.status_code < 600:
                # recursively retry 5xx HTTP errors
                return download(url, num_retries - 1)
    except requests.exceptions.RequestException as e:
        print('Download error:', e.reason)
        html = None

一个值得注意的区别是,status_code的使用更加方便,因为每个请求中都包含该属性。另外,我们不再需要测试字符编码了,因为Response对象的text属性已经为我们自动化实现了该功能。对于无法处理的URL或超时等罕见情况,都可以使用RequestException进行处理,只需一句简单的捕获异常的语句即可。代理处理也已经被考虑进来了,我们只需传递代理的字典即可(即{'http': 'http://myproxy.net:1234', 'https': 'https://myproxy.net:1234'})。

我们将继续对比和使用这两个库,以便根据你的需求和用例来熟悉它们。无论你是在处理更复杂的网站,还是需要处理重要的人类化方法(如cookie或session)时,我都强烈推荐使用requests。我们将会在第6章中讨论更多有关这些方法的话题。

本文摘自《用Python写网络爬虫》(第2版)

作者:[德]凯瑟琳 雅姆尔(Katharine Jarmul)、[澳]理查德 劳森(Richard Lawson)

译者:李斌

  • 畅销的Python 3网络爬虫
  • 数据抓取采集分析
  • 开发实战图书全新升级版,针对Python 3编写
  • 上一版年度销量近40000册, 提供示例完整源码和实例网站搭建源码

本书是使用Python 3.6的新特性来爬取网络数据的入门指南。本书讲解了从静态网站提取数据的方法,以及如何使用数据库和文件缓存技术来节省时间并管理服务器负载,然后介绍了如何使用浏览器、爬虫和并发爬虫开发一个更为复杂的爬虫。

借助于PyQt和Selenium,你可以决定何时以及如何从依赖JavaScript的网站上爬取数据,以及更好地理解在受CAPTCHA保护的复杂网站上提交表单的方法。本书还讲解了使用Python包(比如mechanize)进行自动化处理的方法、使用Scrapy库创建基于类的爬虫的方法,以及如何在真实的网站上实施所学的爬虫技巧。

本书最后还涵盖了使用爬虫对网站进行测试、远程爬取技术、图像处理以及其他相关的主题。

本书主要内容如下:

  • ページから、簡単なPythonプログラムの抽出データを使用しました。
  • 同時爬虫類、ページの並列処理を構築します。
  • リンクをたどってサイトをクロールします。
  • HTMLから特徴を抽出します。
  • キャッシングは、再利用のために、HTMLをダウンロードしました。
  • 比較の同時実行モデルは、比較的高速爬虫類を決定しました。
  • JavaScriptの構文解析は、サイトによって異なります。
  • フォームやセッションとの対話。

おすすめ

転載: blog.csdn.net/epubit17/article/details/93307564