爬虫之scrapy

一、项目简单流程

1、创建项目

scrapy startproject 项目名

2、创建Spider

cd 项目名

scrapy genspider 爬虫名 域名

class YokaSpider(scrapy.Spider):
    name = 'yoka'
    allowed_domains = ['www.yoka.com/fashion/']
    start_urls = ['http://www.yoka.com/fashion/']
    
    def parse(self,response):
        pass

创建的Spider类需继承scrapy.Spider

name:爬虫名

allowed_domains:允许爬取的域名,不在域名下的请求链接会被过滤掉

start_urls:Spider启动时爬取的url列表,初始请求由它来定义

parse:默认情况下, start_urls里的链接请求完成下载后,返回的响应就会作为唯一的参数传递给这个函数。该方法负责解析返回的响应、提取数据、进一步生成要处理的请求

3、创建Item

Item是保存爬取数据的容器,使用方法和字典类似,不过多了额外的保护机制,可以避免拼写错误和定义字段错误

创建的Item类需继承scrapy.Item,并定义类型为scrapy.Field的字段

class YokadapeiItem(scrapy.Item):
    text= scrapy.Field()
    tags=scrapy.Field()
4、解析Response
5、使用Item

①实例化 item=YokadapeiItem()

②赋值item['text']=...  item['tags']=...

③yield item

6、后续Request

使用scrapy.Request方法

yield scrapy.Request(url=请求连接,callback=回调函数)

7、运行

scrapy crawl 爬虫名

8、保存到文件

scrapy crawl 爬虫名 -o 文件名.后缀名(json、csv、xml等)

9、使用Item Pipeline

Item生成后,会自动被送到Item Pipeline进行处理

Item Pipeline的作用

①清理HTML数据

②验证爬取数据,检查爬取字段

③查重并丢弃重复内容

④将爬取结果保存到数据库

实现Item Pipeline

①定义一个类并实现process_item()方法 ---必须返回包含数据的字典或Item对象,或抛出DropItem异常

该方法有两个参数,第一个是item,每次Spider生成的Item都会作为参数传过来,第二个是spider,就是Spider的实例

接下来,我们是实现一个Item Pipeline筛调长度大于25的title

from scrapy.exceptions import DropItem

class TestSPipeline(object):
    def __init__(self):
        self.limit=25
    def process_item(self, item, spider):
        #判断该字段是否存在
        if item['title']:
            if len(item['title']) > self.limit:
                item['title']=item['title'][0:self.limit].rstrip() + '...'
            return item
        else:
            return DropItem("Missing title")

 ②将处理后的item存入MySQL

创建MysqlPipeline类

import pymsql

class MysqlPipeline(object):
    def __init__(self,mysql_host,mysql_port,mysql_db,mysql_user,mysql_password,mysql_table):
        self.mysql_host=mysql_host
        self.mysql_port=mysql_port
        self.mysql_db=mysql_db
        self.mysql_table=mysql_table
        self.mysql_user=mysql_user
        self.mysql_password=mysql_password

    #类方法,用classmethod标识,通过参数crawler拿到全局配置的每个配置信息
    #再settings中我们可以定义MYSQL_HOST和MYSQL_PORT等来指定MySQL连接需要的地址和端口等信息
    @classmethod
    def from_crawler(cls,crawler):
        return cls(
            mysql_host=crawler.settings.get('MYSQL_HOST'),
            mysql_port=crawler.settings.get('PORT'),
            mysql_db=crawler.settings.get('MYSQL_DB'),
            mysql_user=crawler.settings.get('MYSQL_USER'),
            mysql_password=crawler.settings.get('MYSQL_PASSWORD'),
            mysql_table=crawler.settings.get('MYSQL_TABLE'),
        )

    def open_spider(self,spider):
        self.conn = pymysql.connect(host=self.mysql_host,port=self.mysql_port,
                                    user=self.mysql_user,password=self.mysql_password,
                                    database=self.mysql_db,charset='utf8')
        self.cur=self.conn.cursor()

    def process_item(self,item,spider):
        dc=dict(item)
        for v in dc.values():
            sql="insert into %s (title) values ('%s')"%(self.mysql_table,v)
            self.cur.execute(sql)
            self.conn.commit()
            return item

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

