爬虫第二课

三、抓包工具Fiddler

Fiddler是一款强大Web调试工具,它能记录所有客户端和服务器的HTTP请求。 Fiddler启动的时候,默认IE的代理设为了127.0.0.1:8888,而其他浏览器是需要手动设置。

工作原理

Fiddler 是以代理web服务器的形式工作的,它使用代理地址:127.0.0.1,端口:8888

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cxtdABad-1572593770696)(assets/fidder_pro.jpg)]

Fiddler抓取HTTPS设置

  1. 启动Fiddler,打开菜单栏中的 Tools > Telerik Fiddler Options,打开“Fiddler Options”对话框。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N66kDaWl-1572593770696)(assets/01-fidder.png)]

  2. 对Fiddler进行设置:

    • 打开工具栏->Tools->Fiddler Options->HTTPS,

    • 选中Capture HTTPS CONNECTs (捕捉HTTPS连接),

    • 选中Decrypt HTTPS traffic(解密HTTPS通信)

    • 另外我们要用Fiddler获取本机所有进程的HTTPS请求,所以中间的下拉菜单中选中…from all processes (从所有进程)

    • 选中下方Ignore server certificate errors(忽略服务器证书错误)

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-okMv8ppB-1572593770697)(assets/01-fidder_01-1559532796189.png)]

  3. 为 Fiddler 配置Windows信任这个根证书解决安全警告:Trust Root Certificate(受信任的根证书)。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FcQpOsmV-1572593770697)(assets/01-fidder_03.png)]

  4. Fiddler 主菜单 Tools -> Fiddler Options…-> Connections

    • 选中Allow remote computers to connect(允许远程连接)

    • Act as system proxy on startup(作为系统启动代理)

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FwZ9290k-1572593770697)(assets/01-fidder_02.png)]

  5. 重启Fiddler,使配置生效(这一步很重要,必须做)。

Fiddler 如何捕获Chrome的会话

  1. 安装SwitchyOmega 代理管理 Chrome 浏览器插件

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OdSZsjYu-1572593770697)(assets/switchyomega.png)]

  2. 如图所示,设置代理服务器为127.0.0.1:8888

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ODGyk3ik-1572593770698)(assets/switchyomega_setting.png)]

  3. 通过浏览器插件切换为设置好的代理。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z9PakEi7-1572593770698)(assets/SwitchyOmega_switch.png)]

Fiddler界面

设置好后,本机HTTP通信都会经过127.0.0.1:8888代理,也就会被Fiddler拦截到。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HNqFjloS-1572593770698)(assets/fiddler_show.png)]

请求 (Request) 部分详解
  1. Headers —— 显示客户端发送到服务器的 HTTP 请求的 header,显示为一个分级视图,包含了 Web 客户端信息、Cookie、传输状态等。
  2. Textview —— 显示 POST 请求的 body 部分为文本。
  3. WebForms —— 显示请求的 GET 参数 和 POST body 内容。
  4. HexView —— 用十六进制数据显示请求。
  5. Auth —— 显示响应 header 中的 Proxy-Authorization(代理身份验证) 和 Authorization(授权) 信息.
  6. Raw —— 将整个请求显示为纯文本。
  7. JSON - 显示JSON格式文件。
  8. XML —— 如果请求的 body 是 XML 格式,就是用分级的 XML 树来显示它。
响应 (Response) 部分详解
  1. Transformer —— 显示响应的编码信息。
  2. Headers —— 用分级视图显示响应的 header。
  3. TextView —— 使用文本显示相应的 body。
  4. ImageVies —— 如果请求是图片资源,显示响应的图片。
  5. HexView —— 用十六进制数据显示响应。
  6. WebView —— 响应在 Web 浏览器中的预览效果。
  7. Auth —— 显示响应 header 中的 Proxy-Authorization(代理身份验证) 和 Authorization(授权) 信息。
  8. Caching —— 显示此请求的缓存信息。
  9. Privacy —— 显示此请求的私密 (P3P) 信息。
  10. Raw —— 将整个响应显示为纯文本。
  11. JSON - 显示JSON格式文件。
  12. XML —— 如果响应的 body 是 XML 格式,就是用分级的 XML 树来显示它 。

—————————————————————————————————————————————————

四、urllib和urllib2库的基本使用

所谓网页抓取,就是把URL地址中指定的网络资源从网络流中抓取出来。在Python中有很多库可以用来抓取网页,我们先学习urllib2

urllib2 是 Python2.7 自带的模块(不需要下载,导入即可使用)

urllib2 官方文档:https://docs.python.org/2/library/urllib2.html

