基础库urllib的使用

学习爬虫,其基本的操作便是模拟浏览器向服务器发出请求,那么我们需要从哪个地方做起呢?请求需要我们自己构造吗?我们需要关心请求这个数据结构怎么实现吗?需要了解 HTTP、TCP、IP层的网络传输通信吗?需要知道服务器如何响应以及响应的原理吗?

可能你无从下手,不过不用担心,Python的强大之处就是提供了功能齐全的类库来帮助我们实现这些需求。最基础的 HTTP库有 urllib、requests、httpx等。

拿 urllib 这个库来说,有了它,我们只需要关心请求的链接是什么,需要传递的参数是什么,以及如何设置可选的请求头,而无须深人到底层去了解到底是怎样传输和通信的。有了urllib 库,只用两行代码就可以完成一次请求和响应的处理过程,得到网页内容,是不是感觉方便极了?

本篇博客我们先理解urllib的使用。

urllib简介

首先介绍一个 Python 库,叫作 urllib,利用它就可以实现 HTTP 请求的发送,而且不需要关心 HTTP协议本身甚至更底层的实现,我们要做的是指定请求的 URL、请求头、请求体等信息。此外 urllib 还可以把服务器返回的响应转化为Python对象,我们通过该对象便可以方便地获取响应的相关信息,如响应状态码、响应头、响应体等。

在Python2中,有urllib 和urllib2 两个库来实现HTTP请求的发送。而在Python3中,urllib2库已经不存在了,统一为了 urllib。而且现在基本上没有人使用Python2了。

首先,我们了解一下 urllib 库的使用方法,它是 Python 内置的 HTTP 请求库,也就是说不需要额外安装,可直接使用。urllib库包含如下4个模块。

  • request:这是最基本的 HTTP 请求模块,可以模拟请求的发送。就像在浏览器里输人网址然后按下回车一样,只需要给库方法传人 URL以及额外的参数,就可以模拟实现发送请求的过程了。
  • error:异常处理模块。如果出现请求异常,那么我们可以捕获这些异常,然后进行重试或其他操作以保证程序运行不会意外终止。
  • parse:一个工具模块。提供了许多 URL的处理方法,例如拆分、解析、合并等。
  • robotparser:主要用来识别网站的 robots.txt 文件,然后判断哪些网站可以爬,哪些网站不可以,它其实用得比较少。

发送请求

使用 urllib 库的 request 模块,可以方便地发送请求并得到响应。我们先来看下它的具体用法。

  • urlopen

ur1lib.request 模块提供了最基本的构造 HTTP请求的方法,利用这个模块可以模拟浏览器的请求发起过程,同时它还具有处理授权验证(Authentication)、重定向(Redirection)、浏览器 Cookie 以及其他一些功能。
下面我们体会-下 request 模块的强大之处。这里以百度官网为例,我们把这个网页抓取下来:

import urllib.requestresponse = urllib.request.urlopen('https://www.python.org')
print(response.read().decode('utf-8'))

一运行直接报错了:

Traceback (most recent call last):File "D:\projects\scrapy-demo\main.py", line 4, in <module>print(response.read().decode('utf-8'))
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x8b in position 1: invalid start byte

也就是我们请求回来的格式并不是utf-8,那么我们需要看看它源码中的编码格式究竟是什么:

import urllib.request
response = urllib.request.urlopen('https://www.python.org')
content_encoding = response.getheader('Content-Encoding')
print(content_encoding)

结果是:

gzip

很早之前这个网页返回的就是utf-8,现在格式变了,那么我们只要解压就可以

import urllib.request
import gzipresponse = urllib.request.urlopen('https://www.python.org')
data = gzip.decompress(response.read())
print(data.decode('utf-8'))

运行结果如下:
在这里插入图片描述

这里我们只用了几行代码,便完成了 Python 官网的抓取,输出了其网页的源代码。得到源代码之后,我们想要的链接、图片地址、文本信息不就都可以提取出来了吗?
接下来,看看返回的响应到底是什么。利用type 方法输出响应的类型:

import urllib.requestresponse = urllib.request.urlopen('https://www.python.org')
print(type(response))

输出结果如下:

<class 'http.client.HTTPResponse'>

可以看出,响应是一个 HTTPResposne 类型的对象,主要包含read、readinto、getheader、getheaders、fileno 等方法,以及msg、version、status、reason、debuglevel、closed 等属性。

得到响应之后,我们把它赋值给response 变量,然后就可以调用上述那些方法和属性,得到返回结果的一系列信息了。

例如,调用 read 方法可以得到响应的网页内容、调用 status 属性可以得到响应结果的状态码(200代表请求成功,404代表网页未找到等)。

利用最基本的 urlopen 方法,已经可以完成对简单网页的 GET请求抓取。如果想给链接传递一些参数,又该怎么实现呢?首先看一下urlopen 方法的 API:

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

可以发现,除了第一个参数用于传递 URL之外,我们还可以传递其他内容,例如 data(附加数据)、timeout(超时时间)等。
接下来就详细说明一下 urlopen 方法中几个参数的用法。

  • data参数

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

下面用实例来看一下:

import urllib.parse
import urllib.requestdata = bytes(urllib.parse.urlencode({'name': 'germey'}), encoding='utf-8')
response = urllib.request.urlopen('https://www.httpbin.org/post', data=data)
print(response.read().decode('utf-8'))

这里我们传递了一个参数 name,值是germey,需要将它转码成 bytes 类型。转码时采用了 bytes 方法该方法的第一个参数得是 str(字符串)类型,因此用 urllib.parse 模块里的 urlencode 方法将字典参数转化为字符串;第二个参数用于指定编码格式,这里指定为 utf-8。

此处我们请求的站点是 www.httpbin.org,它可以提供 HTTP 请求测试。本次我们请求的 URL 为https://www,.htpbin.org/post,这个链接可以用来测试 POST 请求,能够输出请求的一些信息,其中就包含我们传递的 data 参数。

上面实例运行的结果如下:

{"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.9", "X-Amzn-Trace-Id": "Root=1-6759abf8-40e4809d78505a460c2a2298"}, "json": null, "origin": "36.163.167.171", "url": "https://www.httpbin.org/post"
}

可以发现我们传递的参数出现在了form字段中,这表明是模拟表单提交,以 POST方式传输数据。

  • timeout参数

timeout 参数用于设置超时时间,单位为秒,意思是如果请求超出了设置的这个时间,还没有得到响应,就会抛出异常。如果不指定该参数,则会使用全局默认时间。这个参数支持 HTTP、HTTPS.FTP 请求。

下面用实例来看一下:

import urllib.requestresponse = urllib.request.urlopen('https://www.httpbin.org/get', timeout=0.1)
print(response.read())

运行结果可能如下:

...raise URLError(err)
urllib.error.URLError: <urlopen error timed out>

这里我们设置超时时间为0.1秒。程序运行了0.1秒后,服务器依然没有响应,于是抛出了 URLError异常。该异常属于 urllib.error 模块,错误原因是超时。

因此可以通过设置这个超时时间,实现当一个网页长时间未响应时,就跳过对它的抓取。此外,利用 try except 语句也可以实现,相关代码如下:

import socket
import urllib.request
import urllib.errortry:response = urllib.request.urlopen('https://www.httpbin.org/get', timeout=0.1)
except urllib.error.URLError as e:if isinstance(e.reason, socket.timeout):print('TIME OUT')

这里我们请求了 https://www.httpbin.org/get 这个测试链接,设置超时时间为 0.1 秒,然后捕获到URLError 这个异常,并判断异常类型是 socket.timeout,意思是超时异常,因此得出确实是因为超时而报错的结论,最后打印输出了TIME OUT。

运行结果如下:

TIME OUT

按照常理来说,0.1秒几乎不可能得到服务器响应,因此输出了TIME OUT的提示。
通过设置 timeout 参数实现超时处理,有时还是很有用的。

  • 其他参数

除了 data参数和 timeout 参数,urlopen方法还有 context参数,该参数必须是 ss1.SSLContext 类型,用来指定 SSL的设置。

此外,cafile 和 capath 这两个参数分别用来指定 CA 证书和其路径,这两个在请求 HTTPS 链接时会有用。
cadefault 参数现在已经弃用了,其默认值为 False。
至此,我们讲解了 urlopen 方法的用法,通过这个最基本的方法,就可以完成简单的请求和网页抓取。

  • Request

利用 urlopen 方法可以发起最基本的请求,但它那几个简单的参数并不足以构建一个完整的请求。
如果需要往请求中加入 Headers 等信息,就得利用更强大的 Request 类来构建请求了。

首先,我们用实例感受一下 Request 类的用法:

import urllib.requestrequest = urllib.request.Request('https://python.org')
response = urllib.request.urlopen(request)
print(response.read().decode('utf-8'))

可以发现,我们依然是用urlopen方法来发送请求,只不过这次该方法的参数不再是URL,而是一个 Request 类型的对象。通过构造这个数据结构,一方面可以将请求独立成一个对象,另一方面可更加丰富和灵活地配置参数。
下面我们看一下可以通过怎样的参数来构造 Request类,构造方法如下:

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-ur1lib,我们可以通过修改这个值来伪装浏览器。例如要伪装火狐浏览器,就可以把 User-Agent
设置为:
Mozilla/5.0(X11;U;Linux i686)Gecko/20071127 Firefox/2.0.0.11
第四个参数 origin req_host 指的是请求方的 host 名称或者 IP 地址。
第五个参数 unverifiable 表示请求是否是无法验证的,默认取值是 False,意思是用户没有足够的权限来接收这个请求的结果。例如,请求一个HTML文档中的图片,但是没有自动抓取图像的权限这时 unverifiable 的值就是 True。
第六个参数 method 是一个字符串,用来指示请求使用的方法,例如 GET、POST 和 PUT 等。

下面我们传人多个参数尝试构建 Request 类:

from urllib import request, parseurl = 'https://www.httpbin.org/post'
headers = {'User-Agent': 'Mozilla/4.0(compatible; MSIE 5.5; Windows NT)','Host': 'ww.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'))

这里我们通过 4个参数构造了一个 Request 类,其中的 url即请求 URL,headers 中指定了User-Agent 和 Host,data用 urlencode 方法和 bytes 方法把字典数据转成字节流格式。另外,指定了请求方式为 POST。
运行结果如下:

{"args": {}, "data": "", "files": {}, "form": {"name": "germey"}, "headers": {"Accept-Encoding": "identity", "Content-Length": "11", "Content-Type": "application/x-www-form-urlencoded", "Host": "ww.httpbin.org", "User-Agent": "Mozilla/4.0(compatible; MSIE 5.5; Windows NT)", "X-Amzn-Trace-Id": "Root=1-675c441e-1f5aeb5b3eccd32e6a610f6f"}, "json": null, "origin": "36.163.154.16", "url": "https://ww.httpbin.org/post"
}

观察结果可以发现,我们成功设置了 data、headers 和 method。通过 add header 方法添加 headers 的方式如下:

req =request.Request(url=url,data=data, method='POST')
req.add header('User-Agent', Mozilla/4.0(compatible; MSIE 5.5; Windows NT)')

有了 Request类,我们就可以更加方便地构建请求,并实现请求的发送啦。

  • 高级用法

我们已经可以构建请求了,那么对于一些更高级的操作(例如 Cookie 处理、代理设置等 ),又该怎么实现呢?
此时需要更强大的工具,于是 Handler 登场了。简而言之,Handler 可以理解为各种处理器,有专门处理登录验证的、处理 Cookie 的、处理代理设置的。利用这些Handler,我们几乎可以实现 HTTP请求中所有的功能。
首先介绍一下 urllib.request 模块里的 BaseHandler 类,这是其他所有 Handler 类的父类。它提供了最基本的方法,例如 default open、protocol request 等。

会有各种 Handler 子类继承 BaseHandler类,接下来举几个子类的例子如下。

(1) HTTPDefaultErrorHandler 用于处理 HTTP 响应错误,所有错误都会抛出 HTTPError 类型的异常。
(2) HTTPRedirectHandler 用于处理重定向。
(3) HTTPCookieProcessor 用于处理 Cookie。
(4) ProxyHandler 用于设置代理,代理默认为空。
(5) HTTPPasswordMgr用于管理密码,它维护着用户名密码的对照表。
(6) HTTPBasicAuthHandler 用于管理认证,如果一个链接在打开时需要认证,那么可以用这个类来解决认证问题。

关于这些类如何使用,现在先不急着了解,后面会用实例演示。
另一个比较重要的类是 0penerDirector,我们可以称之为0pener。我们之前用过的 urlopen 方法实际上就是 urllib 库为我们提供的一个 0pener。
那么,为什么要引人 0pener 呢?因为需要实现更高级的功能。之前使用的Request类和 urlopen 类相当于类库已经封装好的极其常用的请求方法,利用这两个类可以完成基本的请求,但是现在我们需要实现更高级的功能,就需要深人一层进行配置,使用更底层的实例来完成操作,所以这里就用到了 Opener。

0pener 类可以提供 open方法,该方法返回的响应类型和 urlopen方法如出一辙。那么,0pener 类和 Handler 类有什么关系呢?简而言之就是,利用 Handler 类来构建 0pener 类。
下面用几个实例来看看 Handler 类和 0pener 类的用法。

  • 验证
    在访问某些网站时,例如 https://ssr3.scrape.center,可能会弹出这样的认证窗口,如下图所示。

在这里插入图片描述

遇到这种情况,就表示这个网站启用了基本身份认证,英文叫作 HTTP Basic Access Authentication.这是一种登录验证方式,允许网页浏览器或其他客户端程序在请求网站时提供用户名和口令形式的身份凭证。

那么爬虫如何请求这样的页面呢?借助 HTTPBasicAuthHandler 模块就可以完成,相关代码如下:

from urllib.request import HTTPBasicAuthHandler, build_opener, HTTPPasswordMgrWithDefaultRealm
from urllib.error import URLErrorusername = '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,其参数是 HTTPPasswordMgr-WithDefaultRealm对象,它利用add_password方法添加用户名和密码,这样就建立了一个用来处理验证的 Handler 类。
然后将刚建立的 auth_handler 类当作参数传人 build_opener 方法,构建一个0pener,这个0pener在发送请求时就相当于已经验证成功了。

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

  • 代理

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

from urllib.error import URLError
from urllib.request import ProxyHandler, build_openerproxy_handler = ProxyHandler({"http": 'http://127.0.0.1:8080', 'https': "https://127.0.0.1:8080"})
opener = build_opener(proxy_handler)
try:response = opener.open('https://www.baidu.com')print(response.read().decode('utf-8'))
except URLError as e:print(e.reason)

这里需要我们事先在本地搭建一个 HTTP 代理,并让其运行在 8080端口上。
上面使用了 ProxyHandler,其参数是一个字典,键名是协议类型(例如 HTTP 或者 HTTPS 等)、键值是代理链接,可以添加多个代理。
然后利用这个 Handler 和 build opener 方法构建了一个 0pener,之后发送请求即可。

  • Cookie
    处理 Cookie 需要用到相关的 Handler。
    我们先用实例来看看怎样获取网站的Cookie,相关代码如下:
import http.cookiejar, urllib.requestcookie = 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]ar 对象。然后需要利用 HTTPCookieProcessor 构建一个 Handler,最后利用 build opener 方法构建 0pener,执行 open 函数即可。

运行结果如下:

BAIDUID=077CDC4722F11FC3E3B03E82251C67DD:FG=1
BAIDUID_BFESS=077CDC4722F11FC3E3B03E82251C67DD:FG=1
H_PS_PSSID=61027_60851_61331_61358_61366_61391_61388_61406
PSTM=1734101145
BD_NOT_HTTPS=1

可以看到,这里分别输出了每个Cookie 条目的名称和值。

既然能输出,那么可不可以输出文件格式的内容呢?我们知道 Cookie 实际上也是以文本形式保存的。因此答案当然是肯定的,这里通过下面的实例来看看:

import urllib.request, http.cookiejarfilename = '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)

这时需要将 Cookie]ar 换成 MozillaCookie]ar,它会在生成文件时用到,是 (ookie]ar 的子类,可以用来处理跟 Cookie 和文件相关的事件,例如读取和保存 Cookie,可以将 Cookie 保存成 Mozilla 型浏览器的 Cookie 格式。
运行上面的实例之后,会发现生成了一个 cookie.txt 文件,该文件内容如下:

# Netscape HTTP Cookie File
# http://curl.haxx.se/rfc/cookie_spec.html
# This is a generated file!  Do not edit..baidu.com	TRUE	/	FALSE	1765637336	BAIDUID	8B9571B632AF777394D83C0368E8AAE7:FG=1
.baidu.com	TRUE	/	TRUE	1765637336	BAIDUID_BFESS	8B9571B632AF777394D83C0368E8AAE7:FG=1
.baidu.com	TRUE	/	FALSE	1765637335	H_PS_PSSID	60277_61027_61099_61219_61245_60853_61367_61390_61388
.baidu.com	TRUE	/	FALSE	3881584983	PSTM	1734101335
www.baidu.com	FALSE	/	FALSE	1734101636	BD_NOT_HTTPS	1

另外,LWPCookie]ar同样可以读取和保存 Cookie,只是 Cookie 文件的保存格式和 MozillaCookie]ar不一样,它会保存成LWP(libwww-perl)格式。

要保存 LWP 格式的 Cookie 文件,可以在声明时就进行修改:

import urllib.request, http.cookiejarfilename = 'cookie.txt'
cookie =http.cookiejar.LWPCookieJar(filename)
handler = urllib.request.HTTPCookieProcessor(cookie)
opener = urllib.request.build_opener(handler)
response = opener.open('https://www.baidu.com')
cookie.save(ignore_discard=True)

此时生成的内容如下:

#LWP-Cookies-2.0
Set-Cookie3: BAIDUID="D8BAD7FD29F9BC81EB78AFCEA14DC816:FG=1"; path="/"; domain=".baidu.com"; path_spec; expires="2025-12-13 14:52:21Z"; version=0
Set-Cookie3: BAIDUID_BFESS="D8BAD7FD29F9BC81EB78AFCEA14DC816:FG=1"; path="/"; domain=".baidu.com"; path_spec; secure; expires="2025-12-13 14:52:21Z"; SameSite=None; version=0
Set-Cookie3: H_PS_PSSID=60275_61027_61098_61218_61243_60853_61325_61360_61372; path="/"; domain=".baidu.com"; path_spec; domain_dot; expires="2025-12-13 14:52:20Z"; version=0
Set-Cookie3: PSTM=1734101540; path="/"; domain=".baidu.com"; path_spec; domain_dot; expires="2092-12-31 18:06:28Z"; version=0
Set-Cookie3: BD_NOT_HTTPS=1; path="/"; domain="www.baidu.com"; path_spec; expires="2024-12-13 14:57:21Z"; version=0

由此看来,不同格式的 Cookie 文件差异还是比较大的。
那么,生成 Cookie 文件后,怎样从其中读取内容并加以利用呢?
下面我们以 LWPCookie]ar 格式为例来看一下:

import urllib.request, http.cookiejarcookie = 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('utf-8'))

可以看到,这里调用 1oad方法来读取本地的 Cookie 文件,获取了Cookie的内容。这样做的前提是我们首先生成了 LWPCookie]ar 格式的 Cookie,并保存成了文件。读取 Cookie 之后,使用同样的方法构建 Handler 类和 0pener 类即可完成操作。
运行结果正常的话,会输出百度网页的源代码。
通过上面的方法,我们就可以设置绝大多数请求的功能。

处理异常

我们已经了解了如何发送请求,但是在网络不好的情况下,如果出现了异常,该怎么办呢?这时要是不处理这些异常,程序很可能会因为报错而终止运行,所以异常处理还是十分有必要的。

urllib 库中的 error 模块定义了由 request 模块产生的异常。当出现问题时,request 模块便会抛出 error 模块中定义的异常。

  • URLError

URLError 类来自 urllib库的error 模块,继承自 0SError类,是error 异常模块的基类,由 request模块产生的异常都可以通过捕获这个类来处理。
它具有一个属性 reason,即返回错误的原因。
下面用一个实例来看一下:

from urllib import request, errortry:response = request.urlopen('https://cuiqingcai.com/404')
except error.URLError as e:print(e.reason)

我们打开了一个不存在的页面,照理来说应该会报错,但是我们捕获了URLError 这个异常,运行结果如下:

Not Found

程序没有直接报错,而是输出了错误原因,这样可以避免程序异常终止,同时异常得到了有效处理。

  • HTTPError

HTTPError 是URLError 的子类,专门用来处理HTTP请求错误,例如认证请求失败等。它有如下3个属性。

(1) code:返回 HTTP 状态码,例如 404表示网页不存在,500 表示服务器内部错误等。
(2) reason:同父类一样,用于返回错误的原因。
(3) headers:返回请求头。
下面我们用几个实例来看看:

from urllib import request, errortry:response = request.urlopen('https://cuiqingcai.com/404')
except error.HTTPError as e:print(e.reason, e.code, e.headers, sep='\n')

运行结果如下:

Not Found
404
Connection: close
Content-Length: 9379
Server: GitHub.com
Content-Type: text/html; charset=utf-8
Access-Control-Allow-Origin: *
ETag: "64d39a40-24a3"
Content-Security-Policy: default-src 'none'; style-src 'unsafe-inline'; img-src data:; connect-src 'self'
x-proxy-cache: MISS
X-GitHub-Request-Id: A70E:3CA0C7:3F7B8D:449189:675C4EB2
Accept-Ranges: bytes
Age: 0
Date: Fri, 13 Dec 2024 15:11:54 GMT
Via: 1.1 varnish
X-Served-By: cache-lon420137-LON
X-Cache: MISS
X-Cache-Hits: 0
X-Timer: S1734102715.581747,VS0,VE85
Vary: Accept-Encoding
X-Fastly-Request-ID: aef6c63cad118dac55be2f1269f01bbfaf098acb

依然是打开同样的网址,这里捕获了 HTTPError 异常,输出了reason、code 和 headers 属性。因为URLError 是HTTPError 的父类,所以可以先选择捕获子类的错误,再捕获父类的错误,于是上述代码的更好写法如下:

from urllib import request, errortry:response = request.urlopen('https://cuiqingcai.com/404')
except error.HTTPError as e:print(e.reason, e.code, e.headers, sep='\n')
except error.URLError as e:print(e.reason)
else:print('Request Successfully')

这样就可以做到先捕获 HTTPError,获取它的错误原因、状态码、请求头等信息。如果不是HTTPError 异常,就会捕获URLError 异常,输出错误原因。最后,用else语句来处理正常的逻辑。这是一个较好的异常处理写法。
有时候,reason属性返回的不一定是字符串,也可能是一个对象。再看下面的实例:

import socket
import urllib.request
import urllib.errortry:response = urllib.request.urlopen('https://www.baidu.com', timeout=0.01)
except urllib.error.URLError as e:print(type(e.reason))if isinstance(e.reason, socket.timeout):print('TIME OUT')

这里我们直接设置超时时间来强制抛出 timeout 异常。
运行结果如下:

<class 'socket.timeout'>
TIME OUT

可以发现,reason 属性的结果是 socket.timeout 类。所以这里可以用 isinstance 方法来判断它的类型,做出更详细的异常判断。
本节我们讲述了 error 模块的相关用法,通过合理地捕获异常可以做出更准确的异常判断,使程序更加稳健。

解析链接

前面说过,urllib库里还提供了 parse模块,这个模块定义了处理URL的标准接口,例如实现 URL各部分的抽取、合并以及链接转换。它支持如下协议的URL处理:file、fp、gopher、hdl、htp、https、imap、 mailto、 mms、 news、 nntp、 prospero、 rsync、rtsp、rtspu、sftp、 sip、 sips、 snews、svn、svntssh.telnet 和 wais。

