Learning Scrapy筆記(三)- Scrapy基礎


摘要:本文介紹了Scrapy的基礎爬取流程,也是最重要的部分

 

Scrapy的爬取流程

Scrapy的爬取流程可以概括為一個方程式:UR2IM,其含義如下圖所示

clip_image001

URL:Scrapy的運行就從那個你想要爬取的網站地址開始,當你想要驗證用xpath或其他解析器來解析這個網頁時,可以使用Scrapy shell工具來進行分析,譬如

clip_image003

現在你就可以開始驗證了

 

Request和Response:在上面使用Scrapy shell的過程中可以發現,只要我們輸入了一個URL,它就可以自動發送一個GET請求並獲取返回結果。request是一個把url封裝好的對象,response則是一個把網頁返回結果封裝好的對象,response.body的值是網頁的源代碼,response.url是網頁的url地址,還有更多相關的屬性

 

Items:我們要爬取一個網頁的時候並不是只把源代碼下載下來就完事了,還需要提取網頁中的相關信息,譬如網頁的標題,網頁的發布時間等等內容,而這些內容使用面向對象的技術,封裝成一個Item對象,然后從網頁中提取信息來填充這個Item

 

新建Scrapy工程

首先新建一個名為properties的Scrapy工程

$ scrapy startproject properties
$ cd properties
$ tree

.
├── properties
│   ├── __init__.py
│   ├── items.py
│   ├── pipelines.py
│   ├── settings.py
│   └── spiders
│       └── __init__.py
└── scrapy.cfg
2 directories, 6 files

注意,本系列文章的源代碼都可以從github上下載

 

編寫爬蟲

定義item

編輯items.py文件,在該文件中定義的item並不是一定要在每一個spider中填充,也不是要全部同時使用的,你可以隨意添加字段,並且在任何時候填充。

from scrapy.item import Item, Field

class PropertiesItem(Item):
    # Primary fields
    title = Field()
    price = Field()
    description = Field()
    address = Field()
    image_urls = Field()

    # Calculated fields,這些字段需要運算后才得到,后面的文章會解析,暫時不用管
    images = Field()
    location = Field()

    # Housekeeping fields,這些字段用來在調試時顯示相關信息
    url = Field()
    project = Field()
    spider = Field()
    server = Field()
    date = Field()

 

定義spider

在項目的根目錄下根據basic模板創建一個名為basic的spider,后面的web指的是spider的可運行的域名

scrapy genspider –t basic basic web

當然可以自己手寫一個spider,但是從模板里創建可以省去不少的時間和減少出錯機率,查看其他模板的命令:

scrapy genspider -l

由模板創建的basic.py文件的代碼如下

# -*- coding: utf-8 -*-
import scrapy


class BasicSpider(scrapy.Spider):
    name = "basic"
    allowed_domains = ["web"]
    start_urls = (
        'http://www.web/',
    )

    def parse(self, response):
        pass

把抓取到的網頁存入item中(文件名:basic.py)

import scrapy
from properties.items import PropertiesItem
class BasicSpider(scrapy.Spider):
    name = "basic"
    allowed_domains = ["web"]
    start_urls = (
        'http://web:9312/properties/property_000000.html',
    )
    def parse(self, response):
        item = PropertiesItem()
        item['title'] = response.xpath(
            '//*[@itemprop="name"][1]/text()').extract()
        item['price'] = response.xpath(
            '//*[@itemprop="price"][1]/text()').re('[.0-9]+')
        item['description'] = response.xpath(
            '//*[@itemprop="description"][1]/text()').extract()
        item['address'] = response.xpath(
            '//*[@itemtype="http://schema.org/'
            'Place"][1]/text()').extract()
        item['image_urls'] = response.xpath(
            '//*[@itemprop="image"][1]/@src').extract()
        return item

啟動爬蟲后,看到控制台輸出如下信息,說明爬取成功

$scrapy crawl basic
DEBUG: Scraped from <200 http://...000.html>
  {'address': [u'Angel, London'],
   'description': [u'website ... offered'],
   'image_urls': [u'../images/i01.jpg'],
   'price': [u'334.39'],
   'title': [u'set unique family well']}

將上面的輸出保持到各種文件中:

scrapy crawl basic –o item.json
scrapy crawl basic –o item.jl
#json格式的文件會把整個json對象保存在一個巨大的數組里,意味着如果你要保存的數據量有1GB,那么在你解析這些數據之前,就必須用1GB的內存來保存整個文件。而jl格式會在每一行上放一個json對象,所以讀取起來效率會更高

scrapy crawl basic –o item.xml
scrapy crawl basic –o item.csv
scrapy crawl basic –o ftp://user:pass@ftp.scrapybook.com/items.json 直接保存到ftp上

 

ItemLoader