urllib2 源码:https://hg.python.org/cpython/file/2.7/Lib/urllib2.py

在 python3 中,urllib2 被改为urllib.request

urlopen

我们先来段代码:

# 创建一个py文件:urllib2_urlopen.py

# 导入urllib2 库
import urllib2

# 向指定的url发送请求,并返回服务器响应的类文件对象
response = urllib2.urlopen("http://www.baidu.com")

# 类文件对象支持 文件对象的操作方法,如read()方法读取文件全部内容,返回字符串
html = response.read()

# 打印字符串
print(html)

执行写的python代码,将打印结果

python@ubuntu:~/Desktop/demo$ python urllib2_urlopen.py

实际上,如果我们在浏览器上打开百度主页, 右键选择“查看源代码”,你会发现,跟我们刚才打印出来的是一模一样。也就是说,上面的4行代码就已经帮我们把百度的首页的全部代码爬了下来。

一个基本的url请求对应的python代码真的非常简单。

Request

在我们第一个例子里,urlopen()的参数就是一个url地址;

但是如果需要执行更复杂的操作,比如增加HTTP报头,必须创建一个 Request 实例来作为urlopen()的参数;而需要访问的url地址则作为 Request 实例的参数。

我们编辑urllib2_request.py

# urllib2_request.py

import urllib2

# url 作为Request()方法的参数,构造并返回一个Request对象
request = urllib2.Request("http://www.baidu.com")

# Request对象作为urlopen()方法的参数,发送给服务器并接收响应
response = urllib2.urlopen(request)

html = response.read()

print(html)

运行结果是完全一样的:

新建Request实例,除了必须要有 url 参数之外,还可以设置另外两个参数:

  1. data(默认空):提交的Form表单数据,同时 HTTP 请求方法将从默认的 "GET"方式 改为 "POST"方式。
  2. headers(默认空):参数为字典类型,包含了需要发送的HTTP报头的键值对。

User-Agent

但是这样直接用urllib2给一个网站发送请求的话,确实略有些唐突了,就好比,人家每家都有门,你以一个路人的身份直接闯进去显然不是很礼貌。而且有一些站点不喜欢被程序(非人为访问)访问,有可能会拒绝你的访问请求。

但是如果我们用一个合法的身份去请求别人网站,显然人家就是欢迎的,所以我们就应该给我们的这个代码加上一个身份,就是所谓的User-Agent头。

  • 浏览器 就是互联网世界上公认被允许的身份,如果我们希望我们的爬虫程序更像一个真实用户,那我们第一步就是需要伪装成一个被浏览器。用不同的浏览器在发送请求的时候,会有不同的 User-Agent 报头。
  • urllib2默认的User-Agent头为:Python-urllib/x.y (x和y 是Python 主.次 版本号,例如 Python-urllib/2.7)
# coding:utf-8
#urllib2_useragent.py

import urllib2

url = "http://www.baidu.com"

# IE 9.0 的 User-Agent,包含在 user_agent里
user_agent = {"User-Agent" : "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)"} 

#  url 连同 headers,一起构造Request请求,这个请求将附带 IE9.0 浏览器的User-Agent
request = urllib2.Request(url, headers = user_agent)

# 向服务器发送这个请求
response = urllib2.urlopen(request)

html = response.read()
print(html)

添加更多的Header信息

在 HTTP Request 中加入特定的 Header,来构造一个完整的HTTP请求消息。

可以通过调用Request.add_header() 添加/修改一个特定的header 也可以通过调用Request.get_header()来查看已有的header。

  • 添加一个特定的header
# coding:utf-8
# urllib2_headers.py

import urllib2

url = "http://www.baidu.com"

#IE 9.0 的 User-Agent
user_agent = {"User-Agent" : "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)"}
request = urllib2.Request(url, headers = user_agent)

#也可以通过调用Request.add_header() 添加/修改一个特定的header
request.add_header("Connection", "keep-alive")

# 也可以通过调用Request.get_header()来查看header信息
# request.get_header(header_name="Connection")

response = urllib2.urlopen(request)