下面我们将介绍 parse 模块中的常用方法,看一下它的便捷之处。

  • urlparse

该方法可以实现 URL的识别和分段,这里先用一个实例来看一下:

from urllib.parse import urlparseresult = urlparse('https://www.baidu.com/index.html;user?id=5#comment')
print(type(result))
print(result)

这里我们利用 urlparse 方法对一个URL进行了解析,然后输出了解析结果的类型以及结果本身。运行结果如下:

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

可以看到,解析结果是一个ParseResult类型的对象,包含6部分,分别是scheme、netloc、path、params、query和fragment。
再观察一下上述实例中的 URL:
https://www.baidu.com/index.html;user?id=5#comment

可以发现,urlparse 方法在解析 URL 时有特定的分隔符。例如://前面的内容就是 scheme,代表协议。第一个/符号前面便是netloc,即域名;后面是path,即访问路径。分号;后面是params,代表参数。问号?后面是査询条件 query,一般用作 GET类型的 URL。井号#后面是锚点 fragment,用于直接定位页面内部的下拉位置。

于是可以得出一个标准的链接格式,具体如下:

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

一个标准的 URL都会符合这个规则,利用 urlparse 方法就可以将它拆分开来。
除了这种最基本的解析方式外,urlparse 方法还有其他配置吗?接下来,看一下它的 API用法:

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

可以看到,urlparse 方法有3个参数。

(1) urlstring:这是必填项,即待解析的 URL。
(2) scheme:这是默认的协议(例如 http 或 https 等 )。如果待解析的 URL 没有带协议信息,就会将这个作为默认协议。我们用实例来看一下:

from urllib.parse import urlparseresult = 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不包含最前面的协议信息,但是通过默认的scheme 参数,返回了结果 https。
假设带上协议信息:

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

运行结果如下:

ParseResult(scheme='http', netloc='www.baidu.com', path='/index.html', params='user', query='id=5', fragment='comment')

可见,scheme 参数只有在 URL,中不包含协议信息的时候才生效。如果 URL中有,就会返回解析出的 scheme。

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

下面我们用实例来看一下:

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

运行结果如下:

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

假设 URL中不包含 params 和 query,我们再通过实例看一下:

from urllib.parse import urlparseresult = urlparse('https://www.baidu.com/index.html#comment', allow_fragments=False)
print(result)

运行结果如下:

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

可以发现,此时 fragment 会被解析为 path 的一部分。

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

from urllib.parse import urlparseresult = urlparse('https://ww.baidu.com/index.html#comment', allow_fragments=False)
print(result.scheme, result[0], result.netloc, result[1], sep='\n')

这里我们分别用属性名和索引获取了scheme 和 netloc,运行结果如下:

https
https
ww.baidu.com
ww.baidu.com

可以发现,两种获取方式都可以成功获取,且结果是一致的。

  • urlunparse

有了 urlparse 方法,相应就会有它的对立方法 urlunparse,用于构造 URL。这个方法接收的参数是一个可迭代对象,其长度必须是6,否则会抛出参数数量不足或者过多的问题。先用一个实例看一下:

from urllib.parse import urlunparsedata = ['https', 'www.baidu.com', 'index.html', 'user', 'a=6', 'comment']
print(urlunparse(data))

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

运行结果如下:

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

这样我们就成功实现了 URL 的构造。

  • urlsplit

这个方法和 urlparse 方法非常相似,只不过它不再单独解析 params 这一部分(params 会合并到path中),只返回5个结果。实例如下:

from urllib.parse import urlsplitresult = 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 urlsplitresult = 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 urlunsplitdata = ['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 这3个内容,并对新链接缺失的部分进行补充,最后返回结果。
下面通过几个实例看一下:

from urllib.parse import urljoinprint(urljoin('https://www.baidu.com', 'FAQ.html'))
print(urljoin('https://www.baidu.com', 'https://cuigingcai.com/FA0.html'))
print(urljoin('https://ww,baidu.com/about.html', 'https://cuigingcai.com/FAQ.html'))
print(urljoin('https://ww.baidu.com/about.html', 'https://cuigingcai.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#comment', '?category=2'))

可以发现,base_url提供了三项内容:scheme、netloc和 path。如果新的链接里不存在这三项就予以补充;如果存在,就使用新的链接里面的,base_url中的是不起作用的

通过 urljoin 方法,我们可以轻松实现链接的解析、拼合与生成。

  • urlencode

这里我们再介绍一个常用的方法–urlencode,它在构造 GET 请求参数的时候非常有用,实例如下:

from urllib.parse import urlencodeparams = {'name': 'germey', 'age': 25}
base_url = 'https://www.baidu.com?'
url = base_url + urlencode(params)
print(url)

这里首先声明了一个字典 params,用于将参数表示出来,然后调用urlencode方法将 params 序列化为 GET请求的参数。
运行结果如下:

https://www.baidu.com?name=germey&age=25

可以看到,参数已经成功地由字典类型转化为 GET请求参数:
urlencode 方法非常常用。有时为了更加方便地构造参数,我们会事先用字典将参数表示出来然后将字典转化为 URL的参数时,只需要调用该方法即可。

  • parse_qs

有了序列化,必然会有反序列化。利用parse_qs 方法,可以将一串 GET请求参数转回字典,实例如下:

from urllib.parse import parse_qsquery = 'name=germey&age=25'
print(parse_qs(query))

运行结果如下:

{'name': ['germey'], 'age': ['25']}

可以看到,URL的参数成功转回为字典类型。

  • parse_qsl

parse qsl方法用于将参数转化为由元组组成的列表,实例如下:

from urllib.parse import parse_qslquery = 'name=germey&age=25'
print(parse_qsl(query))

运行结果如下:

[('name', 'germey'), ('age', '25')]

可以看到,运行结果是一个列表,该列表中的每一个元素都是一个元组,元组的第一个内容是参数名,第二个内容是参数值。

  • quote

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

from urllib.parse import quotekeyword = '壁纸'
url = 'https://www.baidu.com/s?wd=' + quote(keyword)
print(url)

这里我们声明了一个中文的搜索文字,然后用 quote 方法对其进行 URL 编码,最后得到的结果如下:

https://www.baidu.com/s?wd=%E5%A3%81%E7%BA%B8
  • unquote

有了 quote 方法,当然就有 unquote 方法,它可以进行 URL解码,实例如下:

from urllib.parse import unquoteurl = 'https://www.baidu.com/s?wd=%E5%A3%81%E7%BA%B8'
print(unquote(url))

这里的 ur1 是上面得到的 URL编码结果,利用 unquote 方法将其还原,结果如下:

https://www.baidu.com/s?wd=壁纸

可以看到,利用 unquote 方法可以方便地实现解码。

本节我们介绍了 parse 模块的一些常用 URL处理方法。有了这些方法,我们可以方便地实现 URL的解析和构造,建议熟练掌握。

分析Robots协议

利用 urllib 库的 robotparser 模块,可以分析网站的 Robots 协议。我们再来简单了解一下这个模块的用法。

  • Robots协议

Robots协议也称作爬虫协议、机器人协议,全名为网络爬虫排除标准( Robots Exclusion Protocol),用来告诉爬虫和搜索引擎哪些页面可以抓取、哪些不可以。它通常是一个叫作 robots.txt的文本文件,一般放在网站的根目录下。

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

下面我们看一个robots.txt 的样例:

User-agent:*
Disallow:/
Allow: /public/

这限定了所有搜索爬虫只能爬取 public 目录。将上述内容保存成robots.txt文件,放在网站的根目录下,和网站的入口文件(例如index.php、index.html和 index.jsp 等)放在一起。

上面样例中的 User-agent 描述了搜索爬虫的名称,这里将其设置为*,代表 Robots 协议对所有爬取爬虫都有效。例如,我们可以这样设置:

User-agent: Baiduspider

这代表设置的规则对百度爬虫是有效的。如果有多条user-agent记录,则意味着有多个爬虫会受到爬取限制,但至少需要指定一条。

Disallow 指定了不允许爬虫爬取的目录,上例设置为/,代表不允许爬取所有页面。
A11ow一般不会单独使用,会和 Disallow一起用,用来排除某些限制。上例中我们设置为 /public/,结合 Disallow 的设置,表示所有页面都不允许爬取,但可以爬取 public 目录。
下面再来看几个例子。禁止所有爬虫访问所有目录的代码如下:

User-agent:*
Disallow:/

允许所有爬虫访问所有目录的代码如下:

User-agent:*
Disallow:

另外,直接把 robots.txt 文件留空也是可以的。

禁止所有爬虫访问网站某些目录的代码如下:

User-agent:*
Disallow: /private/
Disallow:/tmp/

只允许某一个爬虫访问所有目录的代码如下:

User-agent: WebCrawler
Disallow:
User-agent:*
Disallow:/

以上是 robots.txt 的一些常见写法。

  • 爬虫名称

大家可能会疑惑,爬虫名是从哪儿来的?为什么叫这个名?其实爬虫是有固定名字的,例如百度的爬虫就叫作 BaiduSpider。下表列出了一些常见搜索爬虫的名称及对应的网站。

在这里插入图片描述

  • robotparser

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

该类用起来非常简单,只需要在构造方法里传人robots.txt文件的链接即可。首先看一下它的声明:

urllib.robotparser.RobotFileParser(url='")

当然,也可以不在声明时传人robots.txt文件的链接,就让其默认为空,最后再使用set ur1()方法设置一下也可以。

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

  • set url:用来设置 robots.txt 文件的链接。如果在创建 RobotFileParser 对象时传人了链接就不需要使用这个方法设置了。
  • read:读取 robots.txt 文件并进行分析。注意,这个方法执行读取和分析操作,如果不调用这个方法,接下来的判断都会为False,所以一定记得调用这个方法。这个方法虽不会返回任何内容,但是执行了读取操作。
  • parse:用来解析 robots.txt文件,传人其中的参数是robots.txt文件中某些行的内容,它会按照robots.txt 的语法规则来分析这些内容。
  • can fetch:该方法有两个参数,第一个是User-Agent,第二个是要抓取的 URL。返回结果是 True或 False,表示 User-Agent 指示的搜索引擎是否可以抓取这个 URL。
  • mtime:返回上次抓取和分析 robots.xt文件的时间,这对于长时间分析和抓取robots.txt文件的搜索爬虫很有必要,你可能需要定期检查以抓取最新的 robots.txt 文件。
  • modified:它同样对长时间分析和抓取的搜索爬虫很有帮助,可以将当前时间设置为上次抓取和分析 robots.txt 文件的时间。

下面我们用实例来看一下:

from urllib.robotparser import RobotFileParserrp = 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/'))

这里以百度为例,首先创建了一个 RobotFileParser 对象 p,然后通过 set url 方法设置了robots.txt 文件的链接。当然,要是不用 set ur1方法,可以在声明对象时直接用如下方法设置:

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

接着利用 can fetch 方法判断了网页是否可以被抓取。
运行结果如下:

True
True
False

可以看到,这里我们利用 Baiduspider 可以抓取百度的首页以及 homepage 页面,但是 Googlebot就不能抓取 homepage 页面。

打开百度的 robots.txt 文件,可以看到如下信息:

User-agent: Baiduspider
Disallow: /baidu
Disallow: /s?
Disallow: /ulink?
Disallow: /link?
Disallow: /home/news/data/
Disallow: /bhUser-agent: Googlebot
Disallow: /baidu
Disallow: /s?
Disallow: /shifen/
Disallow: /homepage/
Disallow: /cpro
Disallow: /ulink?
Disallow: /link?
Disallow: /home/news/data/
Disallow: /bhUser-agent: MSNBot
Disallow: /baidu
Disallow: /s?
Disallow: /shifen/
Disallow: /homepage/
Disallow: /cpro
Disallow: /ulink?
Disallow: /link?
Disallow: /home/news/data/
Disallow: /bhUser-agent: Baiduspider-image
Disallow: /baidu
Disallow: /s?
Disallow: /shifen/
Disallow: /homepage/
Disallow: /cpro
Disallow: /ulink?
Disallow: /link?
Disallow: /home/news/data/
Disallow: /bhUser-agent: YoudaoBot
Disallow: /baidu
Disallow: /s?
Disallow: /shifen/
Disallow: /homepage/
Disallow: /cpro
Disallow: /ulink?
Disallow: /link?
Disallow: /home/news/data/
Disallow: /bhUser-agent: Sogou web spider
Disallow: /baidu
Disallow: /s?
Disallow: /shifen/
Disallow: /homepage/
Disallow: /cpro
Disallow: /ulink?
Disallow: /link?
Disallow: /home/news/data/
Disallow: /bhUser-agent: Sogou inst spider
Disallow: /baidu
Disallow: /s?
Disallow: /shifen/
Disallow: /homepage/
Disallow: /cpro
Disallow: /ulink?
Disallow: /link?
Disallow: /home/news/data/
Disallow: /bhUser-agent: Sogou spider2
Disallow: /baidu
Disallow: /s?
Disallow: /shifen/
Disallow: /homepage/
Disallow: /cpro
Disallow: /ulink?
Disallow: /link?
Disallow: /home/news/data/
Disallow: /bhUser-agent: Sogou blog
Disallow: /baidu
Disallow: /s?
Disallow: /shifen/
Disallow: /homepage/
Disallow: /cpro
Disallow: /ulink?
Disallow: /link?
Disallow: /home/news/data/
Disallow: /bhUser-agent: Sogou News Spider
Disallow: /baidu
Disallow: /s?
Disallow: /shifen/
Disallow: /homepage/
Disallow: /cpro
Disallow: /ulink?
Disallow: /link?
Disallow: /home/news/data/
Disallow: /bhUser-agent: Sogou Orion spider
Disallow: /baidu
Disallow: /s?
Disallow: /shifen/
Disallow: /homepage/
Disallow: /cpro
Disallow: /ulink?
Disallow: /link?
Disallow: /home/news/data/
Disallow: /bhUser-agent: ChinasoSpider
Disallow: /baidu
Disallow: /s?
Disallow: /shifen/
Disallow: /homepage/
Disallow: /cpro
Disallow: /ulink?
Disallow: /link?
Disallow: /home/news/data/
Disallow: /bhUser-agent: Sosospider
Disallow: /baidu
Disallow: /s?
Disallow: /shifen/
Disallow: /homepage/
Disallow: /cpro
Disallow: /ulink?
Disallow: /link?
Disallow: /home/news/data/
Disallow: /bhUser-agent: yisouspider
Disallow: /baidu
Disallow: /s?
Disallow: /shifen/
Disallow: /homepage/
Disallow: /cpro
Disallow: /ulink?
Disallow: /link?
Disallow: /home/news/data/
Disallow: /bhUser-agent: EasouSpider
Disallow: /baidu
Disallow: /s?
Disallow: /shifen/
Disallow: /homepage/
Disallow: /cpro
Disallow: /ulink?
Disallow: /link?
Disallow: /home/news/data/
Disallow: /bhUser-agent: *
Disallow: /

不难看出,百度的 robots.txt 文件没有限制 Baiduspider 对百度 homepage 页面的抓取,限制了Googlebot 对 homepage 页面的抓取。

这里同样可以使用 parse 方法执行对 robots.txt 文件的读取和分析,实例如下:

from urllib.request import urlopen
from urllib.robotparser import RobotFileParserrp = 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

本节介绍了 robotparser 模块的基本用法和实例,利用此模块,我们可以方便地判断哪些页面能抓取、哪些页面不能。

总结

本篇博客内容比较多,我们介绍了 urllib库的request、error、parse、robotparser 模块的基本用法这些是一些基础模块,有一些模块的实用性还是很强的,例如我们可以利用 parse 模块来进行 URL,的各种处理,还是很方便的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/64190.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

剑指Offer|day4 LCR 004. 只出现一次的数字 II

LCR 004. 只出现一次的数字 II 给你一个整数数组 nums &#xff0c;除某个元素仅出现 一次 外&#xff0c;其余每个元素都恰出现 **三次 。**请你找出并返回那个只出现了一次的元素。 示例 1&#xff1a; 输入&#xff1a;nums [2,2,3,2] 输出&#xff1a;3提示&#xff1a…

Mysql学习笔记之SQL-1

上篇文章我们介绍了Mysql的安装&#xff0c;这篇文章我们介绍Mysql的操作语言SQL 1. 简介 sql全称&#xff08;Structured Query Language&#xff09;是结构化查询语言&#xff0c;操作关系型数据库的编程语言&#xff0c;定义了一套操作关系型数据库统一标准 2. sql分类 …

埃隆马斯克X-AI发布Grok-2大模型,快来体验~

引言 近年来&#xff0c;人工智能技术的快速发展推动了大语言模型的广泛应用。无论是日常生活中的智能助手&#xff0c;还是行业中的自动化解决方案&#xff0c;大语言模型都扮演着越来越重要的角色。2024年&#xff0c;X-AI推出了新一代的大模型——Grok-2&#xff0c;这款模…

PostgreSQL的学习心得和知识总结(一百六十三)|深入理解PostgreSQL数据库之 GUC参数compute_query_id 的使用和实现

目录结构 注&#xff1a;提前言明 本文借鉴了以下博主、书籍或网站的内容&#xff0c;其列表如下&#xff1a; 1、参考书籍&#xff1a;《PostgreSQL数据库内核分析》 2、参考书籍&#xff1a;《数据库事务处理的艺术&#xff1a;事务管理与并发控制》 3、PostgreSQL数据库仓库…

多线程编程杂谈(上)

问题 线程执行的过程中可以强制退出吗&#xff1f; 主动退出&#xff1f;被动退出&#xff1f; 问题抽象示例 需要解决的问题 g_run 全局变量需要保护吗&#xff1f; 如何编码使得线程中每行代码的执行可被 g_run 控制&#xff1f; 线程代码在被 g_run 控制并 "强制退…

【Git】:企业级开发和多人协作开发啊

目录 多人协作 模拟配置多人协作环境 多人同一分支开发 多人不同分支开发 远程分支删除后的问题 企业级开发模型 系统开发环境 分支设计规范 多人协作 模拟配置多人协作环境 目前&#xff0c;我们所完成的工作如下&#xff1a; 基本完成 Git 的所有本地库的相关操作&#xff…

【Sql优化】数据库优化方法、Explain使用

文章目录 一、金字塔优化模型二、SQL优化的利器&#xff1a;Explain工具1. Explain 的作用2. Explain 的用法 三、SQL优化方法&#xff08;后续文章细讲&#xff09;1. 创建索引减少扫描量2. 调整索引减少计算量3. 索引覆盖4. 干预执行计划5. SQL改写 四、通过 Explain 优化案例…

Deepmotion技术浅析(五):运动追踪

运动追踪是 DeepMotion 动作捕捉和 3D 重建流程中的核心模块之一。该模块的主要任务是在视频序列中跟踪人体的运动轨迹&#xff0c;捕捉人体各部分随时间的变化&#xff0c;并生成连续的 3D 运动数据。DeepMotion 的运动追踪技术结合了计算机视觉、深度学习和物理模拟等方法&am…

Android 系统应用重名install安装失败分析解决

Android 系统应用重名install安装失败分析解决 文章目录 Android 系统应用重名install安装失败分析解决一、前言1、Android Persistent apps 简单介绍 二、系统 persistent 应用直接安装需求分析解决1、系统应用安装报错返回的信息2、分析解决 三、其他1、persistent系统应用in…

使用Nexus3搭建npm私有仓库

一、npm介绍 npm的全称是Node Package Manager&#xff0c;它是一个开放源代码的命令行工具&#xff0c;用于安装、更新和管理Node.js模块。npm是Node.js的官方模块管理器&#xff0c;它允许用户从一个集中的仓库中下载和安装公共的Node.js模块&#xff0c;并将这些模块集成到…

【ChatGPT】解锁AI思维链:如何让机器像人类一样思考?

在人工智能领域&#xff0c;我们一直在追求让机器像人类一样思考。然而&#xff0c;即使是最先进的AI&#xff0c;也常常被诟病缺乏“常识”&#xff0c;难以理解复杂问题&#xff0c;更不用说像人类一样进行逻辑推理和解决问题了。最经常的表现就是遇到不会的地方&#xff0c;…

蓝桥杯刷题——day5

蓝桥杯刷题——day5 题目一题干解题思路一代码解题思路二代码 题目二题干解题思路代码 题目一 题干 给定n个整数 a1,a2,⋯ ,an&#xff0c;求它们两两相乘再相加的和&#xff0c;即&#xff1a; 示例一&#xff1a; 输入&#xff1a; 4 1 3 6 9 输出&#xff1a; 117 题目链…

监测预警智能分析中心建设项目方案

随着科技的不断进步&#xff0c;地理信息与遥感技术在国家治理、环境保护、灾害预警等领域发挥着越来越重要的作用。监测预警智能分析中心的建设&#xff0c;旨在通过集成先进的遥感技术、地理信息系统&#xff08;GIS&#xff09;、大数据分析和人工智能&#xff08;AI&#x…

【漫话机器学习系列】009.词袋模型(Bag of Words)

词袋模型&#xff08;Bag of Words, 简称 BoW&#xff09; 词袋模型是一种常见的文本表示方法&#xff0c;主要用于自然语言处理&#xff08;NLP&#xff09;和信息检索领域。它将文本数据转换为特征向量&#xff0c;忽略语序&#xff0c;仅考虑词的出现与否或出现频率。 1. 基…

分治算法(单选题)

2-1 分数 2 下列多少种排序算法用了分治法&#xff1f; 堆排序插入排序归并排序快速排序选择排序希尔排序 A.2 B.3 C.4 D.5 正确答案 A 2-2 分数 2 分治法的设计思想是将一个难以直接解决的大问题分割成规模较小的子问题&#xff0c;分别解决问题&#xff0c;最后将子…

UNIX简史

从1991年Linux出现至今&#xff0c;由于众多IT巨头以及技术社区的推动&#xff0c;Linux已经成为非常成熟、可用于各种关键领域的操作系统&#xff0c;适当了解其发展历史&#xff0c;对于理顺其技术流派、从而更好地学习和使用Linux具有重要意义。由于其基于UNIX系统二十多年的…

C# OpenCV机器视觉:畸变矫正

在一个阳光明媚的早晨&#xff0c;阿强决定去拍照。他拿起相机&#xff0c;穿上他最喜欢的羊毛大衣&#xff0c;准备记录下生活中的美好瞬间。可是&#xff0c;当他兴奋地查看照片时&#xff0c;发现自己拍的每一张都像是被外星人用变形金刚的力量扭曲过一样&#xff01;“这是…

tryhackme——Defensive Security Intro(防御安全简介)

任务一&#xff1a;Introduction to Defensive Security防御安全简介 此room的两个要点&#xff1a; Preventing intrusions from occurring 防止入侵发生Detecting intrusions when they occur and responding properly 检测发生的入侵并正确响应 防御安全还有更多内容。 除上…

使用rust语言创建python模块(pyo3+maturin)

1. 首先使用conda创建python虚拟环境&#xff08;已创建的可省略&#xff09; >conda create --prefixE:\python_envs\rust_python python3.11 2. 激活python虚拟环境 conda activate rust_python 3. 安装maturin pip install maturin 4. 创建rust项目 >cd E:\py…

关于Postgresql旧版本安装

抛出问题 局点项目现场&#xff0c;要求对如下三类资产做安全加固&#xff0c;需要在公司侧搭建测试验证环境&#xff0c;故有此篇。 bclinux 8.2 tomcat-8.5.59 postgrel -11 随着PG迭代&#xff0c;老旧版本仅提供有限维护。如果想安装老版本可能就要费劲儿一些。现在&…