#Python爬蟲#Item Pipeline介紹(附爬取網(wǎng)站獲取圖片到本地代碼)

1 Item Pipeline

當(dāng)spider爬取到item后扶关,它被發(fā)送到項目管道(Item Pipeline),通過幾個組件按順序進行處理残吩。每一個Item Pipeline是一個實現(xiàn)了簡單方法的Python類生棍,它接收到一個item并對其執(zhí)行一個操作,也要決定該item是否應(yīng)該繼續(xù)通過管道猪钮,或者被丟棄,不再進行處理胆建。
Item Pipeline典型的用途是:
1.清理HTML數(shù)據(jù)
2.驗證爬取的數(shù)據(jù)(檢查items是否包含某些字段)
3.檢查副本(并刪除它們)
4.將item數(shù)據(jù)存儲在數(shù)據(jù)庫中

1.1 編寫自己的Item Pipeline

每個Item Pipeline都是一個Python類烤低,它必須實現(xiàn)以下方法:
process_item(self, item, spider)
這個方法可以被每個Item Pipeline調(diào)用,process_item()必須是:返回一個字典類型數(shù)據(jù)笆载、返回一個條目(或任何子類)對象扑馁,返回一個 Twisted Deferred 或者DropItem異常涯呻,丟棄的item不再由進一步的Item Pipeline處理。
參數(shù)含義:
item: Item對象或字典腻要,爬取的item
spider:spider對象复罐,爬取了這個item的spider
此外,他們還可以實現(xiàn)以下方法:
open_spider(self, spider)
當(dāng)spider打開時雄家,函數(shù)就會被調(diào)用效诅,spider參數(shù)含義:被打開的spider
close_spider(self, spider)
當(dāng)spider關(guān)閉是,函數(shù)會被調(diào)用
from_crawler(cls, crawler)
如果存在趟济,這個類方法被調(diào)用來從一個Crawler創(chuàng)建一個spider實例乱投。它必須返回管道的一個新實例,Crawler對象提供對所有的scrapy核心組件的訪問顷编,比如設(shè)置和信號;這是管道訪問它們并將其功能連接到scrapy的一種方式戚炫。

1.2 Pipeline示例

1.2.1 價格驗證示例

from scrapy.exceptions import DropItem

class PricePipeline(object):

    vat_factor = 1.15

    def process_item(self, item, spider):
        if item['price']:
            if item['price_excludes_vat']:
                item['price'] = item['price'] * self.vat_factor
            return item
        else:
            raise DropItem("Missing price in %s" % item)

1.2.2 寫入json文件

下面的Pipeline將所有經(jīng)過的項目(從所有的spiders)存儲到一個item.jl文件中,其中每行以JSON格式序列化:

import json

class JsonWriterPipeline(object):

    def open_spider(self, spider):
        self.file = open('items.jl', 'w')

    def close_spider(self, spider):
        self.file.close()

    def process_item(self, item, spider):
        line = json.dumps(dict(item)) + "\n"
        self.file.write(line)
        return item

1.2.3 寫入MongoDB

在本例中媳纬,我們將使用pymongo將items寫入MongoDB双肤。MongoDB地址和數(shù)據(jù)庫名稱在scrapy settings中指定;MongoDB集合以item類命名。本例的主要目的是展示如何使用from_crawler()方法以及如何正確地清理資源层宫。

import pymongo

class MongoPipeline(object):

    collection_name = 'scrapy_items'

    def __init__(self, mongo_uri, mongo_db):
        self.mongo_uri = mongo_uri
        self.mongo_db = mongo_db

    @classmethod
    def from_crawler(cls, crawler):
        return cls(
            mongo_uri=crawler.settings.get('MONGO_URI'),
            mongo_db=crawler.settings.get('MONGO_DATABASE', 'items')
        )

    def open_spider(self, spider):
        self.client = pymongo.MongoClient(self.mongo_uri)
        self.db = self.client[self.mongo_db]

    def close_spider(self, spider):
        self.client.close()

    def process_item(self, item, spider):
        self.db[self.collection_name].insert_one(dict(item))
        return item