print(response.code     #可以查看响应状态码)
html = response.read()

print(html)
  • 随机添加/修改User-Agent
# urllib2_add_headers.py

import urllib2
import random

url = "http://www.baidu.com"

ua_list = [
    "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1",
    "Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11",
    "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6",
    "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6"
]

user_agent = random.choice(ua_list)

request = urllib2.Request(url)

#也可以通过调用Request.add_header() 添加/修改一个特定的header
request.add_header("User-Agent", user_agent)

# get_header()的字符串参数,第一个字母大写,后面的全部小写
request.get_header("User-agent")

response = urllib2.urlopen(request)

html = response.read()
print(html)

—————————————————————————————————————————————————

(一)URL编码转换

一般HTTP请求提交数据,需要将数据编码成 URL编码格式,然后做为查询字符串或者表单参数,构建Request对象中再发送。

urllib 和 urllib2 都是接受URL请求的相关模块,但是提供了不同的功能。两个最显著的不同如下:

  • urllib 模块仅可以接受URL,不能创建 设置了headers 的Request 类实例;
  • 但是 urllib 提供 urlencode 方法用来产生GET查询字符串,而 urllib2 则没有。(这是 urllib 和 urllib2 经常一起使用的主要原因)
  • 编码工作使用urllib的urlencode()函数,帮我们将key:value这样的键值对,转换成"key=value"这样的字符串,解码工作可以使用urllib的unquote()函数。( 注意,不是urllib2.urlencode())
# IPython2 中的测试结果
In [6]: import urllib
# 通过urllib.urlencode()方法,将字典键值对按URL编码转换,从而能被web服务器接受。
In [7]: a = {"wd":"杭州很美"}

In [8]: urllib.urlencode(a)
Out[8]: 'wd=%E6%9D%AD%E5%B7%9E%E5%BE%88%E7%BE%8E'


# 通过urllib.unquote()方法,把 URL编码字符串,转换回原先字符串。
In [9]: print(urllib.unquote('wd=%E6%9D%AD%E5%B7%9E%E5%BE%88%E7%BE%8E'))
wd=杭州很美

——————————————————————————————————————————————————

六、Get请求

GET请求一般用于我们向服务器获取数据,比如说,我们用百度搜索杭州好美

https://www.baidu.com/s?wd=“杭州好美”

浏览器的url会跳转成如图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LVcYFFce-1572593770699)(assets/01-shousuo.png)]

https://www.baidu.com/s?wd=%22%E6%9D%AD%E5%B7%9E%E5%A5%BD%E7%BE%8E%E2%80%9C

在其中我们可以看到在请求部分里,http://www.baidu.com/s? 之后出现一个长长的字符串,其中就包含我们要查询的关键词–杭州很美,于是我们可以尝试用默认的Get方式来发送请求。

# urllib2_get.py

import urllib      #负责url编码处理

url = "http://www.baidu.com/s?"

keyword = {"wd":"杭州好美"}

word = urllib.urlencode(keyword) #转换成url编码格式(查询字符串)
newurl = url + word    # 拼接url

