利用scrapy和MongoDB來開發一個爬蟲


今天我們利用scrapy框架來抓取Stack Overflow里面最新的問題(問題標題和網址),並且將這些問題保存到MongoDb當中,直接提供給客戶進行查詢。

安裝

在進行今天的任務之前我們需要安裝二個框架,分別是Scrapy (1.1.0)和pymongo (3.2.2).

scrapy

如果你運行的的系統是osx或者linux,可以直接通過pip進行安裝,而windows需要另外安裝一些依賴,因為電腦的原因不對此進行講解。

$ pip install Scrapy

一旦安裝完成之后你可以直接在python shell當中輸入下面的命令,倘若沒有出現錯誤的話,說明已安裝完成

>>> import scrapy
>>>

安裝PyMongo和mongodb

因為系統是osx的,所以直接通過下面的語句就可以安裝。

brew install mongodb

運行mongodb同樣特別的簡單,只需要在終端下面輸入下面的語法:

mongod --dbpath=.

--dbpath是指定數據庫存放的路徑,運行之后會在該路徑下面生成一些文件
屏幕快照 2016-05-22 下午9.45.50

下一步我們就需要安裝PyMongo,同樣采用pip的方式

$ pip install pymongo

Scrapy 項目

我們來創建一個新的scrapy的項目,在終端輸入下面的語法

$ scrapy startproject stack

屏幕快照 2016-05-22 下午9.48.22
一旦上面的命令完成之后,scrapy會直接創建相應的文件,這些文件包含了基本的信息,便於你來修改相應的內容。
屏幕快照 2016-05-22 下午9.48.50

定義數據

items.py文件用於我們定義需要抓取對象的存儲的“容器“
有關StackItem()預定義時並讓其繼承於scrapy.Item

# -*- coding: utf-8 -*-

# Define here the models for your scraped items
#
# See documentation in:
# http://doc.scrapy.org/en/latest/topics/items.html

import scrapy

class StackItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    pass


這里我們需要在里面添加兩個字段,分別用來存放抓取到的標題以及鏈接


from scrapy.item import Item,Field

class StackItem(Item):
    # define the fields for your item here like:
    title=Field()
    url=Field()

創建爬蟲

我們需要在spider文件夾下面創建一個stack_spider.py的文件,這個里面包容我們爬蟲進行抓取時的行為。就是告訴爬蟲我們需要抓取哪些內容以及內容的來源。

from scrapy import Spider
from scrapy.selector import Selector
from stack.items import StackItem

class StackSpider(Spider):
    name="stack"
    allowed_domains=['stackoverflow.com']
    start_urls = [
        "http://stackoverflow.com/questions?pagesize=50&sort=newest",
    ]

  • name 是定義爬蟲的名稱
  • allowed_domains 指定爬蟲進行爬取的域地址
  • start_urls 定義爬蟲需要抓取的網頁的url地址

XPath 選擇

scrapy使用XPath來進行匹配相應的數據的來源,html是一種標記的語法,里面定義了很多的標簽和屬性,比如說我們定義一個下面的這樣的一個標簽,這里我們就可以通過'//div[@class="content"]'來找到這個標記,找到之后我們可以取出其中的屬性或者它的子節點

<div class='content'>

下面我們通過chrome來講解如果找到xpath的路徑 ,在進行操作之前我們需要打開開發者工具,可以點擊菜單欄上面的視圖->開發者->開發者工具來打進入開發者模式,或者可以根據快捷捷來進行打開。
屏幕快照 2016-05-22 下午10.08.15

打開之后我們在需要的內容上面點擊右擊會彈出一個菜單,這里我們可以選擇檢查來找到當前的內容在html相應的位置
屏幕快照 2016-05-22 下午10.05.54
這里chrome會自動幫助我們找到相應的位置,通過下面的分析,我們知道標題的路徑是包含在一個

下面的h3標記當中。
屏幕快照 2016-05-22 下午10.06.02

