Python3-urllib模块的使用

urllib

在 Python 2 中,有 urllib 和 urllib2 两个库来实现请求的发送。而在 Python 3 中,已经不存在 urllib2 这个库了,统一为 urllib,其官方文档链接为:https://docs.python.org/3/library/urllib.html

urllib 库,它是 Python 内置的 HTTP 请求库,也就是说不需要额外安装即可使用。

它包含如下 4 个模块。

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

安装

cmd : pip install urllib

使用

urlopen

import urllib.request

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

运行结果如图所示:

image-20191202165113148

查看结果的类型

import urllib.request

res = urllib.request.urlopen("https://www.python.org")
print(type(res))

输出结果:

<class 'http.client.HTTPResponse'>

查看有哪些方法或者属性

image-20191202165413091

HTTPResposne 类型的对象,主要包含 read、readinto、getheader、getheaders、fileno 等方法,以及 msg、version、status、reason、debuglevel、closed 等属性

实例化一下:

import urllib.request

res = urllib.request.urlopen("https://www.python.org")
print(res.status)
print(res.getheaders())
print(res.getheader('server'))

输出结果:

200   # res.status
[('Server', 'nginx'), ('Content-Type', 'text/html; charset=utf-8'), ('X-Frame-Options', 'DENY'), ('Via', '1.1 vegur'), ('Via', '1.1 varnish'), ('Content-Length', '49800'), ('Accept-Ranges', 'bytes'), ('Date', 'Mon, 02 Dec 2019 08:56:01 GMT'), ('Via', '1.1 varnish'), ('Age', '2926'), ('Connection', 'close'), ('X-Served-By', 'cache-iad2142-IAD, cache-hkg17927-HKG'), ('X-Cache', 'HIT, HIT'), ('X-Cache-Hits', '2, 2926'), ('X-Timer', 'S1575276962.605613,VS0,VE0'), ('Vary', 'Cookie'), ('Strict-Transport-Security', 'max-age=63072000; includeSubDomains')]
nginx # res.getheader('server')

对urlopen方法传参

urlopen方法的API:

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

这里常用的如 data, timeout 等

data参数

import urllib.parse
import urllib.request

data = bytes(urllib.parse.urlencode({'name':'Tao'}), encoding='utf-8')
res = urllib.request.urlopen('http://httpbin.org/post', data=data)
print(res.read())

这里的data需要被转码为bytes(字节流), 需要用 urllib.parse 模块里的 urlencode 方法来将参数字典转化为字符串;第二个参数指定编码格式,这里指定为 utf8

执行结果:

b'{\n  "args": {}, \n  "data": "", \n  "files": {}, \n  "form": {\n    "name": "Tao"\n  }, \n  "headers": {\n    "Accept-Encoding": "identity", \n    "Content-Length": "8", \n    "Content-Type": "application/x-www-form-urlencoded", \n    "Host": "httpbin.org", \n    "User-Agent": "Python-urllib/3.7"\n  }, \n  "json": null, \n  "origin": "*,*", \n  "url": "https://httpbin.org/post"\n}\n'

timeout

timeout 参数用于设置超时时间,单位为秒,意思就是如果请求超出了设置的这个时间,还没有得到响应,就会抛出异常

实例一下:

import urllib.request
res = urllib.request.urlopen('http://httpbin.org/get', timeout=0.01)
print(res.read())

运行结果如下:

.......
URLError: <urlopen error timed out>

我们这里的超时设置的是0.01,因为0.01服务器不会来得及响应,所以抛出异常

通过try,except来跳过

import urllib.request
import urllib.error

try:
    res = urllib.request.urlopen('http://httpbin.org/get',timeout=0.01)
except urllib.error.URLError as e:
    print(e)

执行结果:

<urlopen error timed out>

其他参数

除了 data 参数和 timeout 参数外,还有 context 参数,它必须是 ssl.SSLContext 类型,用来指定 SSL 设置

此外,cafile 和 capath 这两个参数分别指定 CA 证书和它的路径,这个在请求 HTTPS 链接时会有用 cadefault 参数现在已经弃用了,其默认值为 False。

更详细可以 参见官方文档:https://docs.python.org/3/library/urllib.request.html

Request

import urllib.request

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

输出结果:

<!doctype html>
<!--[if lt IE 7]>   <html class="no-js ie6 lt-ie7 lt-ie8 lt-ie9">   <![endif]-->
<!--[if IE 7]>      <html class="no-js ie7 lt-ie8 lt-ie9">          <![endif]-->
<!--[if IE 8]>      <html class="no-js ie8 lt-ie9">                 <![endif]-->
<!--[if gt IE 8]><!--><html class="no-js" lang="en" dir="ltr">  <!--<![endif]-->

<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">

    <link rel="prefetch" href="//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js">
..... # 省略

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 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.90 Safari/537.36
  • 第五个参数 unverifiable 表示这个请求是否是无法验证的,默认是 False,意思就是说用户没有足够权限来选择接收这个请求的结果。例如,我们请求一个 HTML 文档中的图片,但是我们没有自动抓取图像的权限,这时 unverifiable 的值就是 True
  • 第六个参数 method 是一个字符串,用来指示请求使用的方法,比如 GET、POST 和 PUT 等

