爬虫基本库:urllib

urllib

    在 python2 中,有 urllib 和 urllib2 两个库来实现请求的发送,而在 python3 中,统一为 urllib,官方文档链接为:https://docs.python.org/3/library/urllib.html
    urllib 库是 python 内置的HTTP请求库,不需要额外安装即可使用,包括以下4个模块:

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

1. request模块

1.1. urlopen()

    urlllib.request 模块提供了最基本的构造 HTTP 求的方法,利用它可以模拟浏览器的一个请求发起过程,同时它还带有处理授权验证(authenticaton)、重定向(redirection)、浏览器 Cookies 及其他内容。

from urllib import request

# 抓取网页源代码并输出
response = request.urlopen("https://www.python.org")
print(response.read().decode("utf-8"))
# 输出response的类型    结果为 <class 'http.client.HTTPResponse'>
print(type(response))

    可以发现是一个HTTPResponse 类型的对象,主要包含 read()、readinto()、getheader(name)、getheaders()、fileno() 等方法,以及 msg、version、status、reason、debuglevel、closed 等属性。
    得到这个对象之后,我们把它赋值为 response 变量,然后就可以调用这些方法和属性,得到返回结果的一系列信息了。
    调用read()方法可以得到返回的网页内容,调用status属性可以得到返回结果的状态码,如200代表请求成功,404代表网页未找到。

# 输出相应的状态码
print(response.status)
# 200

# 调用getheaders()方法输出响应头信息
print(response.getheaders())
"""
[('Server', 'edge-esnssl-1.12.1-13'), ('Date', 'Fri, 23 Nov 2018 07:56:54 GMT'), ('Content-Type', 'text/html'), 
('Transfer-Encoding', 'chunked'), ('Connection', 'close'), ('Last-Modified', 'Fri, 23 Nov 2018 07:54:01 GMT'), 
('Vary', 'Accept-Encoding'), ('X-Powered-By', 'shci_v1.03'), ('Expires', 'Fri, 23 Nov 2018 07:57:54 GMT'), 
('Cache-Control', 'max-age=60'), ('Age', '0'), ('Via', 'https/1.1 ctc.ningbo.ha2ts4.74 (ApacheTrafficServer/6.2.1 [cMsSfW]), 
https/1.1 ctc.qingdao.ha2ts4.26 (ApacheTrafficServer/6.2.1 [cMsSfW])'), 
('X-Via-Edge', '154295981423876e0a07b3105f98c6dbeb1e9'), ('X-Cache', 'MISS.26'), 
('X-Via-CDN', 'f=edge,s=ctc.qingdao.edssl.45.nb.sinaedge.com,c=123.160.224.118;f=edge,
s=ctc.qingdao.ha2ts4.22.nb.sinaedge.com,c=140.249.5.45;f=Edge,s=ctc.qingdao.ha2ts4.26,
c=140.249.5.22;f=edge,s=ctc.ningbo.ha2ts4.73.nb.sinaedge.com,c=140.249.5.26;f=edge,
s=ctc.ningbo.ha2ts4.73.nb.sinaedge.com,c=115.238.190.73;f=Edge,s=ctc.ningbo.ha2ts4.74,c=115.238.190.73')]
"""

# 调用getheader()方法输出响应头的Server值
print(response.getheader('Server'))
# edge-esnssl-1.12.1-13

    如果需要给链接传递参数,该怎么做?下面是urlopen()函数的API(应用程序编程接口):

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

    可以发现,除了第一个参数可以传递URL外,还可以传递其他内容,比如data(附加数据)、timeout(超时时间)等。

  • data参数
        data 参数是可选的,如果要添加该参数,并且如果它是字节流编码格式的内容,即 bytes 类型,则需要通过 bytes() 方法转化,该方法的第一个参数需要是 str(字符串)类型,需要用 urllib.parse 模块里的 urlencode() 方法来将参数字典转化为字符串。另外,如果传递了这个参数,则它的请求方式就不再是GET,而是POST方式。
  • timeout参数
        timeout 参数用于设置超时时间,单位为秒,意思就是如果请求超出了设置的这个时间,还没有得到响应,就会抛出异常。如果不指定该参数,就会使用全局默认时间。它支持HTTP、 HTTPS、FTP请求。
