python-tools

Python Tools

Python urllib

urllib是Python中请求url连接的官方标准库,在Python2中主要为urllib和urllib2,在Python3中整合成了urllib。

而urllib3是第三方库,增加了连接池等功能,两者互相都有补充的部分。

Python2.x 有这些库名可用: urlliburllib2urllib3httplibhttplib2requests
Python3.x 有这些库名可用: urlliburllib3httplib2requests

urllib作为Python的标准库,基本上涵盖了基础的网络请求功能。urllib 库用于操作网页 URL,并对网页的内容进行抓取处理。

urllib 包 包含以下几个模块:

  • urllib.request - request`这个模块主要负责构造和发起网络请求,并在其中加入Headers、Proxy等
  • urllib.error - 包含 urllib.request 抛出的异常。
  • urllib.parse - 解析 URL。
  • urllib.robotparser - 解析 robots.txt 文件。

urllib.request

urllib.request 定义了一些打开 URL 的函数和类,包含授权验证、重定向、浏览器 cookies等。

urllib.request 可以模拟浏览器的一个请求发起过程。

我们可以使用 urllib.request 的 urlopen 方法来打开一个 URL,语法格式如下:

1
urllib.request.urlopen(url, data=None, [timeout, ]*, cafile=None, capath=None, cadefault=False, context=None)
  • url:url 地址。
  • data:发送到服务器的其他数据对象,默认为 None。
  • timeout:设置访问超时时间。
  • cafile 和 capath:cafile 为 CA 证书, capath 为 CA 证书的路径,使用 HTTPS 需要用到。
  • cadefault:已经被弃用。
  • context:ssl.SSLContext类型,用来指定 SSL 设置。

实例如下:

get

1
2
3
4
from urllib.request import urlopen

myURL = urlopen("https://www.runoob.com/")
print(myURL.read())

urlopen()方法中传入字符串格式的url地址,则此方法会访问目标网址,然后返回访问的结果。

访问的结果会是一个http.client.HTTPResponse对象,使用此对象的read()方法,则可以获取访问网页获得的数据。但是要注意的是,获得的数据会是bytes的二进制格式,所以需要decode()一下,转换成字符串格式。

read() 是读取整个网页内容:

print(myURL.read(300)) #指定读取的长度

print(myURL.readline()) #读取一行内容

lines = myURL.readlines():# 取文件的全部内容,它会把读取的内容赋值给一个列表变量
for line in lines:
print(line)

post

urlopen()默认的访问方式是GET,当在urlopen()方法中传入data参数时,则会发起POST请求。注意:传递的data数据需要为bytes格式。

设置timeout参数还可以设置超时时间,如果请求时间超出,那么就会抛出异常。

1
2
3
4
from urllib import request

resp = request.urlopen('http://httpbin.org/post', data=b'word=hello', timeout=10)
print(resp.read().decode())

通过urllib发起的请求会有默认的一个Headers:”User-Agent”:”Python-urllib/3.6”,指明请求是由urllib发送的。

所以遇到一些验证User-Agent的网站时,我们需要自定义Headers,而这需要借助于urllib.request中的Request对象。

1
2
3
4
5
6
7
8
9
from urllib import request

url = 'http://httpbin.org/get'
headers = {'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36'}

# 需要使用url和headers生成一个Request对象,然后将其传入urlopen方法中
req = request.Request(url, headers=headers)
resp = request.urlopen(req)
print(resp.read().decode())

Request对象

如上所示,urlopen()方法中不止可以传入字符串格式的url,也可以传入一个Request对象来扩展功能,Request对象如下所示。

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

构造Request对象必须传入url参数,data数据和headers都是可选的。

最后,Request方法可以使用method参数来自由选择请求的方法,如PUT,DELETE等等,默认为GET。

添加Cookie

为了在请求时能带上Cookie信息,我们需要重新构造一个opener。

使用request.build_opener方法来进行构造opener,将我们想要传递的cookie配置到opener中,然后使用这个opener的open方法来发起请求。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from http import cookiejar
from urllib import request

url = 'https://www.baidu.com'
# 创建一个cookiejar对象
cookie = cookiejar.CookieJar()
# 使用HTTPCookieProcessor创建cookie处理器
cookies = request.HTTPCookieProcessor(cookie)
# 并以它为参数创建Opener对象
opener = request.build_opener(cookies)
# 使用这个opener来发起请求
resp = opener.open(url)

# 查看之前的cookie对象,则可以看到访问百度获得的cookie
for i in cookie:
print(i)

或者也可以把这个生成的opener使用install_opener方法来设置为全局的。

则之后使用urlopen方法发起请求时,都会带上这个cookie。

1
2
3
# 将这个opener设置为全局的opener
request.install_opener(opener)
resp = request.urlopen(url)

设置Proxy代理

使用爬虫来爬取数据的时候,常常需要使用代理来隐藏我们的真实IP。

1
2
3
4
5
6
7
8
9
10
11
from urllib import request

url = 'http://httpbin.org/ip'
proxy = {'http':'218.18.232.26:80','https':'218.18.232.26:80'}
# 创建代理处理器
proxies = request.ProxyHandler(proxy)
# 创建opener对象
opener = request.build_opener(proxies)

resp = opener.open(url)
print(resp.read().decode())

下载数据到本地

在我们进行网络请求时常常需要保存图片或音频等数据到本地,一种方法是使用python的文件操作,将read()获取的数据保存到文件中。

urllib提供了一个urlretrieve()方法,可以简单的直接将请求获取的数据保存成文件。

1
2
3
4
from urllib import request

url = 'http://python.org/'
request.urlretrieve(url, 'python.html')

urlretrieve()方法传入的第二个参数为文件保存的位置,以及文件名。

注:urlretrieve()方法是python2直接移植过来的方法,以后有可能在某个版本中弃用。


urllib.response

在使用urlopen()方法或者opener的open()方法发起请求后,获得的结果是一个response对象。

这个对象有一些方法和属性,可以让我们对请求返回的结果进行一些处理。

  • read()

    获取响应返回的数据,只能使用一次。

  • getcode()

    获取服务器返回的状态码。

  • getheaders()

    获取返回响应的响应报头。

  • geturl()

    获取访问的url。


urllib.error

urllib.error 模块为 urllib.request 所引发的异常定义了异常类,基础异常类是 URLError。

urllib中主要设置了两个异常,一个是URLError,一个是HTTPErrorHTTPErrorURLError的子类。

HTTPError还包含了三个属性:

  • code:请求的状态码
  • reason:错误的原因
  • headers:响应的报头

URLError 是 OSError 的一个子类,用于处理程序在遇到问题时会引发此异常(或其派生的异常),包含的属性 reason 为引发异常的原因。

HTTPError 是 URLError 的一个子类,用于处理特殊 HTTP 错误例如作为认证请求的时候,包含的属性 code 为 HTTP 的状态码, reason 为引发异常的原因,headers 为导致 HTTPError 的特定 HTTP 请求的 HTTP 响应头。

例子:

1
2
3
4
5
6
7
8
In [1]: from urllib.error import HTTPError

In [2]: try:
...: request.urlopen('https://www.jianshu.com')
...: except HTTPError as e:
...: print(e.code)

403

status

1
2
3
4
5
6
7
8
9
import urllib.request

myURL1 = urllib.request.urlopen("https://www.runoob.com/")
print(myURL1.getcode()) # 200
try:
myURL2 = urllib.request.urlopen("https://www.runoob.com/no.html")
except urllib.error.HTTPError as e:
if e.code == 404:
print(404) # 404

urllib.parse

urllib.parse 是urllib中用来解析各种数据格式的模块,用于解析 URL,格式如下:

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

urlstring 为 字符串的 url 地址,scheme 为协议类型,

allow_fragments 参数为 false,则无法识别片段标识符。相反,它们被解析为路径,参数或查询组件的一部分,并 fragment 在返回值中设置为空字符串。

case
1
2
3
4
from urllib.parse import urlparse

o = urlparse("https://www.runoob.com/?s=python+%E6%95%99%E7%A8%8B")
print(o)

以上实例输出结果为:

1
ParseResult(scheme='https', netloc='www.runoob.com', path='/', params='', query='s=python+%E6%95%99%E7%A8%8B', fragment='')

从结果可以看出,内容是一个元组,包含 6 个字符串:协议,位置,路径,参数,查询,判断。

我们可以直接读取协议内容:

1
2
3
4
from urllib.parse import urlparse

o = urlparse("https://www.runoob.com/?s=python+%E6%95%99%E7%A8%8B")
print(o.scheme)

以上实例输出结果为:

1
https

完整内容如下:

属性 索引 值(如果不存在)
scheme 0 URL协议 scheme 参数
netloc 1 网络位置部分 空字符串
path 2 分层路径 空字符串
params 3 最后路径元素的参数 空字符串
query 4 查询组件 空字符串
fragment 5 片段识别 空字符串
username 用户名 None
password 密码 None
hostname 主机名(小写) None
port 端口号为整数(如果存在) None
urllib.parse.quote

在url中,是只能使用ASCII中包含的字符的,也就是说,ASCII不包含的特殊字符,以及中文等字符都是不可以在url中使用的。而我们有时候又有将中文字符加入到url中的需求,例如百度的搜索地址:

1
https://www.baidu.com/s?wd=南北

?之后的wd参数,则是我们搜索的关键词。那么我们实现的方法就是将特殊字符进行url编码,转换成可以url可以传输的格式,urllib中可以使用quote()方法来实现这个功能。

1
2
3
4
>>> from urllib import parse
>>> keyword = '南北'
>>> parse.quote(keyword)
'%E5%8D%97%E5%8C%97'

如果需要将编码后的数据转换回来,可以使用unquote()方法。

1
2
>>> parse.unquote('%E5%8D%97%E5%8C%97')
'南北'
urllib.parse.urlencode

在访问url时,我们常常需要传递很多的url参数,而如果用字符串的方法去拼接url的话,会比较麻烦,所以urllib中提供了urlencode这个方法来拼接url参数。

1
2
3
4
>>> from urllib import parse
>>> params = {'wd': '南北', 'code': '1', 'height': '188'}
>>> parse.urlencode(params)
'wd=%E5%8D%97%E5%8C%97&code=1&height=188'

urllib.robotparser

urllib.robotparser 用于解析 robots.txt 文件。

robots.txt(统一小写)是一种存放于网站根目录下的 robots 协议,它通常用于告诉搜索引擎对网站的抓取规则。

urllib.robotparser 提供了 RobotFileParser 类,语法如下:

1
class urllib.robotparser.RobotFileParser(url='')

这个类提供了一些可以读取、解析 robots.txt 文件的方法:

  • set_url(url) - 设置 robots.txt 文件的 URL。
  • read() - 读取 robots.txt URL 并将其输入解析器。
  • parse(lines) - 解析行参数。
  • can_fetch(useragent, url) - 如果允许 useragent 按照被解析 robots.txt 文件中的规则来获取 url 则返回 True。
  • mtime() -返回最近一次获取 robots.txt 文件的时间。 这适用于需要定期检查 robots.txt 文件更新情况的长时间运行的网页爬虫。
  • modified() - 将最近一次获取 robots.txt 文件的时间设置为当前时间。
  • crawl_delay(useragent) -为指定的 useragent 从 robots.txt 返回 Crawl-delay 形参。 如果此形参不存在或不适用于指定的 useragent 或者此形参的 robots.txt 条目存在语法错误,则返回 None。
  • request_rate(useragent) -以 named tuple RequestRate(requests, seconds) 的形式从 robots.txt 返回 Request-rate 形参的内容。 如果此形参不存在或不适用于指定的 useragent 或者此形参的 robots.txt 条目存在语法错误,则返回 None。
  • site_maps() - 以 list() 的形式从 robots.txt 返回 Sitemap 形参的内容。 如果此形参不存在或者此形参的 robots.txt 条目存在语法错误,则返回 None。

Python urllib3

Urllib3是一个功能强大,条理清晰,用于HTTP客户端的Python库。许多Python的原生系统已经开始使用urllib3。Urllib3提供了很多python标准库urllib里所没有的重要特性:

  1. 线程安全
  2. 连接池
  3. 客户端SSL/TLS验证
  4. 文件分部编码上传
  5. 协助处理重复请求和HTTP重定位
  6. 支持压缩编码
  7. 支持HTTP和SOCKS代理

安装

urllib3是一个第三方库,安装非常简单,pip安装即可:

1
pip install urllib3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import urllib3

# 直接访问
res = urllib3.request("GET", "http://httpbin.org/get")
print(res.json())

# 使用连接池进行访问
http = urllib3.PoolManager()
r = http.request('GET', 'http://httpbin.org/robots.txt')
print(r.status)
print(r.data.decode('utf-8'))

# 设置headers
headers={'X-Something': 'value'}
resp = http.request('GET', 'http://httpbin.org/headers', headers=headers)
print(resp.json())

# 设置url参数
fields = {'arg': 'value'}
resp = http.request('GET', 'http://httpbin.org/get', fields=fields)
print(resp.json())

# 如果使用的是POST等方法,则会将fields作为请求的请求正文发送。
# 所以,如果你的POST请求是需要url参数的话,那么需要自己对url进行拼接。
fields = {'arg': 'value'}
# resp = http.request('POST', 'http://httpbin.org/get', fields=fields) # 接口报错
# print(resp.data)

proxy = urllib3.ProxyManager('http://50.233.137.33:80', headers={'connection': 'keep-alive'})
resp = proxy.request('get', 'http://httpbin.org/ip')
print(resp.data)

注:urllib3中没有直接设置cookies的方法和参数,只能将cookies设置到headers中


Python网络请求urllib和urllib3详解 - 简书 (jianshu.com)

Python urllib、urllib2、urllib3用法及区别_urllib2和urllib3的区别-CSDN博客

User Guide - urllib3 2.2.2 documentation

requests

requests它是一个Python第三方库,处理URL资源特别方便。

1
pip install requests
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import requests

r = requests.get('https://www.httpbin.org/get') # 豆瓣首页
print(r.status_code)
print(r.text)


# 增加request参数
r = requests.get('https://www.httpbin.org/get', params={'q': 'python', 'cat': '1001','name':"哈哈"})
# 查看编码后的url
print(r.url)



# 无论响应是文本还是二进制内容,我们都可以用content属性获得bytes对象:
print(r.content)


# 传入header参数
r = requests.get('https://www.httpbin.org/get', headers={'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit'})
# 对应json类型可以直接获取
print(r.json())


# 发送POST请求,只需要把get()方法变成post(),然后传入data参数作为POST请求的数据
print("post form....")
r = requests.post('https://www.httpbin.org/post', data={'form_email': 'abc@example.com', 'form_password': '123456'})
print(r.text)
# requests默认使用application/x-www-form-urlencoded对POST数据编码。如果要传递JSON数据,可以直接传入json参数

print("post json....")
params = {'key': 'value'}
r = requests.post("https://www.httpbin.org/post", json=params) # 内部自动序列化为JSON
print("响应内容:",r.json())
# 获取响应头:
print("响应头:",r.headers)

# 传递cookie
cs = {'token': '12345', 'status': 'working'}
r = requests.get("https://www.httpbin.org/cookies", cookies=cs)
print(r.text)
# requests对Cookie做了特殊处理,使得我们不必解析Cookie就可以轻松获取指定的Cookie:
print("所有cookies:",r.cookies)
# print("获取某个cookie,需要存在,否则报错:",r.cookies['ts'])


# 要指定超时,传入以秒为单位的timeout参数:
r = requests.get('https://www.httpbin.org/get', timeout=2.5)
print(r.text)
# 类似的,上传文件需要更复杂的编码格式,但是requests把它简化成files参数:
# upload_files = {'file': open('report.xls', 'rb')}
# r = requests.post("url", files=upload_files)
# 在读取文件时,注意务必使用'rb'即二进制模式读取,这样获取的bytes长度才是文件的长度。

logging

内置的logging

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import logging

def foo(s):
return 10 / int(s)

def bar(s):
return foo(s) * 2

def main():
try:
bar('0')
except Exception as e:
logging.exception(e)

main()
print('END')

chardet

当我们拿到一个bytes时,就可以对其检测编码。用chardet检测编码,只需要一行代码:

1
2
>>> chardet.detect(b'Hello, world!')
{'encoding': 'ascii', 'confidence': 1.0, 'language': ''}

检测出的编码是ascii,注意到还有个confidence字段,表示检测的概率是1.0(即100%)。

我们来试试检测GBK编码的中文:

1
2
3
>>> data = '离离原上草,一岁一枯荣'.encode('gbk')
>>> chardet.detect(data)
{'encoding': 'GB2312', 'confidence': 0.7407407407407407, 'language': 'Chinese'}

检测的编码是GB2312,注意到GBK是GB2312的超集,两者是同一种编码,检测正确的概率是74%,language字段指出的语言是'Chinese'

对UTF-8编码进行检测:

1
2
3
>>> data = '离离原上草,一岁一枯荣'.encode('utf-8')
>>> chardet.detect(data)
{'encoding': 'utf-8', 'confidence': 0.99, 'language': ''}

我们再试试对日文进行检测:

1
2
3
>>> data = '最新の主要ニュース'.encode('euc-jp')
>>> chardet.detect(data)
{'encoding': 'EUC-JP', 'confidence': 0.99, 'language': 'Japanese'}

可见,用chardet检测编码,使用简单。获取到编码后,再转换为str,就可以方便后续处理。

psutil

Pillow