Scrapy : Requests and Responses

Requests and Responses

scrapy 使用 Request and Response 对象来爬取网站。

通常,request对象在爬虫里生成并在系统中传递,直到他们到达下载器,这执行请求并返回Response 对象,该对象返回到发送请求的spider 中。

这俩都有子类用来添加基类中不用的功能。下面就是这俩子类的表述。

Request objects

class scrapy.http.Request(url,callback=None,method='GET,header=None.body=None,cookies=None,meta=None,encoding='utf-8',priority=0,dont_filter=False,errback=None,flags=None,cb_kwargs=None)

Request 对象代表了一个HTTP 请求,通常在spider中生成下载器来执行,然后生成Resposne

Parameters:

  • url(string) - 这个请求的url,如果是空,ValueError 报错

  • callback(callable)-在请求下载了响应后调用这个函数,如果没有指定,就使用parse()方法。如果在处理过程中出现错误,调用errback方法。

  • method-HTTP请求方式,默认是 GET

  • meta(dict) -Request.meta属性的初始值,如果给了,这个字典就在参数中传递浅复制。

  • body (str or unicode) – 请求体,如果传递的是Unicode,会使用接收了编码格式的encoding 解析成字符串(默认是utf-8),如果没给这个参数,储存一个空字符串。不过这个参数的格式,最后都是储存为字符串(不会是unicode or None)

  • headers (dict) – 请求头,字典形式的值可以是字符串(单值标头)或是列表(多值表头)。如果传递None,HTTP标头就不会传送

  • cookies (dict or list) –

    请求cookies,可以用两种格式发送:

    1. Using a dict:字典

      request_with_cookies = Request(url="http://www.example.com",
                                     cookies={'currency': 'USD', 'country': 'UY'})
      
    2. Using a list of dicts:字典的列表

      request_with_cookies = Request(url="http://www.example.com",
                                     cookies=[{'name': 'currency',
                                              'value': 'USD',
                                              'domain': 'example.com',
                                              'path': '/currency'}])
      

    后一个形式允许自定义cookie的 domain and path属性。如果cookie要保存为下一个请求使用,这个功能才有用。

    当一些网站返回cookies(再响应中)时会被保存在domain的cookies里并会在将来的请求种再次发送。通常的浏览器都是这样。

    创造一个不发送储存cookies并不保存接收cookies的请求,设置dont_merge_coookies关键字为True, 再request.meta里。

    发送自定义的cookie并忽略cookie储存请求的实例:

    Request(
        url="http://www.example.com",
        cookies={'currency': 'USD', 'country': 'UY'},
        meta={'dont_merge_cookies': True},
    )
    

    For more info see CookiesMiddleware.

  • encoding (string) –请求的编码方式默认 utf-8。这个编码将用来百分比编码URL并转换body为 字符串(如果给的是unicode)

  • priority (int) – 请求的优先级默认0,调度器使用优先级来定义用来处理请求的顺序,高优先级的请求经早点执行。负值也可以用来表示更低的优先级。

  • dont_filter (boolean) –表示不要让调度器过滤掉请求。当你像执行一个请求很多次时使用,忽视重复项过滤,小心使用,否则会循环抓取,默认False.

  • errback (callable) – 再处理请求时如果出现任何错误,调用这个方法。这包括404HTTP错误等,接收一个Failure 作为第一个参数,

    Changed in version 2.0: 回调参数不在需要当errback参数指定时

  • flags (list) – 发送给请求的标志,可以用来日志记录或类似的处理。

  • cb_kwargs (dict) – 一个可以有任意数据的字典,将作为关键字参数传递给请求的回调函数。

url

请求URL 的字符串,记住,这个属性包含转义的URL,所以不同于__inti__f方法种传递的URL,这个属性是只读的,改变请求的URL,使用replace() 方法。

method

代表HTTP方法的字符串,要保证是大写,例如 GET POST 等。

headers

字典形式的对象,包含请求头。

body

包含了请求正文的字符串,这个属性只读,使用replace()方法改变请求正文。

meta

字典包含了这个请求任意的元数据,新请求这个字典是空的,通常会被不同的scrapy 组件填充(extensions middlewares等),所以这个字典包含的数据取决于你可用的扩展,scrapy识别的特殊元关键字的列表详见 Request.meta special keys。当请求使用copy() or replace()克隆时,这个字典是浅复制,也可以从你的spider种通过response.meta属性种访问。

cb_kwargs

包含了这个请求任意元数据的字典,他的内容将作为回调函数的关键字参数传递。新的请求时空的,也就是说回调函数只会得到一个 response对象作为参数,当请求使用copy() replace()方法克隆时是浅复制,从你的spider 的response.cb_kwargs属性可以访问。

copy()[source]

. See also: Passing additional data to callback functions.# 返回复制的请求对象,

replace([url, method, headers, body, cookies, meta, flags, encoding, priority, dont_filter, callback, errback, cb_kwargs])[source]#

返回带有相同成员的请求对象,除了那些通过指定关键字参数赋予新值的成员。默认 request.cb_kwargs and request.meta 被浅复制(除非给了新值作为参数)。

classmethod from_curl(curl_command, ignore_unknown_options=True, **kwargs)[source]

从包含了cURL 命令的字符串种创建以请求对象,它填充HTTP方法,URL,请求头,cookie 和 body。它使用request类相同的参数,优先并重写包含在cURL命令里的同一参数的值。无法识别参数就默认忽略。当发现未知选项时为了生成错误,调用传递给 ignore_unknown_options=False的方法。

Caution

从Request子类种使用 from_curl()像 jsonrequest or xmlrpcrequest,并且downloader middlewares and spider middlewares可用,像DefaultHeadersMiddleware, UserAgentMiddleware, or HttpCompressionMiddleware,将修改请求对象

To translate a cURL command into a Scrapy request, you may use curl2scrapy.# 将cURL 命令翻译成scrapy request,详见

Passing additional data to callback functions

scrapy.http.Response) object as its first argument.# 当响应被下载后调用请求的回调函数,回调函数的第一个参数就是下载的Response对象。