在配置文件settings中

#数字越小越先被调用
ITEM_PIPELINES = {
   'test_s.pipelines.TestSPipeline': 300,
   'test_s.pipelines.MysqlPipeline':400,
}
MYSQL_HOST='localhost'
MYSQL_PORT='3306'
MYSQL_DB='scrapy'
MYSQL_TABLE='titles'
MYSQL_USER='root'
MYSQL_PASSWORD='123456'

二、Selector的用法

xpath、css、正则

三、Spider的用法

1、Spider运行流程 ---Spider主要做两件事[1]、定义爬取网站的动作 [2]、分析爬取下来的网页

 

2、Spider类 ---提供了start_requests()方法的默认实现 -> 读取start_urls属性,并根据返回的结果调用parse()方法解析结果

属性:

①name:爬虫名称

②allowed_domains:允许爬虫的域名

③start_urls:起始URL列表,当没有实现start_requests()时,默认会从这个列表开始抓取

③custom_settings:字典,专属本Spider的配置,覆盖项目全局的设置。此设置必须在初始化之前被更新,必须定义成类变量

④crawler:由from_crawler()方法设置的,代表本Spider类对应的Crawler对象,利用它可以获取项目的一些配置信息

⑤settings;Settings对象,可以直接获取项目的全局设置变量

方法:

①start_requests():生成初始请求,必须返回一个可迭代对象。默认使用start_urls里的URL来构造Request(GET请求方式),

若想以POST方式访问某个站点,可以直接重写该方法,发送POST请求时使用FormRequest

②parse():当Response没有指定回调函数时,该方法会默认被调用。该方法需返回一个包含Request或Item的可迭代对象

③closed():当Spider关闭时,该方法会被调用

四、Downloader Middleware的用法

Scrapy内置了许多Downloader Middleware,被定义在DOWNLOADER_MIDDLEWARES_BASE变量中,

可以在settings修改DOWNLOADER_MIDDLEWARES以及禁用(将该中间件优先级设置为None)内置的Downloader Middleware

在整个架构中的位置为:

①在Request执行下载之前对其进行修改

②在生成Response被Spider解析之前对其进行修改

可以修改User-Agent、处理重定向、设置代理、失败重试、设置Cookies等

核心方法有三个:process_request(request,spider)、process_response(request,response,spider)、process_exception(request,exception,spider)

实现至少一个方法,就可以定义一个Downloader Middleware

1、process_request(request,spider)

在Request从队列里调度出来到Downloader下载执行之前,都可以用process_request()方法对Request进行处理

参数:①request:Request对象,即被处理的Request

   ②spider:Spider对象,即此Request对应的Spider

---返回值必须为None、Response对象、Request对象 之一,或抛出IgnoreRequest异常

①返回None时,不同的Downloader Middleware按照设置的优先级顺序依次对Request进行修改,最后送至Downloader执行

②返回Response对象时,更低优先级的Downloader Middleware的process_request()和process_exception()不会继续调用,每个Downloader Middleware的process_response()依次被调用,调用完毕后,直接将Response对象发送给Spider来处理

③返回Request对象时,更低优先级的Downloader Middleware的process_request()停止执行,该Request会重新放到调度队列里,相当于全新的Request

④IgnoreRequest异常抛出,所有的Downloader Middleware的process_exception()依次执行,若没有一个方法处理该异常,那么Request的errorback()方法就会回调。如果该异常还没有被处理,那么它便会被忽略

2、process_response(request,response,spider)

参数:①request:Request对象,此Response对应的Request

   ②response:Response对象,此被处理的Response

   ③spider:Spider对象,此Response对应的Spider

不同的返回情况:

①返回Request对象时,更低优先级的Downloader Middleware的process_response()不会被调用,该Request会重新放到调度队列里,相当于全新的Request

②返回Response对象时,更低优先级的Downloader Middleware的process_response()继续调用,继续对该Response对象进行处理

③IgnoreRequest异常抛出,则Request的errorback()方法就会回调。如果该异常还没有被处理,那么它便会被忽略

3、process_exception(request,exception,spider)