from urllib import request

# 设置超时时间为1秒,1秒过后服务器没有响应则跑出URLError异常,错误原因是超时
response = request.urlopen("url地址", timeout=1)
print(response.read())
  • 其他参数
        除data参数和timeout参数外,还有context参数必须是ssl.SSLContext类型,用来指定SSL设置。此外,cafile和capath这两个参数分别制定CA证书和它的路径,这个在请求HTTPS链接时有用。cadefault参数已经弃用,默认值为False。

urlopen()方法官方文档:https://docs.python.org/3/library/urllib.request.html

1.2. Request

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

from urllib import request

req = request.Request('url地址')
response = request.urlopen(req)
print(response.read().decode('utf-8'))

    依旧是用urlopen()方法来发送请求,不过这次的参数是一个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_urllib,我们可以通过修改它来伪装浏览器,比如伪装火狐浏览器,可以设置为Mozilla/5.0 (X11; U; Linux i686) Gecko/20071127 Firefox/2.0.0.11
    headers还可以用 add_header() 方法来添加
req = request.Request(url=url, data=data, method='POST')
req.add_header('User-Agent', 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)')
  • 第四个参数 origin_req_host 指的是请求方的 host 名称或者IP地址
  • 第五个参数 unverifiable 表示这个请求是否是无法验证的,默认是 False ,意思就是说用户没有足够权限来选择接收这个请求的结果。例如,我们请求一个 HTML 文档中的图片,但是我们没有向动抓取图像的权限,这时 unverifiable 的值就是 True。
  • 第六个参数 method 是一个字符串,用来指示请求使用的方法,如GET、POST 和 PUT 等。

1.3. 高级用法

    处理器 Handler:处理登录验证、Cookies、代理设置。
    urllib.request 模块里的 BaseHandler 类是所有其他 Handler 的父类,它提供了最基本的方法,例如 default_open()、protocol_request() 等。
    接下来,就有各种 Handler 子类继承 BaseHandler 类:

  • HTTPDefaultErrorHandler:用于处理HTTP响应错误,错误都会抛出HTTPError类型的异常。
  • HTTPRedirectHandler:用于处理重定向。
  • HTTPCookieProcessor:用于处理Cookies。
  • ProxyHandler:用于设置代理,默认代理为空。
  • HTTPPasswordMgr:用于管理密码,它维护了用户名和密码的表。
  • HTTPBasicAuthHandler:用于管理认证,如果一个链接打开时需要认证,那么可以用它来解决认证问题。

更多详情见官方文档:https://docs.python.org/3/library/urllib.request.html#urllib.request.BaseHandler

    另一个比较重要的类就是OpenerDirector,我们可以称为Opener。之前的 urlopen() 方法就是 urllib 提供的一个 Opener 。
    之前的 Request 和 urlopen() 相当于类库封装好了常用的方法,利用它们可以完成基本的请求,但是想要实现更高级的功能,就需要深入一层进行配置,使用更底层的实例完成操作,所以就用到了Opener。
    Opener 可以使用 open() 方法,返回的类型和 urlopen() 如出一辙,那么它和Handler 有什么区别呢?简而言之,就是利用 Handler 来构建 Opener。

  • 验证
        有些网站打开时会弹出提示框,提示输入账号密码验证成功后才能查看页面,那么,如果要请求这样的页面,需要借助 HTTPBasicAuthHandler 来完成:
from urllib.request import HTTPPasswordMgrWithDefaultRealm
from urllib.request import HTTPBasicAuthHandler
from urllib.request import build_opener
from urllib.error import URLError

username = 'username'
password = 'password'
url = 'http://localhost:5000/'

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')
except URLError as e:
    print(e.reason)

    这里首先实例化 HTTPBasicAuthHandler 对象,其参数是 HTTPPasswordMgrWithDefaultRealm 对象,它利用 add_password() 添加进去用户名和密码,这样就建立了一个处理验证的 Handler。
    接下来利用这个 Handler 并使用 build_opener() 方法构建了一个 Opener ,这个 Opener 在发送请求就相当于已经验证成功了。
    最后利用 Opener 的 open() 方法打开链接,就可以完成验证获取到页面源码内容。

  • 代理
        我们在做爬虫的时候免不了要使用代理,添加代理时可以这样做:
prom urllib.error import URLError
from urllib.request import ProxyHandler
from urllib.request import build_opener

proxy_handler = ProxyHandler({
    'http': 'http://127.0.0.1:9743',
    'https': 'https://127.0.0.1:9743'
})

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)

    本地搭建代理,运行在9743端口。使用的 ProxyHandler 参数为一个字典,键名是协议类型(如HTTP或者HTTPS等),键值是代理链接,可以添加多个代理。之后利用 Handler 以及 build_opener() 方法构造一个 Opener ,之后发送请求即可。

  • Cookies
        Cookies 的处理需要相关的 Handler,用实例获取网站的Cookies:
from http import cookiejar
from urllib import request

cookie = cookiejar.CookieJar()
handler = request.HTTPCookieProcessor(cookie)
opener = request.build_opener(handler)
response = opener.open('http://www.baidu.com')
for item in cookie:
    print(item.name + "=" + item.value)

    首先声明一个 CookieJar 对象,然后利用 HTTPCookieProcessor 来创建一个 Handler,最后利用 build_opener() 方法构建出 Opener ,执行 open() 函数即可获得。

运行结果如下:
BAIDUID=D467648C85174615BC257302C9AB32BF:FG=1
BIDUPSID=D467648C85174615BC257302C9AB32BF
H_PS_PSSID=1459_21119_26350_27508
PSTM=1543026564
delPer=0
BDSVRTM=0
BD_HOME=0

    Cookies 实际上是以文本形式保存的,所以也可以将其输出为文件:

from http import cookiejar
from urllib import request

filename = 'cookies.txt'
cookie = cookiejar.MozillaCookieJar(filename)
handler = request.HTTPCookieProcessor(cookie)
opener = request.build_opener(handler)
response = opener.open('http://www.baidu.com')
cookie.save(ignore_discard=True, ignore_expires=True)

    MozillaCookieJar 是 CookieJar 的子类,在生成文件时会用到,可以用来处理 Cookies 和文件相关的事件,比如读取和保存 Cookies ,可以将 Cookies 保存成 Mozilla 型浏览器的 Cookies 格式。
    运行后生成一个 cookies.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 3690511287 BAIDUID 681BB9746C6952E4B3BCFF5DD3E6560:FG=1
.baidu.com TRUE / FALSE 3690511287 BIDUPSID B681BB9746C6952E4B3BCFF5DD3E6560
.baidu.com TRUE / FALSE H_PS_PSSID 1427_21111_18560_22160
.baidu.com TRUE / FALSE 3690511287 PSTM 1543027641
.baidu.com TRUE / FALSE delPer 0
www.baidu.com FALSE / FALSE BDSVRTM 0
www.baidu.com FALSE / FALSE BD_HOME 0

2. 处理异常

    在网络不好的情况下,如果出现了异常,该怎么办?这时如果不处理这些异常,程序很可能因报错而终止运行,所以异常处理还是十分有必要的。
    urllib 的 error 模块定义了由 request 模块产生的异常。如果出现了问题,request 模块便会抛出 error 模块中定义的异常。

2.1. URLError

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

from urllib import request
from urllib import error

try:
    response = request.urlopen('https://cuiqingcai.com/index.htm')
except error.URLError as e:
    print(e.reason)

    打开一个不存在的页面照理来说应该会报错,但是这时我们捕获了 URLError 这个异常,运行结果如下:
        Not Found
    程序没有直接报错,而是输出了如上内容,这样通过如上操作,我们就可以避免程序异常终止,同时异常得到了有效处理。

2.2. HTTPError

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

  • code:返回 HTTP 状态码,比如 404 表示网页不存在, 500 表示服务器内部错误等。
  • reason:同父类一样,用于返回错误的原因。
  • headers:返回请求头。
from urllib import error
from urllib import request

try:
    response = request.urlopen('https://cuiqingcai.com/index.htm')
except error.HTTPError as e:
    print(e.reason, e.code, e.headers)

Not Found
404
Server: nginx/1.10.3 (Ubuntu)
Date: Sat, 24 Nov 2018 03:28:20 GMT
Content-Type: text/html; charset=UTF-8
Transfer-Encoding: chunked
Connection: close
Vary: Cookie
Expires: Wed, 11 Jan 1984 05:00:00 GMT
Cache-Control: no-cache, must-revalidate, max-age=0
Link: https://cuiqingcai.com/wp-json/; rel="https://api.w.org/"

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