Example:

def parse_page1(self, response):
    return scrapy.Request("http://www.example.com/some_page.html",
                          callback=self.parse_page2)

def parse_page2(self, response):
    # this would log http://www.example.com/some_page.html
    self.logger.info("Visited %s", response.url)

一些情况下,你可能想给这些回调函数传递一些参数,在第二个回调函数中接收,下面的例子展示了cb_wkargs的方法。

def parse(self, response):
    request = scrapy.Request('http://www.example.com/index.html',
                             callback=self.parse_page2,
                             cb_kwargs=dict(main_url=response.url))
    request.cb_kwargs['foo'] = 'bar'  # add more arguments for the callback
    yield request

def parse_page2(self, response, main_url, foo):
    yield dict(
        main_url=main_url,
        other_url=response.url,
        foo=foo,
    )

Caution

cb_kwargs在1.7版本中引入,之前推荐使用 request.meta来在回调函数中传递信息。1.7以后,cb_kwargs变成了更好的方式来处理使用信息,脱离了meta 来在组件中进行通讯(像中间件,扩展)

Using errbacks to catch exceptions in request processing

在处理请求时如果出现例外就调用这个函数,

接收一个 Failure 作为第一个参数,可用来跟踪连接建立超时,DNS错误等。

记录所有错误,并抓取一些指定错误如果需要。

import scrapy

from scrapy.spidermiddlewares.httperror import HttpError
from twisted.internet.error import DNSLookupError
from twisted.internet.error import TimeoutError, TCPTimedOutError

class ErrbackSpider(scrapy.Spider):
    name = "errback_example"
    start_urls = [
        "http://www.httpbin.org/",              # HTTP 200 expected
        "http://www.httpbin.org/status/404",    # Not found error
        "http://www.httpbin.org/status/500",    # server issue
        "http://www.httpbin.org:12345/",        # non-responding host, timeout expected
        "http://www.httphttpbinbin.org/",       # DNS error expected
    ]

    def start_requests(self):
        for u in self.start_urls:
            yield scrapy.Request(u, callback=self.parse_httpbin,
                                    errback=self.errback_httpbin,
                                    dont_filter=True)

    def parse_httpbin(self, response):
        self.logger.info('Got successful response from {}'.format(response.url))
        # do something useful here...

    def errback_httpbin(self, failure):
        # log all failures
        self.logger.error(repr(failure))

        # in case you want to do something special for some errors,
        # you may need the failure's type:

        if failure.check(HttpError):
            # these exceptions come from HttpError spider middleware
            # you can get the non-200 response
            response = failure.value.response
            self.logger.error('HttpError on %s', response.url)

        elif failure.check(DNSLookupError):
            # this is the original request
            request = failure.request
            self.logger.error('DNSLookupError on %s', request.url)

        elif failure.check(TimeoutError, TCPTimedOutError):
            request = failure.request
            self.logger.error('TimeoutError on %s', request.url)