1.2.4 重復(fù)過濾器

用于查找重復(fù)items的篩選器杨伙,并刪除已處理的item,假設(shè)我們的items有一個惟一的id,但是我們的spider返回的是具有相同id的多個items:

from scrapy.exceptions import DropItem

class DuplicatesPipeline(object):

    def __init__(self):
        self.ids_seen = set()

    def process_item(self, item, spider):
        if item['id'] in self.ids_seen:
            raise DropItem("Duplicate item found: %s" % item)
        else:
            self.ids_seen.add(item['id'])
            return item

1.2.5 激活I(lǐng)tem Pipeline

激活I(lǐng)tem Pipeline 必須要在setting中設(shè)置ITEM_PIPELINES萌腿,示例如下:

ITEM_PIPELINES = {
    'myproject.pipelines.PricePipeline': 300,
    'myproject.pipelines.JsonWriterPipeline': 800,
}

在這個設(shè)置中分配給類的整數(shù)值決定了它們運行的順序:item從低到高執(zhí)行限匣,整數(shù)值范圍是0-1000。

2 Feed exports

執(zhí)行scrapy時最常需要的特性之一就是能夠正確地存儲爬取出來的數(shù)據(jù)毁菱,scrapy提供了這個功能米死,允許使用多種序列化格式來生成一個Feed。

2.1 序列化格式

用于序列化scrapy的數(shù)據(jù)格式主要有以下幾種類型:

  • JSON
  • JSON lines
  • CSV
  • XML

你也可以通過setting中的FEED_EXPORTERS字段來擴展支持的格式贮庞。
JSON
FEED_FORMAT: json
使用的類: JsonItemExporter
JSON lines
FEED_FORMAT: jsonlines
使用的類: JsonLinesItemExporter
CSV
FEED_FORMAT: csv
使用的類: CsvItemExporter
XML
FEED_FORMAT: xml
使用的類: XmlItemExporter
Pickle
FEED_FORMAT: pickle
使用的類: PickleItemExporter
Marshal
FEED_FORMAT: marshal
使用的類: MarshalItemExporter

2.2 使用方法

進入項目目錄峦筒,執(zhí)行命令:

scrapy crawl tushu -o tushu.json

通過-o參數(shù)后面接要輸出的格式即可。

3 下載和處理文件和圖像

scrapy提供了可重用的 item pipelines窗慎,用于下載與特定item 相關(guān)的文件(例如物喷,當(dāng)你爬取了產(chǎn)品并想要在本地下載它們的圖像時),這些pipelines共享一些功能和結(jié)構(gòu)(我們將它們稱為media pipelines)遮斥,但是通常要么使用Files Pipeline 要么使用 Images Pipeline峦失。
這兩個Pipeline都實現(xiàn)了這些特性:

  • 避免重新下載最近下載的媒體
  • 指定存儲介質(zhì)的位置(文件系統(tǒng)目錄等)

Image Pipeline有一些額外的功能用于處理圖像:

  • 將所有下載的圖像轉(zhuǎn)換為通用格式(JPG)和模式(RGB)
  • 生成縮略圖
  • 檢查圖像寬度/高度以確保它們滿足最小約束條件

Pipeline為正準(zhǔn)備下載的media url的保留了內(nèi)部隊列,將包含相同媒體的response連接到該隊列术吗,這樣可以避免在多個items共享的情況下下載相同的媒體尉辑。

3.1 使用Files Pipeline

