urllib的使用

urllib时python的一个内置库,一共包含四个模块

1. request-请求

这是最基本的HTTP请求模块,可以模拟请求的发送。就像在浏览器输入URL,按下回车一样,只需要给库方法传入URL以及额外的参数,就可以模拟实现发送请求的过程了

urlopen

发送请求。

urllib.request模块提供了最基本的构造HTTP请求的方法,利用这个模块可以模拟浏览器请求的发起过程,同时它还具有处理授权验证,重定向, 浏览器Cookie以及其他的一些功能

这里以百度为例

说明: 这里只是单纯的使用一下 `urlopen` 所以获取的网页内容并不重要

import urllib.request

response = urllib.request.urlopen('https://www.baidu.com')

print(response.read().decode('utf-8'))

查看返回的响应

import urllib.request

response = urllib.request.urlopen('https://www.baidu.com')

print(type(response))

输出:

复制代码
<class 'http.client.HTTPResponse'>

响应式一个 HTTPResponse 对象,主要包含: read, readinto, getheader, getheaders, fileno,

等方法, 以及 msg, version, status, reason, debuglevel, closed等属性

可以通过上述方法和属性来对网页进行具体操作

import urllib.request

response = urllib.request.urlopen('https://baidu.com')

print(response.status) # 查看状态码

print(response.getheaders()) # 查看相应头信息

print(response.getheader('Server')) # 获取响应头中的 Server 的值

输出:

复制代码
200
[('Bdpagetype', '1'), ('Bdqid', '0x89f2326c001257e1'), ('Content-Length', 省略一大堆
BWS/1.1

urllib.request.urlopen(url, data=None, [timeout, ]*, cafile=None, capath=None, cadefault=False, context=None )

参数说明:

url: 为必传, 网页地址

data: 可选参数。在添加该参数时,需要使用bytes方法将参数转化为字节流编码格式的内容,即bytes类型。另外如果传递了该内容,那么请求方式就不再时 GET 而是 POST 了

import urllib.parse

import urllib.request

data = bytes(urllib.parse.urlencode({'name': 'germey'}), encoding='utf-8')

response = urllib.request.urlopen('https://www.httpbin.org/post', data=data)

print(response.read().decode())

输出:

复制代码
{
  "args": {}, 
  "data": "", 
  "files": {}, 
下面是我们传递的参数:
  "form": {
    "name": "germey"
  }, 
  "headers": {
    "Accept-Encoding": "identity", 
    "Content-Length": "11", 
    "Content-Type": "application/x-www-form-urlencoded", 
    "Host": "www.httpbin.org", 
    "User-Agent": "Python-urllib/3.11", 
    "X-Amzn-Trace-Id": "Root=1-66934f46-661cf2f9337c4b704676c9d8"
  }, 
  "json": null, 
  "origin": "117.152.24.63", 
  "url": "https://www.httpbin.org/post"
}

timeout: 可选参数,设置超时时间,单位为秒,这个参数设置的是请求和响应的总时间。如果需要分别设置可以以元组的形式 例如 (3,2),如果不设置,则使用全局默认时间,这个参数支持HTTP , HTTPS, FTP请求

import urllib.request

response = urllib.request.urlopen('https://httpbin.org/get', timeout=0.1)

print(response.read())

超时报错,输出:

复制代码
URLError: <urlopen error timed out>

也可以使用try except 来捕获异常实现当网页长时间未响应时,跳过或继续等待

import urllib.request

import socket

import urllib.error

try:

response = urllib.request.urlopen('https://httpbin.org/get', timeout=0.1)

except urllib.error.URLError as e:

if isinstance(e.reason, socket.timeout):

print('TIME OUT')

输出:

复制代码
TIME OUT

context: 可选参数,该参数必须是ssl.SSLContext类型, 用来指定SSL的设置

cafile和capath:可选参数, 分别来指定CA证书和其路径

cadefault: 可选参数, 貌似已经弃用了,默认为False

Request

urlopen 只能发送简单的请求,如果要加如 Headers 就需要使用Request来构建请求了

简单使用:

import urllib.request

request = urllib.request.Request('https://www.baidu.com')

response = urllib.request.urlopen(request)

print(response.read().decode())

构造方法:

class urllib.request.Request(url, data=None, headers={}, origin_req_host=None, unverifiable=False, method=None)

url: 除了 url 是必传参数外, 其余都是可选参数

data: 要传的数据, 必须是bytes类型,如果是字典,可以先用urllib.parse模块中的urlencode方法进行编码

headers: 是一个字典,这就是请求头,在我们构造请求时,可以通过headers参数直接构造此项,也可以通过调用请求实例的add_header方法添加

添加请求头常见的方法是通过修改 User-Agent 来伪装浏览器,默认的 User-Agent 是 Python-urllib

origin_req_host: 是指请求方的host名称 或者 IP地址

unverifiable: 表示请求是否是无法验证的,默认值是False, 意思是用户没有足够的权限来接收这个请求的结果

method: 是一个字符串, 用来指定请求时使用的方法 例如 GET POST PUT 等

简单使用

from urllib import request, parse

url = 'https://www.httpbin.org/post'

headers = {'User-Agen': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36',

'Host': 'www.httpbin.org'}

dict = {'name': 'germey'}

data = bytes(parse.urlencode(dict), encoding='utf-8')

req = request.Request(url=url, data=data, headers=headers, method='POST')

response = request.urlopen(req)

print(response.read().decode('utf-8'))

通过add_header 方法添加 headers

req = request.Request(url=url, data=data, method='POST')

req.add_header('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36')

Handler

Handler可以理解为各种处理器,有专门处理登录验证,处理Cookie, 处理代理设置利用这些Handler几乎可以实现HTTP请求中的所有功能

urllib.request模块中的BaseHandler类是所有Handler类的父类,它提供了最基本的方法

会有各种子类继承BaseHandler类

  1. HTTPDefaultErrorHandler 用来处理HTTP中的响应错误,所有错误都会抛出HTTPError类型异常

  2. HTTPRedirectHandler 用来处理重定向

  3. HTTPCookieProcessor 用于处理Cookie

  4. ProxyHandler 用于处理代理,代理默认为空

  5. HTTPPasswordMgr 用于管理密码,它维护着用户名密码的对照表

  6. HTTPBasicAuthHandler 用于管理认证,如果一个链接在打开时需要认证,那么可以用这个类来解决认证问题

验证

在访问网站时,可能会弹出需要登录的窗口,遇到这种情况,就表示这个网站启用了基本身份认证,这是一种登录验证的方式,爬虫可以借助HTTPBasicAuthHandler模块来模拟登录

简单示例: 示例网站: https://ssr3.scrape.center

from urllib.request import HTTPPasswordMgrWithDefaultRealm, HTTPBasicAuthHandler,build_opener

from urllib.error import URLError

username = 'admin'

password = 'admin'

url = 'https://ssr3.scrape.center/'

p = HTTPPasswordMgrWithDefaultRealm()

p.add_password(None, url, username, password)

auth_handler = HTTPBasicAuthHandler(p)

opener = build_opener(auth_handler)

try:

result = opener.open(url)

html = result.read().decode('utf-8')

print(html)

except URLError as e:

print(e.reason)

代码说明:

这里首先实例化了一个 HTTPBasicAuthHandler 对象 auth_handler 其参数是 HTTPPasswordMgrWithDefaultRealm 对象, 它利用 add_password方法添加了 用户名和密码

这样就建立了一个用来处理验证的Handler类

然后将刚建立的auth_handler 类 当作参数传入 bulid_opener方法, 构建一个Opener,这个Opener 发送请求时,就相当于验证成功了

最后利用Opener 类中中的open 方法打开链接,即可完成验证,这里获取的结果就是验证成功后的页面源码内容

代理

做爬虫的时候免不了要使用代理,如果要添加代理可以这样做

from urllib.error import URLError

from urllib.request import ProxyHandler, build_opener

proxy_handler = ProxyHandler({

'http': 'http://127.0.0.1:8080',

'https': 'https://127.0.0.1:8080'

})

openr = build_opener('https://www.baidu.com')

try:

response = opener.open('https://www.baidu.com')

print(response.read().decode('utf-8'))

except URLError as e:

print(e.reason)

首先上面的代码时不能运行的,因为代理使用的时本地地址, 需要更改代理IP

代码说明:

这里现在本地搭建了一个代理,并让其运行在8080端口上

上面使用了ProxyHandler, 其参数是一个字典,键名是协议类型(HTTP 或 HTTPS等)键是代理的链接,可以添加多个代理

然后利用这个Handler 和 build_opener 方法构建了一个Opener 之后发送请求即可

要想处理Cookie 首先要获取Cookie

示例代码:

获取Cookie

import http.cookiejar, urllib.request

cookie = http.cookiejar.CookieJar()

handler = urllib.request.HTTPCookieProcessor(cookie)

opener = urllib.request.build_opener(handler)

response = opener.open('https://www.baidu.com')

for item in cookie:

print(item.name + '=' + item.value)

首先必须声明一个Cookie对象,然后利用HTTPCookieProcessor构建一个Handler, 最后利用build_opener 方法构建一个Opener , 执行open即可

保存Cookie

保存Cookie

import urllib.request, http.cookiejar

filename = 'cookie.txt'

cookie = http.cookiejar.MozillaCookieJar(filename)

handler = urllib.request.HTTPCookieProcessor(cookie)

opener = urllib.request.build_opener(handler)

response = opener.open('https://www.baidu.com')

cookie.save(ignore_discard=True, ignore_expires=True)

这里需要将CookieJar 换成 MozillaCookieJar, 它会在生成文件时用到, 是CookieJar的子类,可以用来处理跟Cookie和文件相关的事件, 例如读取和保存Cookie, 可以将Cookie保存成Mozilla型浏览器的Cookie格式

另外, LWPCookieJar 同样也可以读取和保存Cookie, 只是Cookie文件的保存格式和MozillaCookieJar不一样,它会保存成 LWP(libwww-perl)格式

cookie = http.cookiejar.LWPCookieJar(filename)

读取Cookie 文件 这里以 LWPCookieJar格式为例

读取

import urllib.request, http.cookiejar

cookie = http.cookiejar.LWPCookieJar()

cookie.load('cookie.txt', ignore_discard=True, ignore_expires=True)

handler = urllib.request.HTTPCookieProcessor(cookie)

opener = urllib.request.build_opener(handler)

response = opener.open('https://www.baidu.com')

print(response.read().decode())

这里只是演示读取cookie文件, 所以不要纠结从百度读取了什么,因为这里没有设置headers所以不能读取到真正的内容

这里调用了load 方法来读取本地的Cookie文件, 获取了Cookie的内容,这样做的前提是我们实现生成LWPCookieJar格式的cookie, 并保存了文件,读取cookie后,使用同样的方法构建handler类和opener类 即可完成操作

2. error-异常

异常处理模块。如果出现请求异常,那么我们可以捕获这些异常,然后进行重试或者其他操作来保证程序不会意外终止

URLError

URLErroe类来自urllib库中error模块, 继承自OSError类, 是error异常模块的基类,由request模块产生的异常都可以通过捕获这个类来处理

它具有一个属性 reason 即返回错误的原因

示例:

from urllib import request, error

try:

response = request.urlopen('https://www.baidu.com')

response = request.urlopen('htttps://cuiqingcai.com/404')

print('111')

except error.URLError as e:

print('not Found')

print(e.reason)

输出:

复制代码
unknown url type: htttps

这里第一运行的时候报错了, 切换网址运行之后,再切换回来又好了,不明所以

HTTPError

HTTPError 是 URLError 的子类, 专门用来处理HTTP请求的错误, 例如认证请求失败等,

有如下三个属性:

code: 返回HTTP状态码

reason: 同父类一样,返回错误的原因

headers: 返回请求头

示例:

from urllib import request, error

try:

response = request.urlopen('https://www.baidu.com')

response = request.urlopen('htttps://cuiqingcai.com/404')

except error.HTTPError as e:

print('not Found')

print(e.reson, e.code, e.headers, sep='\n')

except error.URLError as e:

print(e.reason)

else:

print('Request Successfully')

这里并不能正确的捕获到HTTPError的异常, 暂时不知道原因

有的时候 reason 返回的不一定是字符串也有可能是一个对象

这个时候可以用 isinstance 方法判断类型, 做出更详细的异常判断

from urllib import request, error

import socket

try:

response = request.urlopen('https://www.baidu.com', timeout=0.01)

response = request.urlopen('htttps://cuiqingcai.com/404')

except error.HTTPError as e:

print('not Found')

print(e.reson, e.code, e.headers, sep='\n')

except error.URLError as e:

print(e.reason)

if isinstance(e.reason, socket.timeout):

print('TIME OUT')

else:

print('Request Successfully')

3. parse-解析链接

一个工具模块。它提供了很多URL的处理方法,例如拆分,解析,合并等

它支持如下协议的URL处理:

file, ftp, gopher, hdl, http, https, imap, mailto, mms, news, nntp, prospero, rsync, rtsp, rtspu, sftp, sip, sips, snews, svn, svn+ssh, telnet, wais

urlparse

该方法可以实现URL的识别和分段

示例:

from urllib.parse import urlparse

result = urlparse('https://www.baidu.com/index.html;user?id=5#comment')

print(type(result))

print(result)

输出:

复制代码
<class 'urllib.parse.ParseResult'>
ParseResult(scheme='https', netloc='www.baidu.com', path='/index.html', params='user', query='id=5', fragment='comment')

可以看到解析的对象是一个 ParseResult类型的对象,包含六部分,分别是: scheme, netloc, path, params, query 和 fragment

从解析的地址可以看出urlparse 方法再解析URL时有特定的分割符,:// 前面的内容是scheme, 代表协议, 第一个 / 前面是netloc, 即域名; 后面是path, 即访问路径, 分号 ; 后面是params, 代表参数, 问号 ? 后面是查询条件 query , 一般用作GET类型的URL, 井号 # 后面是锚点 fragment,用于直接定位页面内部的下拉位置

于是可以得出一个标准的链接格式:

scheme://netloc/path;params?query#fragment

urlparse API:

urllib.parse.urlparse(urlstring, scheme='', allow_fragments=True)

urlstring: 必填, 待解析的URL

scheme: 这是默认的协议(如 http 或 https 等) 如果待解析的URL没有带协议信息,则取默认

示例:URL不带协议

result = urlparse('www.baidu.com/index.html;user?id=5#comment', scheme='https')

print(result)

输出:

复制代码
ParseResult(scheme='https', netloc='', path='www.baidu.com/index.html', params='user', query='id=5', fragment='comment')

示例:URL带协议

result = urlparse('https://www.baidu.com/index.html;user?id=5#comment', scheme='http')

print(result)

输出:

复制代码
ParseResult(scheme='https', netloc='www.baidu.com', path='/index.html', params='user', query='id=5', fragment='comment')

从上面两个例子可以看出, scheme 参数只在 URL不带协议的时候生效

allow_fragments: 是否忽略fragment。 如果此项被设置为False, 那么fragment部分就会被忽略,它就会被解析为 path, params 或者 query 的一部分, 而 fragment部分为空

示例:设置allow_fragments为False

result = urlparse('https://www.baidu.com/index.html;user?id=5#comment', allow_fragments=False)

print(result)

输出:fragment 解析到了 query中

复制代码
ParseResult(scheme='https', netloc='www.baidu.com', path='/index.html', params='user', query='id=5#comment', fragment='')

示例:URL中不带parmas和 query

result = urlparse('https://www.baidu.com/index.html#comment', allow_fragments=False)

print(result)

输出:fragment 解析到了 path 中

复制代码
ParseResult(scheme='https', netloc='www.baidu.com', path='/index.html#comment', params='', query='', fragment='')

返回结果 ParseResult 实际上是一个元组, 既可以用属性名获取其内容,也可用索引获取

示例:

result = urlparse('https://www.baidu.com/index.html;user?id=5#comment', allow_fragments=False)

print(result.scheme, result[0], result.netloc, result[1], sep='\n')

输出:

复制代码
https
https
www.baidu.com
www.baidu.com

urlunparse

有了urlparse 就有 urlunparse , 用来构造URL。 这个方法接收的参数是要给可迭代对象,其长度必须是6, 否则会抛出参数量不足或过多的异常

示例:

from urllib.parse import urlunparse

data = ['https', 'www.baidu.com', 'index.html', 'user', 'a=6', 'comment']

print(urlunparse(data))

输出:

https://www.baidu.com/index.html;user?a=6#comment

这里的参数data 用了列表类型, 也可以用其他类型, 例如元组或特定的数据结构

urlsplit

这个方法和urlparse 很相似, 只不过它不在解析 parmas 这一部分(parmas 会合并到 path中), 只返回 5 个结果

示例:

from urllib.parse import urlsplit

result = urlsplit('https://www.baidu.com/index.html;user?id=5#comment')

print(result)

输出:

复制代码
SplitResult(scheme='https', netloc='www.baidu.com', path='/index.html;user', query='id=5', fragment='comment')

返回的结果是一个 SplitResult, 也是一个元组 ,可以通过索引和属性名获取内容

示例:

from urllib.parse import urlsplit

result = urlsplit('https://www.baidu.com/index.html;user?id=5#comment')

print(result.scheme, result[0])

输出:

复制代码
https https

urlunsplit

与 urlunparse 方法类似, 这也是将链接的各个部分组合成完整链接的方法, 传入的参数是一个可迭代对象, 如列表, 元组等。 唯一区别是,这里参数长度必须是 5

示例:

from urllib.parse import urlunsplit

data = ['https', 'www.baidu.com', 'index.html', 'a=6', 'comment']

print(urlunsplit(data))

输出:

复制代码
https://www.baidu.com/index.html?a=6#comment

urljoin

urlunparse 和 urlunsplit 方法都能完成链接的合并, 但都有特定的长度,链接的每一部分都要清晰的分开

而 urljoin 方法 我们可以提供一个 base_url 链接(基础链接) 作为第一个参数, 将新的链接作为第二个参数, urljoin 会分析 base_url 的 scheme, netloc, path 这三个内容, 并对新链接缺失的部分进行补充, 最后返回结果

示例:

from urllib.parse import urljoin

print(urljoin('https://www.baidu.com', 'FAQ.html'))

print(urljoin('https://www.baidu.com', 'https://cuiqingcai.com/FAQ.html'))

print(urljoin('https://www.baidu.com/about.html', 'https://cuiqingcai.com/FAQ.html'))

print(urljoin('https://www.baidu.com/about.html', 'https://cuiqingcai.com/FAQ.html?question=2'))

print(urljoin('https://www.baidu.com?wd=abc', 'https://cuiqingcai.com/index.php'))

print(urljoin('https://www.baidu.com', '?category=2#comment'))

print(urljoin('www.baidu.com', '?category=2#comment'))

print(urljoin('www.baidu.com', '?category=2'))

输出:

复制代码
https://www.baidu.com/FAQ.html
https://cuiqingcai.com/FAQ.html
https://cuiqingcai.com/FAQ.html
https://cuiqingcai.com/FAQ.html?question=2
https://cuiqingcai.com/index.php
https://www.baidu.com?category=2#comment
www.baidu.com?category=2#comment
www.baidu.com?category=2

可以发现, base_url 提供了三项内容: scheme, netloc 和 path 如果新链接里不存在这三项,

就予以补充, 如果存在,就用新链接里面的,base_url 中的不起作用

urlenclde

它再构造GET请求的时候很有用

示例:

from urllib.parse import urlencode

params = {

'name' : 'germey',

'age': 25

}

base_url = 'https://www.baidu.com?'

url = base_url + urlencode(params)

print(url)

输出:

复制代码
https://www.baidu.com?name=germey&age=25

parse_qs

有了序列化,就有反序列化。 利用parse_qs 方法, 可以将一个GET请求参数转换成字典

示例:

from urllib.parse import parse_qs

query = 'name=germey&age=25'

print(parse_qs(query))

输出:

复制代码
{'name': ['germey'], 'age': ['25']}

parse_qsl

parse_qsl 方法将参数转化为 由 元组组成的列表

示例:

from urllib.parse import parse_qsl

query = 'name=germey&age=25'

print(parse_qsl(query))

输出:

复制代码
[('name', 'germey'), ('age', '25')]

quote

该方法可以将内容转化为 URL 编码的格式。 当URL中带有中文参数时, 有可能会导致乱码问题,此时用 quote 方法可以将中文字符转化为URL 编码

示例:

from urllib.parse import quote

keyword = '壁纸'

url = 'https://www.baidu.com/s?wd=' + quote(keyword)

print(url)

输出:

复制代码
https://www.baidu.com/s?wd=%E5%A3%81%E7%BA%B8

4. robotparser-分析Robots协议

主要用来识别网站的robots.txt文件,然后判断哪些网站可以爬,哪些网站不可以爬,用的很少

Robots协议: 也称为 爬虫协议 ,机器人协议,全名为 网络爬虫排除标准,用来告诉引擎哪些页面可以抓取,哪些不可以。

它通常是一个叫做robots.txt的文本文件, 一般放在网站的根目录下

搜素爬虫在访问一个站点的时候, 首先会检查这个站点的根目录下是否存在robots.txt 文件, 如果存在,就会根据其中定义的爬取范围来爬取,如果没有,则会爬取所有可直接访问的页面

robots.txt文件示例:

User-agent: *

Disallow: /

Allow: /public/

这里限定了所有搜素爬虫只能爬取public目录。

User-agent: 描述了搜索爬虫的名称, 设置为* 代表所有爬虫,也可以指定名称

例如: User-agent: Baiduspider

这代表设置规则对百度爬虫有效,可以设置多个,但最少要有一条

Disallow: 指定了不允许爬取的目录, 设置为 / 代表不允许爬取所有页面

Allow: 一般不会单独使用, 会和 Disallow 一起用, 用来排除某些限制。上面设置为 /public/ , 结合 Disallow 的设置, 表示所有页面都不允许爬取, 但可以爬取 public 目录

robots.txt 也可以是空文件

一些爬虫名称

|-------------|-------|
| BaiduSpider | 百度 |
| Googlebot | 谷歌 |
| 360Spider | 360搜素 |

robotparser

了解了 Robots协议之后, 就可以使用 robotparser 模块来解析 robots.txt文件了。该模块为我们提供了一个类: RobotFileParser, 它可以根据一个网站的robots.txt文件来判断一个爬取爬虫是否由权限爬取这个网页

这个方法只需要在构造方法中传入robots.txt 文件的链接即可

urllib.robotparser.RobotFileParser(url='')

也可以不在声明中传入链接, 就让其默认为空, 最后再使用 set_url() 方法设置也可以

下面列出 RobotFileParser 的几个常用方法

set_url: 用来设置 robots.txt 文件的链接。 如果再创建的时候传入了,就不需要了

read: 读取 robots.txt 文件进行分析。 注意:这个方法执行读取和分析操作, 如果不调用这个方法,那么接下来的操作都会是 False,这个方法不返回任何内容, 但执行了读取操作

parse: 用来解析 robots.txt 文件, 传入其中的参数是 robots.txt 某些行的内容,它会按照 robots.txt 的语法规则来分析这些内容

can_fetch: 该方法由两个参数,第一个是User-Agent, 第二个是要抓取的URL。返回的结果是True或False 表示搜素引擎是否可以抓取这个URL

mtime: 返回上次抓取和分析 robots.txt 文件的时间

modified: 设置当前时间为上次抓取的时间

示例:

from urllib.robotparser import RobotFileParser

rp = RobotFileParser()

rp.set_url('https://www.baidu.com/robots.txt')

rp.read()

print(rp.can_fetch('Baiduspider', 'https://www.baidu.com'))

print(rp.can_fetch('Baiduspider', 'https://www.baidu.com/homepage/'))

print(rp.can_fetch('Googlebot', 'https://www.baidu.com/homepage/'))

传递URL的方法也可以为:

rp = RobotFileParser('https://www.baidu.com/robots.txt')

输出:

复制代码
True
True
False

由此可以看出,百度限制了 谷歌对 homepage的抓取,而自己却是可以抓取的

这里同样可以使用 parse 方法执行对 robots.txt 的读取和分析

示例:

from urllib.robotparser import RobotFileParser

from urllib.request import urlopen

rp = RobotFileParser()

rp.parse(urlopen('https://www.baidu.com/robots.txt').read().decode('utf-8').split('\\n'))

print(rp.can_fetch('Baiduspider', 'https://www.baidu.com'))

print(rp.can_fetch('Baiduspider', 'https://www.baidu.com/homepage/'))

print(rp.can_fetch('Googlebot', 'https://www.baidu.com/homepage/'))

输出:

复制代码
True
True
False

可以看出,结果是一样的

相关推荐
请叫我雷轰1 小时前
百度主动推送可以提升抓取,它能提升索引量吗?
百度·dubbo
supercool71 天前
SpringBoot(9)-Dubbo+Zookeeper
spring boot·dubbo·java-zookeeper
菜鸟的梦幻日记2 天前
IEEE(常用)参考文献引用格式详解 | LaTeX参考文献规范(IEEE Trans、Conf、Arxiv)| 期刊会议名缩写查询
dubbo·template method
向阳12185 天前
Dubbo RPC线程模型
网络协议·rpc·dubbo
刘Java5 天前
Dubbo 3.x源码(24)—Dubbo服务引用源码(7)接口级服务发现订阅refreshInterfaceInvoker
java·rpc·服务发现·dubbo
刘Java5 天前
Dubbo 3.x源码(25)—Dubbo服务引用源码(8)notify订阅服务通知更新
java·服务器·rpc·dubbo
向阳12186 天前
Dubbo 使用轻量的 Java SDK 开发 RPC Server 和 Client
java·rpc·dubbo
程序员小潘8 天前
Dubbo分布式日志跟踪实现
分布式·dubbo
问道飞鱼9 天前
【微服务知识】开源RPC框架Dubbo入门介绍
微服务·rpc·开源·dubbo
幂简集成9 天前
如何一步步获得文心一言API密钥
dubbo·api·文心一言