Accessing additional data in errback functions

在处理请求失败的情况下,你可能会对访问回调函数的参数感兴趣,所有你可以基于errback中的参数进行进一步处理,下面的例子展示了使用cb_kwargs进行访问。

def parse(self, response):
    request = scrapy.Request('http://www.example.com/index.html',
                             callback=self.parse_page2,
                             errback=self.errback_page2,
                             cb_kwargs=dict(main_url=response.url))
    yield request

def parse_page2(self, response, main_url):
    pass

def errback_page2(self, failure):
    yield dict(
        main_url=failure.request.cb_kwargs['main_url'],
    )

Request.meta special keys

request.meta属性可以包含任何数据,但是有一些特别的关键字,被scrapy 和内置扩展识别。

Those are:

bindaddress

用于执行请求而发出的IP

download_timeout

下载器在超时前等待的时间,秒为单位

download_latency

获取响应的时间,因为请求已经开始,即通过网络发送的HTTP消息,当响应被下载后这个元数据才可以使用,虽然大部分其他的元数据关键字用来控制scrapy行为,但这个是只读的。

download_fail_on_dataloss

是否因相应失败而放弃。

max_retry_times

这个用来设置每个请求的重试的次数。当初始化时,max_retry_times元键有比RETRY_TIMES设置更高的优先级。

Stopping the download of a Response

从bytes_received信号处理中生成stopdownload例外会终止相应的下载。

import scrapy


class StopSpider(scrapy.Spider):
    name = "stop"
    start_urls = ["https://docs.scrapy.org/en/latest/"]

    @classmethod
    def from_crawler(cls, crawler):
        spider = super().from_crawler(crawler)
        crawler.signals.connect(spider.on_bytes_received, signal=scrapy.signals.bytes_received)
        return spider

    def parse(self, response):
        # 'last_chars' show that the full response was not downloaded
        yield {"len": len(response.text), "last_chars": response.text[-40:]}

    def on_bytes_received(self, data, request, spider):
        raise scrapy.exceptions.StopDownload(fail=False)

which produces the following output:

2020-05-19 17:26:12 [scrapy.core.engine] INFO: Spider opened
2020-05-19 17:26:12 [scrapy.extensions.logstats] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min)
2020-05-19 17:26:13 [scrapy.core.downloader.handlers.http11] DEBUG: Download stopped for <GET https://docs.scrapy.org/en/latest/> from signal handler StopSpider.on_bytes_received
2020-05-19 17:26:13 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://docs.scrapy.org/en/latest/> (referer: None) ['download_stopped']
2020-05-19 17:26:13 [scrapy.core.scraper] DEBUG: Scraped from <200 https://docs.scrapy.org/en/latest/>
{'len': 279, 'last_chars': 'dth, initial-scale=1.0">\n  \n  <title>Scr'}
2020-05-19 17:26:13 [scrapy.core.engine] INFO: Closing spider (finished)

默认情况下,响应结果被他们相应的errbacks处理。如果想要让他们去调用callback,如本例,传递fail=False给StopDownload例外,

Request subclasses

内置的Request的子类,你也可以子类化它实现自己的功能

FormRequest objects

FormRequest类扩展了基本Request的处理HTML 表单的功能,使用response对象的表单数据通过lxml.html forms来填充表单字段。

class scrapy.http.FormRequest(url,formdata)

这个类增加了一个新的关键字属性给__init__方法,其他的参数与request类相同,这里不在记录。

  • Parameters

    formdata (dict or iterable of tuples) – i 字典或可迭代的元组(key,value),包含了HTML表单数据,将进行url编码并发分配给请求体。

formrequest对象支持下面的类方法,除了标准的request方法。