使用Files Pipeline典型的工作流程如下:
1.在一個spider中,你將一個item提取并且將所需的urls放入file_urls字段中较屿;
2.item將從spider返回并進入item pipeline隧魄;
3.當(dāng)item到達(dá)FilePipeline卓练,在file_urls字段中的urls會使用標(biāo)準(zhǔn)scrapy調(diào)度器和下載器下載(這意味著調(diào)度程序和下裝程序中間件被重用),如果優(yōu)先級更高购啄,會在其他頁面被抓取前處理襟企。item會在這個特定的pipline中保持“l(fā)ocker”狀態(tài),知道完成下載(或由于某些原因未完成下載)闸溃。
4.當(dāng)下載文件時整吆,將使用結(jié)果填充另一個字段(files),這個字段將包含一個關(guān)于下載文件的信息的字典辉川,例如下載路徑、原始url(來自file_urls字段)和文件校驗拴测。文件字段列表中的files將保留原來的file_urls字段的相同順序乓旗,如果有下載失敗的文件,錯誤將會被記錄集索,而file不會被記錄到files字段中屿愚。

3.2 使用Images Pipeline

Images Pipeline的使用方式與File Pipeline方式類似,只是默認(rèn)的字段名稱不同务荆,使用image_urls作為一個item的圖片urls妆距,它將填充一個圖像image字段,以獲取關(guān)于下載的圖像的信息函匕。
使用ImagesPipeline對于處理image files的優(yōu)點是娱据,您可以配置一些額外的功能,比如生成縮略圖和根據(jù)它們的大小過濾圖像盅惜。
Images Pipeline程序使用Pillow模塊格式化圖片為JPEG/RGB格式中剩,所以你還需要安裝Pillow模塊,大多數(shù)情況下我們使用PIL抒寂,但眾所周知结啼,在某些情況下會引起麻煩,所以我們建議用Pillow屈芜。

3.3 使用Media Pipeline

如果要使用Media Pipeline你必須要在項目的setting中增加ITEM_PIPELINES設(shè)置郊愧,對于Images Pipeline,使用:

ITEM_PIPELINES = {'scrapy.pipelines.images.ImagesPipeline': 1}

Files Pipeline井佑,使用:

ITEM_PIPELINES = {'scrapy.pipelines.files.FilesPipeline': 1}

注意:Images Pipeline和Files Pipeline可以同時使用属铁。

然后,將目標(biāo)存儲設(shè)置配置為一個有效值毅糟,該值將用于存儲下載的圖像红选。否則即使你配置了ITEM_PIPELINES,也是被禁用的。
如果是File Pipeline姆另,在setting中增加FILES_STORE:

FILES_STORE = '/path/to/valid/dir'

如果是Image Pipeline喇肋,在setting中增加IMAGES_STORE:

IMAGES_STORE = '/path/to/valid/dir'

3.4 支持的存儲

目前官方唯一支持的是文件系統(tǒng)坟乾,但是也支持類似的Amazon S3 and和 Google Cloud Storage.

3.5 示例

1.首先使用media pipline首先要啟用它,在setting中配置:

ITEM_PIPELINES = {'scrapy.pipelines.images.ImagesPipeline': 1}

2.然后設(shè)置字段images和image_urls:

import scrapy

class MyItem(scrapy.Item):

    # ... other item fields ...
    image_urls = scrapy.Field()
    images = scrapy.Field()

3.在setting中添加下載路徑和字段:

# 圖片下載存儲路徑
ITEM_STORE = 'E:\\'

為了避免下載最近下載的文件蝶防,可以設(shè)置FILES_EXPIRES或IMAGES_EXPIRES來配置緩存時間:

# 120天后過期
FILES_EXPIRES = 120

# 30天后過期
IMAGES_EXPIRES = 30

Images Pipline可以自動創(chuàng)建下載圖像的縮略圖甚侣,在setting中增加IMAGES_THUMBS參數(shù),參數(shù)為一個字典,其中的鍵是縮略圖名稱间学,而值是它們的維數(shù):

IMAGES_THUMBS = {
    'small': (50, 50),
    'big': (270, 270),
}