from urllib import error
from urllib import request

try:
    response = request.urlopen('https://cuiqingcai.com/index.htm')
except error.HTTPError as e:
    print(e.reason, e.code, e.headers)
except error.URLError as e:
    print(e.reason)
else:
    print('Request Successfully!')

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

import socket
from urllib import error
from urllib import request

try:
    response = request.urlopen('https://www.baidu.com', timeout=0.01)
except 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() 方法来判断它的类型,作出更详细的异常判断。

3. 解析链接

    urllib 库里还提供了 parse 模块,它定义了处理 URL 的标准接口,例如实现 URL 各部分的抽取、合并以及链接转换。它支持如下协议的 URL 处理:file、gopher、hdl、http、https、imap、mailto、mms、news、nntp、prospero、rsync、rtsp、rtspu、sftp、sip、sips、snews、svn、svn+ssh、telnet 和 wais。本节介绍一下该模块中常用的方法。

3.1. urlparse()

    该方法实现了URL的识别与分段

from urllib.parse import urlparse

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

<class 'urllib.parse.ParseResult'>
ParseResult(scheme='http', netloc='www.baidu.com', path='/index.html', params='user', query='id=5', fragment='comment')
返回结果是一个 ParseResult 类型的对象,它包含6个部分,分别是 scheme、netloc、path、params、query、fragment。

    urlparse() 方法将其拆分成了6个部分。大体观察可以发现,解析时有特定的分隔符。比如,:// 前面的就是 scheme,代表协议;第一个 / 符号前面便是 netloc,即域名;后面是 path ,即访问路径;分号 ; 前面是 params ,代表参数;问号?后面是查询条件 query,一般用作 GET 类型的 URL;井号#后面是锚点,用于直接定位页面内部的下拉位置。
    所以,可以得出一个标准的链接格式,具体如下:

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

    除了这种最基本的解析方式外,urlparse() 方法还有其他配置。它的 API 用法:

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

有三个参数:
  • urlstring:这是必填项,待解析的URL
  • scheme:默认的协议(如 HTTP 或 HTTPS 等),假如链接没有带协议信息,会将这个作为默认的协议
from urllib.parse import urlparse

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没有包含 scheme 信息,但是通过指定的 scheme 默认参数,返回的结果是https。

# 参数中带上scheme
from urllib.parse import urlparse

result = 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 中不包含 scheme 信息时才生效;如果 URL 中包含 scheme,则会解析出 URL 本身的 scheme。

  • allow_fragments:即是否忽略 fragment。如果设置为False,fragment 部分内容就会被忽略,它会被解析为path、parameters 或者 query 的一部分,而 fragment 部分为空。
from urllib.parse import urlparse

result = urlparse('http://www.baidu.com/index.html;user?id=5#comment', allow_fragments=False)
print(result)
# ParseResult(scheme='http', netloc='www.baidu.com', path='/index.html', params='user', query='id=5#comment', fragment='')
from urllib.parse import urlparse

result1 = urlparse('http://www.baidu.com/index.html#comment', allow_fragments=False)
print(result1)
# ParseResult(scheme='http', netloc='www.baidu.com', path='/index.html#comment', params='', query='', fragment='')
# 当 URL 中不包含 params 和 query 时,fragment 会被解析为 path 的一部分
# 返回结果 ParseResult 是一个元组,可以通过索引或属性名来获取
# print(result.scheme, result[0], result.netloc, result[1])
# http   http     www.baidu.com   www.baidu.com

3.2. urlunparse()

    相对于 urlparse() 的解析链接,自然有另一种构造链接的方法:urlunparse(),它接受的参数是一个可迭代对象,但是长度必须是6,否则会抛出参数数量不足或者过多的问题。

from urllib.parse import urlunparse

data = ['http', 'www.baidu.com', 'index.html', 'user', 'a=6', 'comment']
print(urlunparse(data))
# http://www.baidu.com/index.html;user?a=6#comment

    参数 data 用了列表类型,也可以用元组或特定的数据结构,这样就实现了 URL 的构造。