classmethod from_response (response[, formname=None, formid=None, formnumber=0, formdata=None, formxpath=None, formcss=None, clickdata=None, dont_click=False, ])[source]

For an example see Using FormRequest.from_response() to simulate a user login.# 返回一个新的formrequeset对象,他的表单字段值由所给响应中包含的HTML form元素填充。例子请看:

策略是自动模拟点击,默认的,任何看起来可以点击的表单控制元素,例如 。即使这个很方便,也是想要的行为,但有时会造成很难调试的问题。例如:当工作的表单使用javascript来填充或提交,默认的from_response()行为可能不是最适合的,设置dont_click属性为True来禁用这个行为。如果你想改变点击的控件(而不是禁用)你可以使用clickdata属性。

Caution

在选择值中有leading或tariling whitespace 的select元素使用这个方法是没用的,因为lxml的错误,在3.8或更高的版本中修复。

Parameters

  • response (Response object) – 包含了HTML表单的响应,用来填充表单字段。

  • formname (string) –如果给了,使用这个值设置表单的name属性

  • formid (string) – 设置id属性

  • formxpath (string) – 使用匹配xpath的第一个表单

  • formcss (string) – if given, the first form that matches the css selector will be used.

  • formnumber (integer) – 表单使用的数字,当相应包含了多个表单时,第一个是0.

  • formdata (dict) – 重写表单数据的字段,如果字段已经存在在响应的 元素中,他的值被这个传递的参数覆盖,如果传递给这个参数的值是None, 字段将不会包含在请求中,即使存在于相应的元素里。

  • clickdata (dict) – 寻找点击控件的属性,如果没给,表单数据将模拟点击第一个可点击的的元素来提交。除了 html属性,还可以通过nr 属性,从零开始的索引,相对于其他可提交的输入表单来识别控件。

  • dont_click (boolean) –不点击

这个类的其他参数直接传给fromrequest的 __init__方法。

Request usage examples

Using FormRequest to send data via HTTP POST

如果你想模拟一个POST请求并发送一些键值字段,你可以从spider中返回一个formrequest对象

return [FormRequest(url="http://www.example.com/post/action",
                    formdata={'name': 'John Doe', 'age': '27'},
                    callback=self.after_post)]
Using FormRequest.from_response() to simulate a user login

通常网页通过 元素来提供预填充的表单字段,像与session有关的数据或认证(对登录页面)。当抓取时,你想自动填充填充这些字段,并只重写几个,像 user name and password,你可以使用 FormRequest.from_response()方法。

import scrapy

def authentication_failed(response):
    # TODO: Check the contents of the response and return True if it failed
    # or False if it succeeded.
    pass

class LoginSpider(scrapy.Spider):
    name = 'example.com'
    start_urls = ['http://www.example.com/users/login.php']

    def parse(self, response):
        return scrapy.FormRequest.from_response(
            response,
            formdata={'username': 'john', 'password': 'secret'},
            callback=self.after_login
        )

    def after_login(self, response):
        if authentication_failed(response):
            self.logger.error("Login failed")
            return

        # continue scraping with authenticated session...

JsonRequest

JsonRequest类扩展了基本Request类的除了JSON 请求的功能。

class scrapy.http.JsonRequest(url,[...data,dumps_kwargs])

给__init__ 方法提供了两个新的关键字参数,剩下的跟Request类相同