如果想過濾掉小圖片殷费,通過設(shè)置IMAGES_MIN_HEIGHT和 IMAGES_MIN_WIDTH來指定圖像大小:

IMAGES_MIN_HEIGHT = 110
IMAGES_MIN_WIDTH = 110

這個值的配置不會影響縮略圖的生成低葫。
通過上面的配置我們就可以為我們的爬蟲添加下載圖片功能了详羡。

4 小爬蟲

上面說了那么多,大家可能覺得已經(jīng)一頭霧水了嘿悬,接下來我們就用一個小項目來具體說明一下实柠,我們要爬取的網(wǎng)站是(搜房網(wǎng)二手房頁面中的各個房源圖片)如下圖:



獲取網(wǎng)頁列表中,每個條目的詳細(xì)頁中的圖片善涨。

4.1 啟用pipeline

setting.py中增加如下內(nèi)容:

# 新增內(nèi)容從這里開始#################################################################
# 啟動pipline
ITEM_PIPELINES = {
    # 注意窒盐,如果自定義圖片名稱時,此條內(nèi)容要注釋钢拧,不然自定義圖片名不生效
    'scrapy.pipelines.images.ImagesPipeline': 1,
        # 自定義圖片名稱后蟹漓,可以取消注釋此條
    # 'sp.pipelines.SpDownimagePipeline': 200,
}
# 圖片保存地址
IMAGES_STORE = 'E:\\'
# 圖片過期時間30天
IMAGES_EXPIRES = 30
# 設(shè)置縮略圖
# IMAGES_THUMBS = {
#     'small': (50, 50),
#     'big': (270, 270),
# }
# 過濾小圖片
# IMAGES_MIN_HEIGHT = 110
# IMAGES_MIN_WIDTH = 110
# 允許重定向
MEDIA_ALLOW_REDIRECTS = True
# 控制時間,下載等待時間3秒
DOWNLOAD_DELAY = 3
# 請求user-agent
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:57.0) Gecko/20100101 Firefox/57.0'
# 新增內(nèi)容從這里結(jié)束#################################################################

4.2 配置items

設(shè)置要爬取的網(wǎng)頁名字段image和爬取網(wǎng)頁內(nèi)的圖片鏈接字段image_urls源内,items.py代碼如下:

# -*- 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 SpItem(scrapy.Item):
    """
    定義item字段
    """
    # 網(wǎng)頁名稱
    image = scrapy.Field()
    # 網(wǎng)頁內(nèi)圖片鏈接
    image_urls = scrapy.Field()

4.3 spider

我們的爬蟲ftx.py代碼如下:

# -*- coding: utf-8 -*-
import scrapy
from sp.items import SpItem

class MyBlog(scrapy.Spider):
    name = 'ftx'
    start_urls = ['http://esf.fang.com']

    def parse(self, response):
        """
        爬取初始start_urls列表項目的url(相對路徑)葡粒,通過response.follow生成
        request,作為參數(shù)傳入回掉函數(shù)parse_item
        """
        # 獲取首頁所有二手房的鏈接地址(相對地址)
        page_urls = response.css("p.title a::attr(href)").extract()
        for page_url in page_urls:
            # 相對地址使用response.follow方式拼接url
            request = response.follow(page_url, callback=self.parse_item)
            # 如果獲取的連接是絕對地址姿锭,用下面這條方法
            # request = scrapy.Request(page_url, callback=self.parse_item)
            yield request

    def parse_item(self, response):
        """
        處理item函數(shù)
        :param response: 請求的網(wǎng)頁內(nèi)容
        :return: item
        """
        # 導(dǎo)入item類
        item = SpItem()
        # 每個個人頁面中的圖片
        # image是每個詳細(xì)頁的標(biāo)題塔鳍, image_urls是每個詳細(xì)頁內(nèi)圖片的url
        item['image'] = response.css("div.floatl::text").extract_first().strip()
        item['image_urls'] = response.css("img.loadimg::attr(data-src)").extract()
        yield item