對於上面混亂且難看的parse函數,可以使用Item Loader來處理,並且Item Loader提供更多的功能(http://doc.scrapy.org/en/latest/topics/loaders.html),最重要的是能夠對通過xpath提取出來的信息進行處理,譬如去掉空格和替換字符等,然后將清洗后的數據再寫入item中。對數據的清洗是通過processor來實現的,很常用的一個processor就是MapCompose()函數,該函數將python函數或者lambda表達式作為參數(參數個數無限制),然后按順序執行這些函數來產生最終的結果。譬如MapCompose(unicode.strip, float)首先將xpath提取的信息去掉空格,再將其轉換為float格式

Image 082

 

basic.py源代碼文件:

https://github.com/Kylinlin/scrapybook/blob/master/ch03%2Fproperties%2Fproperties%2Fspiders%2Fbasic.py

 

修改上面的basic.py文件,使得代碼更加簡潔和一目了然

import datetime
import urlparse
import socket
import scrapy

from scrapy.loader.processors import MapCompose, Join
from scrapy.loader import ItemLoader

from properties.items import PropertiesItem


class BasicSpider(scrapy.Spider):
    name = "basic"
    allowed_domains = ["web"]

    # Start on a property page
    start_urls = (
        'http://web:9312/properties/property_000000.html',
    )

    def parse(self, response):
        """ This function parses a property page.

        @url http://web:9312/properties/property_000000.html
        @returns items 1
        @scrapes title price description address image_urls
        @scrapes url project spider server date
        """

        # Create the loader using the response
        l = ItemLoader(item=PropertiesItem(), response=response)

        # Load fields using XPath expressions
        l.add_xpath('title', '//*[@itemprop="name"][1]/text()',
                    MapCompose(unicode.strip, unicode.title))
        l.add_xpath('price', './/*[@itemprop="price"][1]/text()',
                    MapCompose(lambda i: i.replace(',', ''), float),
                    re='[,.0-9]+')
        l.add_xpath('description', '//*[@itemprop="description"][1]/text()',
                    MapCompose(unicode.strip), Join())
        l.add_xpath('address',
                    '//*[@itemtype="http://schema.org/Place"][1]/text()',
                    MapCompose(unicode.strip))
        l.add_xpath('image_urls', '//*[@itemprop="image"][1]/@src',
                    MapCompose(lambda i: urlparse.urljoin(response.url, i)))

        # Housekeeping fields 可以通過add_value函數直接向item填充數據
        l.add_value('url', response.url)
        l.add_value('project', self.settings.get('BOT_NAME'))
        l.add_value('spider', self.name)
        l.add_value('server', socket.gethostname())
        l.add_value('date', datetime.datetime.now())

        return l.load_item()

注意在上面的parse函數中有這樣的一段注釋

""" This function parses a property page.

        @url http://web:9312/properties/property_000000.html
        @returns items 1
        @scrapes title price description address image_urls
        @scrapes url project spider server date
        """

這些以@開頭的稱為contract,類似於單元測試,假如你在一個月前寫了這個spider,現在想測試這個spider是否仍然正確運行,就可以使用這些contract。上面的這些contract的意思是:檢查該url,你應該得到一個包含了下面列出來的字段的item

運行scrapy check來檢查contract

$ scrapy check basic
----------------------------------------------------------------
Ran 3 contracts in 1.640s
OK

如果spider的代碼出錯了,或者xpath表達式已經過期了(網站發生了更新),那么就會得到測試失敗的結果,雖然出錯信息並不詳盡,但這是最快的檢查手段

 

Spider的運行原理

在上面用模板定義的spider十一個用來爬取特定網頁的類,包括了如何執行爬取動作(譬如如何跟蹤超鏈接)和如何從頁面中提取信息。換句話說,spider就是你用來定義對某個特定網站的爬取動作的工具,他的爬取循環類似於這樣:

1、 首先要將你指定的初始URL封裝成Request對象,並且要指定在網頁返回該請求的內容后應該用哪個函數來處理網頁的內容。

默認情況下,會調用start_requests()函數,對start_urls中的URL分別生成一個Request對象,並且指定parse()函數作為回調函數(回調函數指的是callback變量指定的函數)

2、 在回調函數中,可以處理response變量,然后返回一個已經提取好數據的字典或者是一個Item對象,或者是Request對象(在這個Request對象中,也可以指定一個回調函數,同樣地,處理完這個Request之后生成的response就會傳送到回調函數中處理)

3、 在回調函數中,也可以提取網頁內容,通常使用Selector(也可以使用BeautifulSoup,lxml或者其他你熟悉的機制)來生成包含了解析數據的item

4、 最后,這些從spider中返回的item通常會存入到數據庫中,或者寫入到文件中

即使上述流程適用於大部分的spider,但是仍然有不同的spider運行不同的默認流程,更多的信息就查閱這里:

http://doc.scrapy.org/en/1.0/topics/spiders.html?highlight=start_request#scrapy.spiders.Spider.start_requests


免責聲明!

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



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