3.3. urlsplit()

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

from urllib.parse import urlsplit

result = urlsplit('http://www.baidu.com/index.html;user?id=5#comment')
print(result)
#SplitResult(scheme='http', netloc='www.baidu.com', path='/index.html;user', query='id=5', fragment='comment')

    返回结果 SplitResult 是一个元组类型,可以通过属性值或索引来获取。

from urllib.parse import urlsplit

result = urlsplit('http://www.baidu.com/index.html;user?id=5#comment')
print(result.scheme, result[0])
# http  http

3.4. urlunsplit()

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

from urllib.parse import urlunsplit

data = ['http', 'www.baidu.com', 'index.html', 'a=6', 'comment']
print(urlunsplit(data))
# http://www.baidu.com/index.html?a=6#comment

3.5. urljoin()

    有了 urlunparse() 和 urlunsplit() 方法,我们可以完成链接的合井,不过前提必须要有特定长度的对象,链接的每一部分都要清晰分开。
    此外,生成链接还有另一个方法,那就是 urljoin() 方法。我们可以提供一个 base_url(基础链接)作为第一个参数,将新的链接作为第二个参数,该方法会分析 base_url 的 scheme、netloc、path 这3个内容并对新链接缺失的部分进行补充,最后返回结果。

from urllib.parse import urljoin

print(urljoin('http://www.baidu.com', 'PAQ.html'))
print(urljoin('http://www.baidu.com', 'https://cuiqinghai.com/FAQ.html'))
print(urljoin('http://www.baidu.com/about.html', 'https://cuiqinghai.com/FAQ.html'))
print(urljoin('http://www.baidu.com/about.html', 'https://cuiqinghai.com/FAQ.html?question=2'))
print(urljoin('http://www.baidu.com?wd=abc', 'https://cuiqinghai.com/index.php'))
print(urljoin('http://www.baidu.com', '?category=2#comment'))
print(urljoin('www.baidu.com', '?category=2#comment'))
print(urljoin('www.baidu.com#comment', '?category=2'))

运行结果为:
http://www.baidu.com/PAQ.html
https://cuiqinghai.com/FAQ.html
https://cuiqinghai.com/FAQ.html
https://cuiqinghai.com/FAQ.html?question=2
https://cuiqinghai.com/index.php
http://www.baidu.com?category=2#comment
www.baidu.com?category=2#comment
www.baidu.com?category=2

    可以发现,base_url 提供了三项内容 scheme、netloc、path。如果这3项在新的链接里不存在,就予以补充;如果新的链接存在,就使用新的链接的部分。而 base_url 中的 params、 query、fragment 是不起作用的。通过 urljoin() 方法,可以轻松实现链接的解析、拼合与生成。

3.6. urlencode()

    构造 GET 请求参数时可以将 params 参数字典序列化成 GET 请求参数。

from urllib.parse import urlencode

params = {
    'name': 'germey',
    'age': 22
}
base_url = 'http://www.baidu.com?'
url = base_url + urlencode(params)
print(url)
# http://www.baidu.com?name=germey&age=22

3.7. parse_qs()

    有了序列化,必然就有反序列化。如果我们有一串 GET 请求参数,利用 parse_qs()方法, 就可以将它转回字典,示例如下:

from urllib.parse import parse_qs

query = 'name=germey&age=22'
print(parse_qs(query))
# {'name': ['germey'], 'age': ['22']}

3.8. parse_qsl()

    parse_qsl() 方法用于将参数转化为元组组成的列表,运行结果是一个列表,而列表中的每一个元素都是一个元组,元组的第一个内容是参数名,第二个内容是参数值。

from urllib.parse import parse_qsl

query = 'name=germey&age=22'
print(parse_qsl(query))
# [('name', 'germey'), ('age', '22')]

3.9. quote()

    该方法可以将内容转化为 URL 编码的格式 URL 中带有中文参数时,有时可能会导致乱码的问题,此时用这个方法可以将巾文字符转化为 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

3.10. unquote()

    URL 解码方法,上面的编码结果用 unquote() 方法还原:

from urllib.parse import unquote

url = 'https://www.baidu.com/s?wd=%E5%A3%81%E7%BA%B8'
print(unquote(url))
# https://www.baidu.com/s?wd=壁纸
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容