4.4 自定義image pipeline

直接上代碼pipelines.py:

# -*- 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 scrapy
from scrapy.pipelines.images import ImagesPipeline

class SpDownimagePipeline(ImagesPipeline):
    """
    自定義圖片下載類
    """
    def get_media_requests(self, item, info):
        """
        ImagesPipeline類的方法,必須返回每個圖像URL的Request
        :param item:獲取的item
        """
        # 從item中獲取圖片url并發(fā)送請求,image_urls就是items.py中定義的字段
        for image_url in item['image_urls']:
            # meta作用就是可以將item的值傳給下一個函數(shù)使用呻此,類似于先緩存起來
            yield scrapy.Request(image_url, meta={'item': item})

    def item_completed(self, results, item, info):
        """
        此處沒有做修改轮纫,只是把ImagesPipeline的方法拿過來用,必須返回item
        """
        if isinstance(item, dict) or self.images_result_field in item.fields:
            item[self.images_result_field] = [x for ok, x in results if ok]
        return item

    def file_path(self, request, response=None, info=None):
        """
        file_path為ImagePipeline自帶的方法焚鲜,這里我們重寫這個方法掌唾,
        為了能夠自定義圖片的名稱,如果不重寫忿磅,SHA1 hash格式糯彬,類似full/63bbfea82b8880ed33cdb762aa11fab722a90a24.jpg
        """
        # 獲取item,從get_media_requests的Request中獲取
        item = request.meta['item']
        # 圖片名稱葱她,一版用split(‘/’)分割后取最后一個值也就是-1撩扒,這里沒用-1是因為圖片最后一個字段不是隨機數(shù)
        # 是長乘以寬如:452x340c.jpg,容易重名吨些,所以用的-2搓谆,倒數(shù)第二個字段
        image_guid = request.url.split('/')[-2] + '.jpg'
        # 全名炒辉,包括路徑和圖片名
        fullname = "full/%s/%s" % (item['image'], image_guid)
        return fullname

對于ImagesPipeline的兩個方法get_media_requests和item_completed這里解釋一下:
get_media_requests(item, info)
pipeline會獲取image的urls從item下載它,因此我們可以重寫get_media_requests方法并且返回每一個url的request:

def get_media_requests(self, item, info):
    for file_url in item['file_urls']:
        yield scrapy.Request(file_url)

這些請求將由pipeline處理,當(dāng)完成下載時結(jié)果將會以2-元素的元組形式被發(fā)送到item_completed方法泉手,每個元組將包含(success黔寇, file_info_or_error)類似下面這種形式:

[(True,
  {'checksum': '2b00042f7481c7b056c4b410d28f33cf',
   'path': 'full/0a79c461a4062ac383dc4fade7bc09f1384a3910.jpg',
   'url': 'http://www.example.com/files/product1.pdf'}),
 (False,
  Failure(...))]

success:布爾值,如果下載圖片成功斩萌,返回True,如果下載圖片失敗缝裤,返回False。file_info_or_error:返回的是一個字典颊郎,其中包括憋飞,url、path和checksum,如果出現(xiàn)問題返回Twisted Failure袭艺。

  • url代表文件從哪里下載的搀崭,這是從get_media_requests返回的request的url
  • path代表文件存儲路徑
  • checksum代表圖像內(nèi)容的MD5 hash

item_completed(results, item, info)
當(dāng)一個單獨項目中所有圖片請求完成時(下載完成或者下載失敗)猾编,此方法將會被調(diào)用,其中results參數(shù)為get_media_requests下載完成后返回的結(jié)果升敲,item_completed必須返回輸出發(fā)送到下一個階段的pipeline答倡。所以你必須返回或刪除item,和之前其它pipeline操作一樣驴党。
下面的一個示例瘪撇,我們將下載的文件路徑(在results中傳遞)存儲在file_path item字段中,如果不包含任何文件港庄,則刪除該項目倔既。