参数:①request:Request对象,产生异常的Request

   ②exception:Exception对象,即抛出的异常

   ③spider:Spider对象,即Request对应的Spider

不同的返回情况:

①返回为None时,更低优先级的Downloader Middleware的process_exception()会被继续调用,直到所有的方法都被调度完毕

②返回Response对象时,更低优先级的Downloader Middleware的process_exception()不再继续调用,每个Downloader Middleware的process_response()转而被依次调用

③返回Request对象时,更低优先级的Downloader Middleware的process_exception()也不再继续调用,该Request会重新放到调度队列里,相当于全新的Request

4、实战:

在parse()中可以用 response.request.headers 查看请求头信息

如 b'User-Agent': [b'Scrapy/1.5.1 (+https://scrapy.org)'], 使用的Use-Agent是Scrapy/1.5.1 (+https://scrapy.org),它是由Scrapy内置的UserAgentMiddleware设置的

from scrapy import signals

class UserAgentMiddleware(object):
    """This middleware allows spiders to override the user_agent"""

    def __init__(self, user_agent='Scrapy'):
        self.user_agent = user_agent

    @classmethod
    def from_crawler(cls, crawler):
        o = cls(crawler.settings['USER_AGENT'])
        crawler.signals.connect(o.spider_opened, signal=signals.spider_opened)
        return o

    def spider_opened(self, spider):
        self.user_agent = getattr(spider, 'user_agent', self.user_agent)

    def process_request(self, request, spider):
        if self.user_agent:
            request.headers.setdefault(b'User-Agent', self.user_agent)

修改User-Agent有两种方式①直接修改在settings的USER_AGENT变量 ②通过Downloader Middleware的process_request()来修改

如需设置随机的User-Agent,需要使用第二种方法

class RandomUserAgentMiddleware(object):

    def __init__(self):
        self.user_agents = [
            "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; AcooBrowser; .NET CLR 1.1.4322; .NET CLR 2.0.50727)",
            "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Acoo Browser; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506)",
            "Mozilla/4.0 (compatible; MSIE 7.0; AOL 9.5; AOLBuild 4337.35; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)",
            "Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)",
            "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 2.0.50727; Media Center PC 6.0)",
            "Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 1.0.3705; .NET CLR 1.1.4322)",
            "Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.2; .NET CLR 1.1.4322; .NET CLR 2.0.50727; InfoPath.2; .NET CLR 3.0.04506.30)",
            "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN) AppleWebKit/523.15 (KHTML, like Gecko, Safari/419.3) Arora/0.3 (Change: 287 c9dfb30)",
            "Mozilla/5.0 (X11; U; Linux; en-US) AppleWebKit/527+ (KHTML, like Gecko, Safari/419.3) Arora/0.6",
            "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.2pre) Gecko/20070215 K-Ninja/2.1.1",
            "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9) Gecko/20080705 Firefox/3.0 Kapiko/3.0",
            "Mozilla/5.0 (X11; Linux i686; U;) Gecko/20070322 Kazehakase/0.4.5",
            "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.8) Gecko Fedora/1.9.0.8-1.fc10 Kazehakase/0.5.6",
            "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11",
            "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/535.20 (KHTML, like Gecko) Chrome/19.0.1036.7 Safari/535.20",
            "Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; fr) Presto/2.9.168 Version/11.52",
            "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.11 TaoBrowser/2.0 Safari/536.11",
            "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.71 Safari/537.1 LBBROWSER",
            "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; LBBROWSER)",
            "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 732; .NET4.0C; .NET4.0E; LBBROWSER)",
            "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.84 Safari/535.11 LBBROWSER",
            "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E)",
            "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; QQBrowser/7.0.3698.400)",
            "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 732; .NET4.0C; .NET4.0E)",
            "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; SV1; QQDownload 732; .NET4.0C; .NET4.0E; 360SE)",
            "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 732; .NET4.0C; .NET4.0E)",
            "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E)",
            "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.89 Safari/537.1",
            "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.89 Safari/537.1",
            "Mozilla/5.0 (iPad; U; CPU OS 4_2_1 like Mac OS X; zh-cn) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148 Safari/6533.18.5",
            "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:2.0b13pre) Gecko/20110307 Firefox/4.0b13pre",
            "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:16.0) Gecko/20100101 Firefox/16.0",
            "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11",
            "Mozilla/5.0 (X11; U; Linux x86_64; zh-CN; rv:1.9.2.10) Gecko/20100922 Ubuntu/10.10 (maverick) Firefox/3.6.10"
            ]

    def process_request(self, request, spider):
        request.headers['User-Agent']=random.choice(self.user_agents)

 Downloader Middleware组件非常重要,是做异常处理和应对反爬处理的核心。后面将用它来处理代理、Cookies等内容

五、Spider Middleware的用法

和Downloader Middleware一样,Scrapy内置了许多Spider Middleware,被定义在SPIDER_MIDDLEWARES_BASE变量中,

可以在settings修改SPIDER_MIDDLEWARES,会和SPIDER_MIDDLEWARES_BASE定义的Spider Middleware合并

在整个架构中的位置为:

①在Response发送给Spider之前对Response进行处理

②在Request发送给Scheduler之前对Request进行处理

③在Item发送给Item Pipeline之前对Item进行处理

核心方法有四个:process_spider_input(response,spider)、process_spider_output(response,result,spider)、process_spider_exception(response,exception,spider)、process_start_request(start_request,spider),实现至少一个方法,就可以定义一个Spider Middleware

1、process_spider_input(response,spider)

当Response被Spider Middleware处理时,process_spider_input()被调用

参数:①response:Response对象,即被处理的Response

   ②spider:Spider对象,即该Response对应的Spider

不同的返回情况:

①返回None时,Scrapy将会继续处理该Response,调用所有其他的Spider Middleware,直到Spider处理该Response

②抛出异常时,Scrapy将不会调用任何其他的Spider Middleware的process_spider_input(),而调用Request的errback(),errback的输出将重新输入到中间件中,使用process_spider_output()来处理,当其抛出异常时则调用process_spider_exception()来处理

2、process_spider_output(response,result,spider)

当Spider处理Response返回结果时,process_spider_output()被调用

参数:①response:Response对象,即生成该输出的Response

   ②result:包含Request或Item对象的可迭代对象,即Spider返回的结果

   ③spider:Spider对象,即其结果对应的Spider

   该方法必须返回包含Request或Item对象的可迭代对象。

3、process_spider_exception(response,exception,spider)

当Spider或Spider Middleware的process_spider_input()抛出异常时,process_spider_exception()被调用

参数:①response:Response对象,即异常被抛出时被处理的Response

   ②exception:Exception对象,即被抛出的异常

   ③spider:Spider对象,即抛出该异常的Spider

不同的返回情况:

①返回None时,Scrapy将会继续处理该异常,调用其他Spider Middleware中的process_spider_exception(),直到所有的Spider Middleware都被调用

②返回包含Request或Item对象的可迭代对象时,则其他Spider Middleware中的process_spider_output()被调用,其他的process_spider_exception()不会被调用

4、process_start_request(start_request,spider)

该方法以Spider启动的Request为参数时被调用,执行过程类似于process_spider_output(),不过没有相关联的Response,并且必须返回Request

参数:①start_request:包含Request的可迭代对象,即Start Request

   ②spider:Spider对象,即Start Request所属的Spider

   该方法必须返回另一个包含Request对象的可迭代对象

Spider Middleware的使用频率不如Downloader Middleware高,在必要的情况下它可以用来方便数据的处理

六、Item Pipeline的用法

当Spider解析完Response之后,Item就会传递到Item Pipeline,被定义的Item Pipeline组件会顺次调用。

Item Pipeline的作用:

    ①清理HTML数据

    ②验证爬取数据,检查爬取字段

    ③查重并丢弃重复内容

    ④将爬取结果保存到数据库

核心方法有四个,必须要实现process_item(item,spider),其他还有几个比较实用的方法,open_spider(spider)、close_spider(spider)、from_crawler(cls,crawler)

1、process_item(item,spider)

被定义的Item Pipeline会默认调用该方法对Item进行处理

参数:①item:Item对象,即被处理的Item

   ②spider:Spider对象,即生成该Item的Spider

不同的返回情况:

①返回Item对象时,此Item会被更低优先级的Item Pipeline的process_item()处理,直到所有的方法被调用完毕

②抛出DropItem异常,那么此Item会被丢弃,不再进行处理

2、open_spider(spider)

该方法实在Spider开启的时候被自动调用的。我们可以在这里做一些初始化操作,如开启数据库连接等

参数:spider:被开启的Spider对象

3、close_spider(spider)

该方法实在Spider关闭的时候被自动调用的。我们可以在这里做一些收尾工作,如关闭数据库连接等

参数:spider:被关闭的Spider对象

4、from_crawler(cls,crawler)

类方法,用@classmethod标识,是一种依赖注入的方式。

参数:①crawler:通过crawler对象,我们可以拿到Scrapy的所有核心组件,如全局配置的每个信息,然后创建一个Pipeline实例

   ②cls:就是Class

   最后返回一个Class实例

七、通用爬虫

1、CrawlSpider

CrawlSpider继承Spider类,除了Spider类的所有属性和方法,还提供了非常重要的属性和方法

①rules:爬取规则的属性,包含一个或多个Rule对象的列表。每个Rule对爬取网站的动作都做了定义,CrawlSpider会读取rules的每一个Rule并进行解析

②parse_start_url():可重写的方法。当start_urls里对应的Request得到Response时,该方法被调用,它会分析Response并返回Item对象或者Request对象

最重要的是Rule的定义:

scrapy.contrib.spiders下的Rule类

class Rule(object):

    def __init__(self, link_extractor, callback=None, cb_kwargs=None, follow=None, process_links=None, process_request=identity):  

参数:

①link_extractor:Link Extractor对象,通过它提取链接并自动生成Request。它又是一个数据结构,常用LxmlLinkExtractor对象作为参数。

scrapy.linkextractors.lxmlhtml下的LxmlLinkExtractor类

class LxmlLinkExtractor(FilteringLinkExtractor):

    def __init__(self, allow=(), deny=(), allow_domains=(), deny_domains=(), restrict_xpaths=(),
                 tags=('a', 'area'), attrs=('href',), canonicalize=False,
                 unique=True, process_value=None, deny_extensions=None, restrict_css=(),
                 strip=True):

  [1]、allow是正则表达式或正则表达式列表,符合的链接才被跟进,deny则相反

  [2]、allow_domains是域名白名单,deny_domains则相反

  [3]、restrict_xpaths、restrict_css xpath和css表达式或其列表,用xpath或css提取链接

②callback:回调函数,注意,避免使用parse()作为回调函数

③cb_kwargs:字典,包含传递给回调函数的参数

④follow:布尔值,指定根据该规则从response提取的链接是否需要跟进。若callback为None,follow默认为True,否则默认为False

⑤process_links:指定处理函数,从link_extractor中获取链接列表时,该函数将会调用,主要用于过滤

⑥process_request:同样指定处理函数,根据该Rule提取到每个Request时,该函数都会调用,对Request进行处理。该函数必须返回Request或None

2、Item Loader

Item提供的是保存抓取数据的容器,Item Loader提供的是填充容器的机制。数据的提取会变得更加规则化。

scrapy.loader下的ItemLoader类

class ItemLoader(object):

    default_item_class = Item
    default_input_processor = Identity()
    default_output_processor = Identity()
    default_selector_class = Selector

    def __init__(self, item=None, selector=None, response=None, parent=None, **context):

返回一个新的Item Loader来填充给定的Item,若没有给出Item,则使用中的类自动实例化default_item_class,用selector、response来使用选择器或响应参数实例化

参数:

①item:Item对象,可以调用add_xpath()、add_css()、add_value()等方法来填充Item对象

②selector:Selector对象,用来提取填充数据的选择器

③response:Response对象,用于使用构造选择器的Response

 

 

 

本文参考文献:[1]崔庆才.python3网络爬虫开发实战[M].北京:人民邮电出版社,2018:468-541.

 

posted on 2018-11-20 09:05  我很好u 阅读( ...) 评论( ...) 编辑 收藏

转载于:https://www.cnblogs.com/jyh-py-blog/p/9986996.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值