使用这个将会把Content-Type标头设置为application/json, 并将Accept 标头设置为 application/json, text/javascript, */*; q=0.01

  • Parameters

    data (JSON serializable object) –任意的JSON可序列化对象需要JSON解码并分配给body。如果requets.body属性被提供,这个参数就忽略,如果request.body属性没有提供而是提供了这个data 参数,request.method 将自动设置为POST。

    dumps_kwargs (dict) – 参数会传递给底层的json.dumps()方法,用来序列化数据成JSON格式。

JsonRequest usage example

Sending a JSON POST request with a JSON payload:

data = {
    'name1': 'value1',
    'name2': 'value2',
}
yield JsonRequest(url='http://www.example.com/post/action', data=data)

Response objects

class scrapy.http.Response(*args,**kwargs)

响应对象代表了一个HTTP 响应,通常被下载(通过下载器)并给spiders来处理。

Parameters

  • url——这个相应的url
  • status——这个响应的HTTP状态,默认200
  • header(dict) ——响应头,字典的值可以是字符串(单值表头)或字典(多值标头)
  • body(bytes)——响应体,以字符串的形式访问解码后的文本,你可以使用可识别编码的Response 子类像 TextResponse 中的response.txt
  • flag(list)——列表包含了Response.flags属性的初始值,如果给了,列表将被浅复制。
  • request——respsone.request属性的最初值,这代表了生成这个响应的Request。
  • certificate (twisted.internet.ssl.Certificate) ——一个对象代表了服务器的SSL证书
  • ip_address (ipaddress.IPv4Address or ipaddress.IPv6Address) – Response起源的服务器的IP地址。

url

响应的url,这个属性只读的,改变这个使用 replace()方法。

status

HTTP的响应码,例如200 404

headers

包含了响应标头的字典样的对象,值可以使用get() 方法访问来返回指定名字的第一个标头值,或使用getlist() 来返回指定名字的所有标头值。例如,获得标头的所有cookieresponse.headers.getlist('Set-Cookie')

body

响应体,记住了Response.body 是字节对象,如果你想要Unicode版本的,使用TextResponse.text(仅在 TextResponse and subclasses中可用)。这个属性是只读的,使用replace()方法来改变,

request

生成这个响应的请求对象,这个属性在scrpay 引擎下响应和请求传递给了所有的 Downloader Middlewares 后分配,特别是,这意味着:

  • HTTP 重定向会造成最初的请求(重定向之前的URL)被分配给了重定向后的响应(最终重定向的URL )
  • Response.request.url 不总是等于 Respone.url
  • 这个属性只在spider代码和Spider Middlewares中可用,,而不是在下载器中间件(尽管你可以通过其他方式使用Request)和response_downloaded信号的处理程序

meta

Response.request对象(即 self.request.meta)的Request.meta 属性的简写。不想response.request属性,Resposne.meta属性是跟随重定向和重试传递的,所有你可以你的spider 中获得最初的Request.meta。

See alsoRequest.meta attribute

cb_kwargs

New in version 2.0.

A shortcut to the Request.cb_kwargs attribute of the Response.request object (i.e. self.request.cb_kwargs).Unlike the Response.request attribute, the Response.cb_kwargs attribute is propagated along redirects and retries, so you will get the original Request.cb_kwargs sent from your spider.# 额意思跟上面那个差不多。

See alsoRequest.cb_kwargs attribute

flags

包含了这个响应的标识的列表,标志是用来标记响应的标签。例如,cached redirected 等,他们展示在响应的字符串表示(字符串方法)上,并被引擎拿来做记录。

certificate

代表服务器的SSL 证书,只会为https 响应填充,其他的就是None.

ip_address

产生响应的服务器的IP地址,这个属性当前仅由HTTP 1.1下载处理器填充,即http(s) 响应,对其他处理器,总是为none

copy()[source]

返回复制了这个响应的新的响应对象

replace([url, status, headers, body, request, flags, cls])[source]

返回有着相同成员的响应对象,除了那些被指定了新的值的。属性response.meta 默认是复制的。

urljoin(url)[source]

构建一个绝对url 通过结合响应的url和一个可能的相对url。这是对parse.urljoin()的包装,只是进行调用的别名。urllib.parse.urljoin(response.url, url)

follow(url, callback=None, method=‘GET’, headers=None, body=None, cookies=None, meta=None, encoding=‘utf-8’, priority=0, dont_filter=False, errback=None, cb_kwargs=None, flags=None)[source]

返回一个request 实例来跟进url 连接。接收和request._init_ 相同的参数,但是url可以是相对url 或一个scrapy.link.Link 对象,不只是绝对url。TextResponse 提供一个follow 方法支持选择器,除了相对/绝对urls 和连接对象

follow_all(urls, callback=None, method=‘GET’, headers=None, body=None, cookies=None, meta=None, encoding=‘utf-8’, priority=0, dont_filter=False, errback=None, cb_kwargs=None, flags=None)[source]

返回一个可迭代的request实例来跟进urls 的所有连接。后面的同上,textresponse 提供的是follow_all()方法。

Response subclasses

可用的内置 Response 子类,你也可以子类化Response class 来实现自己的功能。

TextResponse objects

classscrapy.http.``TextResponse(url**[, encoding[, ]****]**)[source]

TextResponse 对象增加了解码能力,这意味着仅用于二进制数据,像图像声音或任意媒体文件。

TextResponse 对象支持新的__init__方法参数,除了基本的response对象。剩余的功能跟Response 类相同。

  • Parameters

    encoding (string) – 字符串包含的对这个响应的编码方式,如果你想创建一个Textresponse 对象有着unicode body, 就使用这个编码方式来编码(body 属性总是是字符串),如果 encoding 为None(默认),编码方式就在响应 header and body 里找。

除了标准的Response 属性TextResponse 还支持一下属性。

  • text

    响应体,是unicode 字符串

    The same as response.body.decode(response.encoding),但是结果在第一次调用后会被缓存,索引你可以多次访问response.text,而不用额外的开销

    Note

    unicode(response.body )不是转换响应体为unicode 字符串的正确的方式,你会使用系统默认的编码(通常是ascii)而不是响应编码。

  • encoding

    这个响应的编码字符串,编码通过尝试下面的机制来解决,顺序为:

    1. 传递给__init__方法encoding 属性的编码方式
    2. content-type HTTP header声明的编码方式,如果此编码无效(即未知),就忽略并尝试下一个解析机制。
    3. 响应体中声明的编码,textresponse类对此没有提供任何特别的功能,但是 htmlresponse and xmlresponse 提供了。
    4. 通过查看响应体推断出来的编码,这是更加垃圾的方法,但是是最后的方法。
  • selector

    以响应为目标的selector 实例,这个实例在第一次访问时会延迟实例化。

:除了标准的Response 方法TextResponse 还支持一下方法。

  • xpath(query)[source]

    A shortcut to TextResponse.selector.xpath(query):

    response.xpath('//p')

  • css(query)[source]

    A shortcut to TextResponse.selector.css(query):

    response.css('p')

  • follow(url, callback=None, method=‘GET’, headers=None, body=None, cookies=None, meta=None, encoding=None, priority=0, dont_filter=False, errback=None, cb_kwargs=None, flags=None)[source]

    返回一个request实例来跟进链接url,它接受与Request .__ init__方法相同的参数,但url不仅可以是绝对URL,还可以是:

    1. a relative URL
    2. a object, e.g. the result of Link Extractors
    3. a Selector object for a <link> or <a> element, e.g. response.css('a.my_link')[0]
    4. an attribute Selector (not SelectorList), e.g. response.css('a::attr(href)')[0] or response.xpath('//img/@src')[0]

    See A shortcut for creating Requests for usage examples.

  • follow_all(urls=None, callback=None, method=‘GET’, headers=None, body=None, cookies=None, meta=None, encoding=None, priority=0, dont_filter=False, errback=None, cb_kwargs=None, flags=None, css=None, xpath=None)[source]

    一个生成器来生成request实例来跟进所有的url 链接,它接受与Request .__ init__方法相同的参数,但url不仅可以是绝对URL,还可以是:

    1. a relative URL
    2. a object, e.g. the result of Link Extractors
    3. a Selector object for a <link> or <a> element, e.g. response.css('a.my_link')[0]
    4. an attribute Selector (not SelectorList), e.g. response.css('a::attr(href)')[0] or response.xpath('//img/@src')[0]

    另外,css和xpath参数被接受以在follow_all方法内执行链接提取(仅接受url,css和xpath之一)。

    注意,在位urls参数传递selectorlist 作为参数时或使用css or xpaht 参数时,这个方法不会对无法获取链接的选择器产生请求(例如,没有href属性的锚标签)

  • json()[source]

JSON文档反序列化为python 对象,第一次调用的结果会被缓存。

HtmlResponse objects

  • classscrapy.http.``HtmlResponse(url**[, ]**)[source]

    See TextResponse.encoding.# HtmlResponse类是TextResponse的子类,增加了编码自动发现支持通过查看HTML meta http-equiv属性。

XmlResponse objects

  • classscrapy.http.``XmlResponse(url**[, ]**)[source]

    See TextResponse.encoding.# XmlResponse类是TextResponse的子类,增加了编码自动发现支持通过查看XML声明行

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值