headers={ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36"}

request = urllib2.Request(newurl, headers=headers)

response = urllib2.urlopen(request)

print response.read()

批量爬取贴吧页面数据

首先我们创建一个python文件, tiebaSpider.py,我们要完成的是,输入一个百度贴吧的地址,以及起始页和结束页,就能将所有的网页源码下载并保存到本地。

比如百度贴吧LOL吧:

第一页:http://tieba.baidu.com/f?kw=lol&ie=utf-8&pn=0

第二页: http://tieba.baidu.com/f?kw=lol&ie=utf-8&pn=50

第三页: http://tieba.baidu.com/f?kw=lol&ie=utf-8&pn=100

1.发现规律了吧,贴吧中每个页面不同之处,就是url最后的pn的值,其余的都是一样的,我们可以抓住这个规律来写代码。

def test():
	"""测试文件,通过获取输入起始页,结束页,获取pn值"""
    begin_page = int(input("请输入起始页:"))
    end_page = int(input("请输入结束页:"))
    # 循环获取每个页码值
    for page in range(begin_page, end_page + 1):
        # 计算得出当前页码的 pn 值
        pn = (page - 1) * 50
        # 检测pn值是否正确
        print(pn)


if __name__ == '__main__':
    test()
简单写一个小爬虫程序,来爬取百度LOL吧的所有网页。
  • 提示用户输入要爬取的贴吧名,起始页,结束页
  • 写一个main函数
  • main函数里组合的拼接后的url地址,以及起始页码和终止页码,表示要爬取页码的范围。
  • 并用urllib.urlencode()进行转码
  • 然后组合url,假设用户输入的贴吧名是lol,起始页是1, 结束页是大于1的值(例如3)
  • 组合后的起始url就是:http://tieba.baidu.com/f?kw=lol&pn=0
from urllib import parse


tieba_name = input("请输入贴吧名:")
begin_page = int(input("请输入起始页:"))
end_page = int(input("请输入结束页:"))
base_url = "http://tieba.baidu.com/f?"
def main():

    # 循环获取每个页码值
    for page in range(begin_page, end_page + 1):
        # 计算得出当前页码的 pn 值
        pn = (page - 1) * 50

        query_dict = {"kw": tieba_name, "pn": pn}
        # 从urllib包获取parse模块, 里面的urlencode方法
        # 将字典转换为查询字符串
        query_str = parse.urlencode(query_dict)

        # 拼接base_url 和查询字符串,构建完整的url地址
        full_url = base_url + query_str

        print(full_url)

if __name__ == '__main__':
    main()
  • 接下来,我们写一个百度贴吧爬虫接口,url传入send_request函数,函数里用urlopen方法实现爬取网页的功能,并返回一个响应对象。通过response.read()获取响应内容
def send_request(url):
    """
        接收url地址,发送请求,返回响应
    """
    request = urllib.request.Request(url, headers = headers)
    print("[INFO]: 正在发送请求 {}..".format(url))

    response = urllib.request.urlopen(request)
    print(response.read())
    return response
  • 最后如果我们希望将爬取到了每页的信息存储在本地磁盘上,我们可以简单写一个存储文件的接口。
def save_page(response, file_name):
    """
        保存响应内容到指定文件中
    """
    print("[INFO]: 正在保存数据 {}..".format(file_name))

    with open(file_name, "w") as f:
        f.write(response.read().decode())

其实很多网站都是这样的,同类网站下的html页面编号,分别对应网址后的网页序号,只要发现规律就可以批量爬取页面了。

#最终的代码

#coding:utf-8
import urllib
from urllib import request, parse


base_url = "http://tieba.baidu.com/f?"
headers = {"User-Agent" : "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko"}
tieba_name = input("请输入贴吧名:")
begin_page = int(input("请输入起始页:"))
end_page = int(input("请输入结束页:"))


def send_request(url):
    """
        接收url地址,发送请求,返回响应
    """
    request = urllib.request.Request(url, headers = headers)
    print("[INFO]: 正在发送请求 {}..".format(url))

    response = urllib.request.urlopen(request)
    return response

def save_page(response, file_name):
    """
        保存响应内容到指定文件中
    """
    print("[INFO]: 正在保存数据 {}..".format(file_name))

    with open(file_name, "w") as f:
        f.write(response.read().decode())


def main():
    """
        爬虫调度中心
    """
    # 循环获取每个页码值
    for page in range(begin_page, end_page + 1):
        # 计算得出当前页码的 pn 值
        pn = (page - 1) * 50

        # 构建查询字符串
        query_dict = {"kw" : tieba_name, "pn" : pn}
        query_str = urllib.parse.urlencode(query_dict)

        # 拼接base_url 和查询字符串,构建完整的url地址
        full_url = base_url + query_str

        file_name = tieba_name + str(page) + ".html"

        try:
            # 将url地址传给send_request() 发送请求,返回响应
            response = send_request(full_url)
            # 保存响应数据到文件中
            save_page(response, file_name)
        except Exception as e:
            print("[ERROR] : {} 抓取失败.".format(full_url))
            print(e)

if __name__ == '__main__':
    main()


七、POST请求:

获取AJAX加载的内容

有些网页内容使用AJAX请求加载,这种数据无法直接对网页url进行获取。但是只要记住,AJAX请求一般返回给网页的是JSON文件,只要对AJAX请求地址进行POST或GET,就能返回JSON数据了。

如果非要从HTML页面里获取展现出来的数据,也不是不可以。但是要记住,作为一名爬虫工程师,你更需要关注的是数据的来源。

发送POST请求时,需要了解的headers一些属性:

Content-Length: 100: 是指发送的表单数据长度为100,也就是url编码字符串的字符个数是100个。

Content-Type: application/x-www-form-urlencoded : 表示浏览器提交 Web 表单时使用,表单数据会按照 name1=value1&name2=value2 键值对形式进行编码。

X-Requested-With: XMLHttpRequest :表示AJAX异步请求

腾讯翻译君案例:

1.解析页面

输入要翻译的文本,显示出翻译内容,html发生了变化,其url没有发生改变,这是一个动态页面。

通过浏览器自带的抓包工具,抓取动态页面。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c18sd9Q7-1572593770699)(assets/腾讯翻译01.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TtFjhw4x-1572593770699)(assets/腾讯翻译02.png)]

2.分析数据

输入不同的文本内容,分析post携带的表单数据有何异同

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rksbG7pa-1572593770700)(assets/腾讯翻译03.png)]

