前回、StackOverflowから最新の質問をダウンロードし、結果をMongoDBに保存する基本的なWebスクレイパーを実装しました。 この記事では、スクレーパーを拡張して、各ページの下部にあるページネーションリンクをクロールし、各ページから質問(質問のタイトルとURL)を取得します。
無料ボーナス: ここをクリックして、PythonからMongoDBにアクセスする方法を示す完全なソースコードを含むPython+MongoDBプロジェクトスケルトンをダウンロードしてください。
更新:
- 2015年9月6日-Scrapy(v1.0.3)とPyMongo(v3.0.3)の最新バージョンに更新-乾杯!
スクレイピングジョブを開始する前に、サイトの利用規約を確認し、robots.txtファイルを尊重してください。また、短期間に多数の要求でサイトを氾濫させないことにより、倫理的なスクレイピング慣行を順守してください。 スクレイプしたサイトを自分のものであるかのように扱います。
これは、Real Pythonの人々とGyörgy(Python愛好家であり、ソフトウェア開発者であり、現在ビッグデータ会社で働いていると同時に新しい仕事を探している)のコラボレーション作品です。 Twitterで彼に質問することができます-@kissgyorgy。
はじめに
中断したところから続行するには、2つの方法があります。
1つ目は、parse_item
の応答から次のページのリンクをすべて抽出して、既存のSpiderを拡張することです。 xpath式とyield
を使用したメソッド Request
同じparse_item
へのコールバックを持つオブジェクト 方法。このようにして、scrapyは指定したリンクに対して自動的に新しいリクエストを行います。この方法の詳細については、Scrapyのドキュメントをご覧ください。
もう1つのはるかに簡単なオプションは、別の種類のスパイダー(CrawlSpider
)を利用することです。 (リンク)。これは、基本的なSpider
の拡張バージョンです。 、私たちのユースケースにぴったり合うように設計されています。
クロールスパイダー
前回のチュートリアルと同じScrapyプロジェクトを使用するので、必要に応じてリポジトリからコードを取得します。
ボイラープレートを作成する
「スタック」ディレクトリ内で、crawl
からスパイダーボイラープレートを生成することから始めます。 テンプレート:
$ scrapy genspider stack_crawler stackoverflow.com -t crawl
Created spider 'stack_crawler' using template 'crawl' in module:
stack.spiders.stack_crawler
Scrapyプロジェクトは次のようになります:
├── scrapy.cfg
└── stack
├── __init__.py
├── items.py
├── pipelines.py
├── settings.py
└── spiders
├── __init__.py
├── stack_crawler.py
└── stack_spider.py
そして、 stack_crawler.py ファイルは次のようになります:
# -*- coding: utf-8 -*-
import scrapy
from scrapy.contrib.linkextractors import LinkExtractor
from scrapy.contrib.spiders import CrawlSpider, Rule
from stack.items import StackItem
class StackCrawlerSpider(CrawlSpider):
name = 'stack_crawler'
allowed_domains = ['stackoverflow.com']
start_urls = ['http://www.stackoverflow.com/']
rules = (
Rule(LinkExtractor(allow=r'Items/'), callback='parse_item', follow=True),
)
def parse_item(self, response):
i = StackItem()
#i['domain_id'] = response.xpath('//input[@id="sid"]/@value').extract()
#i['name'] = response.xpath('//div[@id="name"]').extract()
#i['description'] = response.xpath('//div[@id="description"]').extract()
return i
この定型文にいくつかの更新を加える必要があります…
start_urls
を更新します リスト
まず、質問の最初のページをstart_urls
に追加します リスト:
start_urls = [
'http://stackoverflow.com/questions?pagesize=50&sort=newest'
]
rules
を更新します リスト
次に、rules
に正規表現を追加して、次のページのリンクがどこにあるかをスパイダーに伝える必要があります。 属性:
rules = [
Rule(LinkExtractor(allow=r'questions\?page=[0-9]&sort=newest'),
callback='parse_item', follow=True)
]
Scrapyは、これらのリンクに基づいて新しいページを自動的にリクエストし、その応答をparse_item
に渡します。 質問とタイトルを抽出する方法。
細心の注意を払っている場合、この正規表現ではクロールが最初の9ページに制限されます。これは、このデモではすべてを削りたくないためです。 176,234ページ!
parse_item
を更新します メソッド
ここで、xpathを使用してページを解析する方法を記述する必要があります。これは、前回のチュートリアルですでに行ったため、コピーするだけです。
def parse_item(self, response):
questions = response.xpath('//div[@class="summary"]/h3')
for question in questions:
item = StackItem()
item['url'] = question.xpath(
'a[@class="question-hyperlink"]/@href').extract()[0]
item['title'] = question.xpath(
'a[@class="question-hyperlink"]/text()').extract()[0]
yield item
蜘蛛は以上ですが、しないでください まだ始めてください。
ダウンロード遅延を追加
settings.py でダウンロードの遅延を設定することで、StackOverflow(さらに言えば任意のサイト)に対応する必要があります。 :
DOWNLOAD_DELAY = 5
これにより、Scrapyは、新しいリクエストを行うたびに少なくとも5秒待機するようになります。あなたは本質的に自分自身をレート制限しています。これを行わないと、StackOverflowによってレートが制限されます。また、レート制限を課さずにサイトをスクレイプし続けると、IPアドレスが禁止される可能性があります。ですから、よろしくお願いします-スクレイプしたサイトはすべて自分のものであるかのように扱ってください。
今やるべきことは1つだけです。それは、データを保存することです。
MongoDB
前回は50の質問しかダウンロードしませんでしたが、今回はより多くのデータを取得するため、データベースに重複する質問を追加しないようにします。これは、MongoDBアップサートを使用して行うことができます。つまり、質問のタイトルが既にデータベースにある場合は更新し、そうでない場合は挿入します。
MongoDBPipeline
を変更します 以前に定義しました:
class MongoDBPipeline(object):
def __init__(self):
connection = pymongo.MongoClient(
settings['MONGODB_SERVER'],
settings['MONGODB_PORT']
)
db = connection[settings['MONGODB_DB']]
self.collection = db[settings['MONGODB_COLLECTION']]
def process_item(self, item, spider):
for data in item:
if not data:
raise DropItem("Missing data!")
self.collection.update({'url': item['url']}, dict(item), upsert=True)
log.msg("Question added to MongoDB database!",
level=log.DEBUG, spider=spider)
return item
簡単にするために、これは本番環境ではないため、クエリを最適化せず、インデックスを処理しませんでした。
テスト
スパイダーを始めよう!
$ scrapy crawl stack_crawler
さあ、座ってデータベースがデータでいっぱいになるのを見てください!
$ mongo
MongoDB shell version: 3.0.4
> use stackoverflow
switched to db stackoverflow
> db.questions.count()
447
>
結論
ソースコード全体をGithubリポジトリからダウンロードできます。以下に質問をコメントしてください。乾杯!
無料ボーナス: ここをクリックして、PythonからMongoDBにアクセスする方法を示す完全なソースコードを含むPython+MongoDBプロジェクトスケルトンをダウンロードしてください。
より多くのウェブスクレイピングをお探しですか?必ずRealPythonコースをチェックしてください。プロのウェブスクレイパーを雇おうとお考えですか? GoScrapeをチェックしてください。