实例一下:

from urllib import request, parse

url = 'http://httpbin.org/post'
headers = {
    'host':'httpbin.org',
    'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.90 Safari/537.36'
}
data = {'name':'Tao'}
data = bytes(parse.urlencode(data), encoding='utf8')
req = request.Request(url=url, headers=headers, data=data, method='POST')
res = request.urlopen(req)
print(res.read().decode('utf-8'))

输出结果:

{
  "args": {}, 
  "data": "", 
  "files": {}, 
  "form": {
    "name": "Tao"
  }, 
  "headers": {
    "Accept-Encoding": "identity", 
    "Content-Length": "8", 
    "Content-Type": "application/x-www-form-urlencoded", 
    "Host": "httpbin.org", 
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.90 Safari/537.36"
  }, 
  "json": null, 
  "origin": "***********", 
  "url": "https://httpbin.org/post"
}

代理

from urllib.error import URLError  
from urllib.request import ProxyHandler, build_opener  

proxy_handler = ProxyHandler({  
    'http': 'http://127.0.0.1:9999',  
    'https': 'https://127.0.0.1:9999'  
})  
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)

Cookies

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')
print(cookie)
for item in cookie:
    print(item.name+"="+item.value)

执行结果:

<CookieJar[<Cookie BAIDUID=628A48729877CDFB781699E348B932A6:FG=1 for .baidu.com/>, <Cookie BIDUPSID=628A48729877CDFB8C7B924D647E5EE2 for .baidu.com/>, <Cookie PSTM=1574840398 for .baidu.com/>, <Cookie BD_NOT_HTTPS=1 for www.baidu.com/>]>
BAIDUID=628A48729877CDFB781699E348B932A6:FG=1
BIDUPSID=628A48729877CDFB8C7B924D647E5EE2
PSTM=1574840398
BD_NOT_HTTPS=1

异常处理

from urllib import request,error
try:
    res = request.urlopen('https://www.betao.cn/index.htm')
except error.HTTPError as e:
    print(e.reason, e.code, e.headers, sep='\n')

执行结果:

Not Found
404
Server: nginx
Date: Mon, 02 Dec 2019 09:40:03 GMT
Content-Type: text/html; charset=UTF-8
Transfer-Encoding: chunked
Connection: close
from urllib import request, error
try:
    res = request.urlopen('https://www.betao.cn/index.htm')
except error.HTTPError as e:
    print(e.reason, e.code, e.headers, sep='\n')
except error.URLError as e:
    print(e.reason)
else:
    print('Successful!!!')

执行结果:

Not Found
404
Server: nginx
Date: Wed, 27 Nov 2019 07:46:03 GMT
Content-Type: text/html; charset=UTF-8
Transfer-Encoding: chunked
Connection: close
....

链接解析

实例一下:

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')

除了这种最基本的解析方式外,urlparse 方法还有其他配置吗?接下来,看一下它的 API 用法: urllib.parse.urlparse(urlstring, scheme='', allow_fragments=True) 可以看到,它有 3 个参数

  • 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)
# 可以发现,我们提供的 URL 没有包含最前面的 scheme 信息,但是通过指定默认的 scheme 参数,返回的结果是 https。

# 执行结果:
ParseResult(scheme='https', netloc='', path='www.baidu.com/index.html', params='user', query='id=5', fragment='comment')
# 假设我们带上了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 信息,就会返回解析出的 scheme

allow_fragments:即是否忽略 fragment。如果它被设置为 False,fragment 部分就会被忽略,它会被解析为 path、parameters 或者 query 的一部分,而 fragment 部分为空。

实例来看一下

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

# 执行结果:
ParseResult(scheme='http', netloc='www.baidu.com', path='/index.html', params='user', query='id=5#comment', fragment='')
# 假设 URL 中不包含 params 和 query,
from urllib.parse import urlparse

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

# 执行结果:
ParseResult(scheme='http', netloc='www.baidu.com', path='/index.html#comment', params='', query='', fragment='')

可以发现,当 URL 中不包含 params 和 query 时,fragment 便会被解析为 path 的一部分。 返回结果 ParseResult 实际上是一个元组,我们可以用索引顺序来获取,也可以用属性名获取。

示例如下:

from urllib.parse import urlparse
result = urlparse('http://www.baidu.com/index.html;user?id=5#comment',allow_fragments=False)
print(result.scheme)
for i in range(10):
    try:
        print(result[i])
    except:
        pass

执行结果:

http
http
www.baidu.com
/index.html
user
id=5#comment

有了 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
from urllib.parse import urlsplit
result = urlsplit('http://www.baidu.com/index.html;user?a=6#comment')
print(result)
print(result[0])

# 执行结果
SplitResult(scheme='http', netloc='www.baidu.com', path='/index.html;user', query='a=6', fragment='comment')
http

还有链接的合并...,不常用,这里略过

urlencode

from urllib.parse import urlencode

param = {
    'name':'Tao',
    'age':18
}
base_url = 'http://www.baidu.com?'
url = base_url + urlencode(param)
print(url)

# 执行结果:
http://www.baidu.com?name=Tao&age=18

end...

本文链接:

https://www.betao.cn/archives/python3-urllib.html
1 + 3 =
快来做第一个评论的人吧~