from scrapy.exceptions import DropItem

def item_completed(self, results, item, info):
    file_paths = [x['path'] for ok, x in results if ok]
    if not file_paths:
        raise DropItem("Item contains no files")
    item['file_paths'] = file_paths
    return item

下面是一個完整的自定義Image pipeline示例:

import scrapy
from scrapy.pipelines.images import ImagesPipeline
from scrapy.exceptions import DropItem

class MyImagesPipeline(ImagesPipeline):

    def get_media_requests(self, item, info):
        for image_url in item['image_urls']:
            yield scrapy.Request(image_url)

    def item_completed(self, results, item, info):
        image_paths = [x['path'] for ok, x in results if ok]
        if not image_paths:
            raise DropItem("Item contains no images")
        item['image_paths'] = image_paths
        return item
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市鹏氧,隨后出現(xiàn)的幾起案子渤涌,更是在濱河造成了極大的恐慌,老刑警劉巖把还,帶你破解...
    沈念sama閱讀 212,294評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件实蓬,死亡現(xiàn)場離奇詭異,居然都是意外死亡吊履,警方通過查閱死者的電腦和手機安皱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,493評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來艇炎,“玉大人酌伊,你說我怎么就攤上這事∽鹤伲” “怎么了居砖?”我有些...
    開封第一講書人閱讀 157,790評論 0 348
  • 文/不壞的土叔 我叫張陵虹脯,是天一觀的道長。 經(jīng)常有香客問我悯蝉,道長归形,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,595評論 1 284
  • 正文 為了忘掉前任鼻由,我火速辦了婚禮暇榴,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蕉世。我一直安慰自己蔼紧,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,718評論 6 386
  • 文/花漫 我一把揭開白布狠轻。 她就那樣靜靜地躺著奸例,像睡著了一般。 火紅的嫁衣襯著肌膚如雪向楼。 梳的紋絲不亂的頭發(fā)上查吊,一...
    開封第一講書人閱讀 49,906評論 1 290
  • 那天,我揣著相機與錄音湖蜕,去河邊找鬼逻卖。 笑死,一個胖子當(dāng)著我的面吹牛昭抒,可吹牛的內(nèi)容都是我干的评也。 我是一名探鬼主播,決...
    沈念sama閱讀 39,053評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼灭返,長吁一口氣:“原來是場噩夢啊……” “哼盗迟!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起熙含,我...
    開封第一講書人閱讀 37,797評論 0 268
  • 序言:老撾萬榮一對情侶失蹤罚缕,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后婆芦,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體怕磨,經(jīng)...
    沈念sama閱讀 44,250評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,570評論 2 327
  • 正文 我和宋清朗相戀三年消约,在試婚紗的時候發(fā)現(xiàn)自己被綠了肠鲫。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,711評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡或粮,死狀恐怖导饲,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤渣锦,帶...
    沈念sama閱讀 34,388評論 4 332
  • 正文 年R本政府宣布硝岗,位于F島的核電站,受9級特大地震影響袋毙,放射性物質(zhì)發(fā)生泄漏型檀。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,018評論 3 316
  • 文/蒙蒙 一听盖、第九天 我趴在偏房一處隱蔽的房頂上張望胀溺。 院中可真熱鬧,春花似錦皆看、人聲如沸仓坞。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,796評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽无埃。三九已至,卻和暖如春毛雇,著一層夾襖步出監(jiān)牢的瞬間嫉称,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,023評論 1 266
  • 我被黑心中介騙來泰國打工灵疮, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留澎埠,地道東北人。 一個月前我還...
    沈念sama閱讀 46,461評論 2 360
  • 正文 我出身青樓始藕,卻偏偏與公主長得像,于是被迫代替她去往敵國和親氮趋。 傳聞我的和親對象是個殘疾皇子伍派,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,595評論 2 350

推薦閱讀更多精彩內(nèi)容