根据文本内容不同(例如,翻译框输入“你好” 和“中国”后对比表单信息),发现表单的所有字段都相同,唯有要翻译的文本信息和时间戳不一样,其他都相同,那我们可以针对这两个字段,替换成符合要求的内容。

"sourceText": input("请输入要翻译的内容")
# 时间戳
# 根据经验,15或16开头的10-15位数字,一般优先考虑是时间戳
"sessionUuid": "translate_uuid" + str(int(time.time() * 1000))

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LARKNNKG-1572593770700)(assets/时间戳.png)]

尝试用POST方式发送请求

import json
import requests
import time



# post请求的url地址, 通过浏览器抓包获取
base_url = "https://fanyi.qq.com/api/translate"

headers = {
    "Accept": "application/json, text/javascript, */*; q=0.01",
    "Connection": "keep-alive",
    "Content-Length": "294",
    "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
    "Cookie": "fy_guid=94af8e98-08f4-49a2-9562-1c93b5299969; qtv=c03f8898e63faaf1; qtk=c0VhySUiXAQjye4yzCLCCnS2VJiX3nO+PguX/CLuhKsDkPu2+aN5vr0fr0/6hfpi+jVIS4Z0Ys7bm4xK1jsYymyeF3qbhP1xI3kbKmqf1UBe/TnrdmhbwkYPdmjP61aqIZfIN89ZyLDagGo2fjNESg==; openCount=1; gr_user_id=6edf2548-7b4e-4c34-9c05-6831c2ebb552; 8507d3409e6fad23_gr_session_id=8577a037-6ae8-4d6e-b898-3eb27a84dc16; grwng_uid=d7ac9b1f-70f3-48b1-8e56-a1d004132d66; 8c66aca9f0d1ff2e_gr_session_id=c74395eb-6342-4057-8224-077764c5a5eb; 8507d3409e6fad23_gr_session_id_8577a037-6ae8-4d6e-b898-3eb27a84dc16=false",
    "Host": "fanyi.qq.com",
    "Origin": "https://fanyi.qq.com",
    "Referer": "https://fanyi.qq.com/",
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36",
    "X-Requested-With": "XMLHttpRequest"
}

# 需要传递的表单数据
form_data = {
    "source": "auto",
    "target": "auto",
    # 翻译的内容
    "sourceText": input("请输入要翻译的内容"),
    "qtv": "c03f8898e63faaf1",
    "qtk": "c0VhySUiXAQjye4yzCLCCnS2VJiX3nO+PguX/CLuhKsDkPu2+aN5vr0fr0/6hfpi+jVIS4Z0Ys7bm4xK1jsYymyeF3qbhP1xI3kbKmqf1UBe/,TnrdmhbwkYPdmjP61aqIZfIN89ZyLDagGo2fjNESg==",
    # 时间戳
    "sessionUuid": "translate_uuid" + str(int(time.time() * 1000))

}

def send_request():
    
    # requests是urllib的封装
    response2 = requests.post(base_url, data=form_data, headers=headers)

    return response2

def parse_response(response):
    # 获取响应字符串
    str_content = response.content.decode("utf-8")
    # 将响应字符串转为Python数据类型
    dict_json = json.loads(str_content)
    print("翻译结果", dict_json["translate"]["records"][0]["targetText"])

def main():
    response2 = send_request()
    parse_response(response2)


if __name__ == '__main__':

    main()


问题:GET和POST的区别?

  • GET方式是直接以链接形式访问,链接中包含了所有的参数,服务器端用Request.QueryString获取变量的值。如果包含了密码的话是一种不安全的选择,不过你可以直观地看到自己提交了什么内容。
    requests是urllib的封装
    response2 = requests.post(base_url, data=form_data, headers=headers)
return response2

def parse_response(response):
# 获取响应字符串
str_content = response.content.decode(“utf-8”)
# 将响应字符串转为Python数据类型
dict_json = json.loads(str_content)
print(“翻译结果”, dict_json[“translate”][“records”][0][“targetText”])

def main():
response2 = send_request()
parse_response(response2)

if name == ‘main’:

main()



------

### 问题:GET和POST的区别?

> - GET方式是直接以链接形式访问,链接中包含了所有的参数,服务器端用Request.QueryString获取变量的值。如果包含了密码的话是一种不安全的选择,不过你可以直观地看到自己提交了什么内容。
> - POST则不会在网址上显示所有的参数,服务器端用Request.Form获取提交的数据,在Form提交的时候。但是HTML代码里如果不指定 method 属性,则默认为GET请求,Form中提交的数据将会附加在url之后,以`?`分开与url分开。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值