現在我們來更新相應的stack_spider.py腳本

from scrapy import Spider
from scrapy.selector import Selector
from stack.items import StackItem

class StackSpider(Spider):
    name="stack"
    allowed_domains=['stackoverflow.com']
    start_urls = [
        "http://stackoverflow.com/questions?pagesize=50&sort=newest",
    ]
    def parse(self,response):
        questions=Selector(response).xpath('//div[@class="summary"]/h3')
        

提取數據

創建抓取的規約之后,我們需要與剛才創建的items實體進行關聯,我們繼續修改stack_spider.py文件

from scrapy import Spider
from scrapy.selector import Selector
from stack.items import StackItem

class StackSpider(Spider):
    name="stack"
    allowed_domains=['stackoverflow.com']
    start_urls = [
        "http://stackoverflow.com/questions?pagesize=50&sort=newest",
    ]
    def parse(self,response):
        questions=Selector(response).xpath('//div[@class="summary"]/h3')
        for question in questions:
            item=StackItem()
            item['title'] = question.xpath(
                'a[@class="question-hyperlink"]/text()').extract()[0]
            item['url'] = question.xpath(
                'a[@class="question-hyperlink"]/@href').extract()[0]
            yield item

通過遍歷所有的符合//div[@class="summary"]/h3的元素,並且從中找到我們真正需要爬取的元素內容

測試

現在我們進行測試,只要在項目的目錄下面運行以下的腳本就可以進行測試 。

scrapy crawl stack

現在我們需要將爬取到的所有的信息保存到一個文件當中,可以在后面添加二個參數-o和-t

scrapy crawl stack -o items.json -t json

下面是實際保存的文件的內容分別包含了title和url
屏幕快照 2016-05-22 下午10.24.21

將元素存放入MongoDB

這里我們需要將所有的元素保存到Mongodb collection當中。
在進行操作之前我們需要在setinngs.py指定相應的pipeline和添加一些數據庫的參數

ITEM_PIPELINES = {
   'stack.pipelines.MongoDBPipeline': 300,
}
MONGODB_SERVER = "localhost"
MONGODB_PORT = 27017
MONGODB_DB = "stackoverflow"
MONGODB_COLLECTION = "questions"

pipeline 管理

在之前的步驟里面我們分別已經完成了對html的解析,以及指定數據的存儲。但是這時所有的信息都在內存當中,我們需要將這些爬取到數據存儲到數據庫當中,這里就輪到pipelines.py上場了,這玩意就負責對數據的存儲的。
在上面我們已經定義了數據庫的參數,現在我們終於派上用場了。

import pymongo
from scrapy.conf import settings
from scrapy.exceptions import DropItem
from scrapy import log

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']]

上面的代碼是我們創建了一個MongoDBPipeline()的類,以及定義初始化函數,用來讀取剛才的參數來創建一個Mongo的連接。

數據處理

下一步我們需要定義一個函數來處理解析的數據

# -*- coding: utf-8 -*-

# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: http://doc.scrapy.org/en/latest/topics/item-pipeline.html
import pymongo
from scrapy.conf import settings
from scrapy.exceptions import DropItem
from scrapy import log

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):
        valid=True
        for data in item:
            if not data:
                valid=False
                raise DropItem('Missing{0}!'.format(data))
        if valid:
            self.collection.insert(dict(item))
            log.msg('question added to mongodb database!',
                    level=log.DEBUG,spider=spider)
        return item

上面已經完成了對數據的連接,以及相應數據的存儲

測試

我們同樣在stack目錄當中運行下面的命令

$ scrapy crawl stack

當內容執行完成之后沒有出現任何的錯誤的提示,恭喜你已經將數據正確的存入到mongodb當中。
這里我們通過Robomongo來訪問數據庫的時候發現創建了一個stackoverflow的數據庫,下面已經成功創建了一個名為questions的Collections.並且已經存入了相應的數據了。
屏幕快照 2016-05-22 下午10.37.33


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM