python的HTTP请求库-Urllib


使用urrlib

Urllib官方文档

是python的内置的HTTP请求库

python3不存在urllib2的说法,统一为urllib*

包括如下四个模块:

  • request

它是最基本的HTTP请求模块,可以用来模拟发送请求。就像在浏览器里输入网址
然后回车一样,只需要给库方法传入U R L 以及额外的参数,就可以模拟实现这个过程了

  • error

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

  • parse

一个工具模块,提供了许多URL处理方法,比如拆分、解析 、合并等

  • robotparser

主要是用来识别网站的robots.txt文件,然后判断哪些网站可以爬,哪些网站不
可以爬,它其实用得比较少。

发送请求

使 用 urllib的 request模块,我们可以方便地实现请求的发送并得到响应

urlopen()

urlopen()官方文档

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

请求方法urllib.request

1
2
3
4
import urllib.request
response = urllib.request.urlopen(url)
print(type(response)) # <class 'http.client.HTTPResponse'>
print(response.read().decode('utf-8'))
响应response常用方法

response是一个 HTTPResposne 类 型 的 对 象 ,主要包含 read(), readinto(), getheader(name),getheaders(), fileno ()等 方 法 ,以及 msg、 version, status, reason, debuglevel, closed 等 属 性 。

  • read()

获取响应体

  • status

响应状态码

  • getheaders()

获取响应头

  • getheaders(‘server’)

输入参数server,返回服务器类型

链接传参

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

  • data

data参数是可选的。如果要添加该参数,并且如果它是字节流编码格式的内容,即 bytes类型,则需要通过bytes()方法转化。另外,如果传递了这个参数,则它的请求方式就不再是GET方式,而是 POST方式

1
2
3
4
5
import urllib.parse
import urllib.request

data = bytes(urllib.parse.urlencode({'word': 'hello'}), encoding='utf-8') # urlencode()将字典转换为字符串;bytes()将字符串转换为字节流
response = urllib.request.urlopen(url, data=data)

http://httpbin.org提供HTTP测试

http://httpbin.org/post提供POST测试

  • timeout

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

超时报urllib.error.URLError

使用try except:

1
2
3
4
5
6
7
8
import socket
import urllib.request
import urllib.error
try:
response = urllib.request.urlopen(url, timeout=1)
except urllib.error.URLError as e:
if isinstance(e.reason, socket.timeout):
print("Time Out")
  • 其它参数

context

它必须是ssl.SSLContext类型,用来指定SSL设置

cafile和capath

这两个参数分别指定C A 证书和它的路径,这个在请求HTTPS链接时会
有用

Request

urlopen只能用于简单的请求

1
2
3
4
5
import urllib.request

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

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

构造请求

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

  • data

data 如果要传,必须传bytes (字节流 )类型的 。如果它是字典,可以先用
urllib.parse 模块里的 urlencode()编码

  • headers

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

添加请求头最常用的用法就是通过修改User-Agent来伪装浏览器,默 认 的 User-Agent是Python-urllib,我们可以通过修改它来伪装浏览器。比如要伪装火狐浏览器,你可以把它设置为:
Mozilla/5.0 (X11; U; Linux i686) Gecko/20071127 Firefox/2.0.0.11

  • origin_req_host

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

  • unverifiable

unverifiable表示这个请求是否是无法验证的,默认是F a ls e ,意思就是说用户没有足够权限来选择接收这个请求的结果。例如,我们请求一个HTML文档中的图片,但是我们没有自动抓取图像的权限,这 时 u nverifiable的值就是True。

  • method

method是一个字符串,用来指示请求使用的方法,比如GET、 POST和 PUT等。

1
2
3
4
5
6
7
8
9
10
11
import urllib.request
import urllib.parse
dict = {"a": "b"}
data = bytes(urllib.parse.urlencode(dict), encoding='utf-8')
headers = {
'User-Agent': '.....'
'Host': 'httpbin.org'
}
request = urllib.request.Request(url, data=data, headers=headers, method='POST')
# request.add_header('user-agent')也可以
response = urllib.request.urlopen(request)

高级用法

Handler

[handler官方文档](https://docs.python.
org/3/library/urllib.request.html#urllib.request.BaseHandlero )

上面无法处理代理,cookies的情况

简而言之,我们可以把它理解为各种处理器, 有专门处理登录验证的,有处理 Cookies的,有处理代理设置的。利用它们,我们几乎可以做到HTTP请求中所有的事情。

首先,介绍一下urllib.request模块里的BaseHandler类 ,它是所有其他Handler的父类, 它提供了最基本的方法,例如 default_open(). protocol_request()等。

有各种Handler子类继承这个BaseHandler类,举例如下 :

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

OpenerDirector

那么,为什么要引入Opener呢?因为需要实现更高级的功能。之前使用的Request和 urlopen()相当于类库为你封装好了极其常用的请求方法,利用它们可以完成基本的请求,但是现在不一样了,我们需要实现更高级的功能,所以需要深入一层进行配置,使用更底层的实例来完成操作,所以这里就用到了 0pener

当打开网页弹出提示框要求输入密码,账号时:

G0g65F.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from urllib.request import HTTPPasswordMgrWithDefaultRealm, HTTPBasicAuthHandler, 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')
print(html)
except URLError as e:
print(e.reason)
代理
1
2
3
4
5
6
7
8
9
10
11
12
from urllib.error import URLError
from urllib.request import ProxyHandler, 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)
Cookies

Cookies的处理就需要相关的Handler 了

1
2
3
4
5
6
7
import http.cookiejar, urllib.request
cookie = http.cookiejar.CookieJar()
handler = urllib.request.HTTPCookieProcessor(cookie)
opener = urllib.request.build_opener(handler)
response = opener.open('http://www.baidu.com')
for item in cookie:
print(item.name+"="+item.value)

处理异常

URLError

1
2
3
4
5
from urllib import request, error
try:
response = request.urlopen(url)
except error.URLError as e:
print(e.reason)

HTTPError

它是URLError的子类,专门用来处理HTTP请求错误

有如下三个属性:

  • code

返 回 HTTP状态码,比如404表示网页不存在, 500表示服务器内部错误等。

  • reason

同父类一样,用于返回错误的原因。

  • headers

返回请求头。

解析链接

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

urlparse()

1
2
3
4
5
6
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.htmT, params='user', query='id=5',
fragment='comment')

可以看到,返回结果是一个ParseResult类型的对象,它 包 含 6 个部分,分另U是 scheme, netloc.path、 param, squery, fragment

result可以用元组的概念,也可以用属性来获取各部分

1
print(result[0], result.params)

urlparse API

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

  • urlstring
  • scheme

当传入的url没有协议时,可以指定scheme参数为http/https

当本身就有协议,有传参为不同的协议,则会输出本身的协议

  • allow_fragments

设置为false时,结果就不会有fragments这一部分

当参数为false且链接中没有params和query时,fragment便会解析为path内容的一部分

urlunparse()

与urlparse()相反

1
2
3
4
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

urlsplit()

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

1
2
3
4
5
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')

urlunsplit()

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

urljoin()

有了 urlunparse()和 urlunsplit()方 法 ,我们可以完成链接的合并,不过前提必须要有特定长度的对象,链接的每一部分都要清晰分开。

生成链接还有另一个方法,那 就 是 urljoin()方法。我们可以提供一个base_url (基础链接)作为第一个参数,将新的链接作为第二个参数,该方法会分析base_url的 scheme, netloc和 path这 3个内容并对新链接缺失的部分进行补充

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from urllib.parse import urljoin
print(urljoin('http://www.baidu.com', 'FAQ.html'))
print(urljoin('http://www.baidu.com', 'https://cuiqingcai.com/FAO.html'))
print(urljoin('http://www.baidu.com/about.html', 'https://cuiqingcai.com/FAO.html'))
print(u:rljoin('http://www.baidu.com/about.html', 'https://cuiqingcai.com/FAO.html?question=2'))
print(urljoin('http://www.baidu.com?wd=abc', 'https://cuiqingcai.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=2J)

>>>
http://www.baidu.com/FAO.html
https://cuiqingcai.com/FAO.html
https://cuiqingcai.com/FAO.html
https://cuiqingcai.com/FAO.html?question=2
https://cuiqingcai.com/index.php
http://www. baidu. com?ca tego:ry=2#comme nt
www.baidu.com?category=2#comment
www.baidu.com?category=2

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

urlencode()

它 在 构 造 GET请求参数的时候非常有用

1
2
3
4
5
6
7
8
9
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

parse_qs()

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

1
2
3
4
5
from urllib.parse import parse_qs
query = 'name=germey&age=22'
print(parse_qs(query))
运行结果如下:
{ 'name': ['germey'], 'age': ['22']}

parse_qsl()

另外,还 有 一 个 parse_qsl()方 法 ,它用于将参数转化为元组组成的列表

1
2
3
4
from urllib.parse import parse_qsl
query = 'name=germey&age=22'
print(parse_qsl(query))
>>>[('name', 'germey'), ('age', '22')]

quote()

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

1
2
3
4
5
from urllib.parse import quote
keyword = '壁纸'
url = 'https://www.baidu.com/s?wd=' + quote(keyword)
prin t(url)
>>> https://www. baidu. com/s?wd=%E 5%A3%81%E7%BA%B8

unquote()

有了 quote。 方法,当然还有unquote()方法,它可以进行URL解 码

1
2
3
4
5
from urllib.parse import unquote
url = 'https://www.baidu.com/s7wdn%E5%A3%81%E7%BA%B8'
print(unquote(url))
这是上面得到的URL编码后的结果,这里 利 用 unquote()方法还原,结果如下:
https://www.baidu. com/s?wd=壁纸

分析Robots协议

Robots协议

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

robots.txt文件可以为空

一个爬虫爬取一个网站时,首先会看是否存在robots.txt,然后在按内容爬取;如果不存在则直接爬取

Allow 一 般 和 Disallow 一 起 使 用 ,一般不会单独使用

  • 禁止所有爬虫爬取任何目录

User-agent: *
Disallow: /

  • 允许所有爬虫爬取任意目录

User-agent: *
Disallow:

  • 禁止所有爬虫爬取某些目录

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

  • 只允许某一个爬虫爬取爬取

User-agent: Webcrawler
Disallow:
User-agent: *
Disallow: /

爬虫名称

大家可能会疑惑,爬虫名是哪儿来的?为什么就叫这个名?其实它是有固定名字的了,比如百度的就叫作BaiduSpider

GDPj7n.png

robotparser

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

1
2
3
4
urllib.robotparser.RobotFileParser(url)
# 等同于
rp = urllib.robotparser.RobotFileParser()
rp.set_url(url)

这个类常用方法:

  • set_url

    实例化这个类时传入了url,就不需要这个方法了

  • read()

    读 取 robots.txt文件并进行分析。注意,这个方法执行一个读取和分析操作,如果不调用这个方法,接下来的 判 断 都 会 为F a ls e ,所以一定记得调用这个方法。这个方法不会返回任何内容,但是执行了读取操作。

    直接传入url和set_url()需要执行read(), 使用parse()不需要使用

  • parse()

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

传入的可以是数组,元组

  • can_fetch()

    该方法传入两个参数,第一个是U ser-agent,第二个是要抓取的URL。 返回的内容是该搜索引擎是否可以抓取这个U R L ,返回结果是True或 False。

  • mtime()

    该方法传入两个参数,第一个是U ser-agent,第二个是要抓取的URL。 返回的内容是该搜索引擎是否可以抓取这个U R L ,返回结果是True或 False。

  • modified()

    它同样对长时间分析和抓取的搜索爬虫很有帮助,将当前时间设置为上次抓取和分析robots.txt的时间。

使用can_fetch()

1
2
3
4
5
from urllib.robotparser import RobotFileParser

rp = RobotFileParser('url/robots.txt')
rp.read()
print(rp.can_fetch('*', '某个url'))

使用parse()

1
2
3
4
5
6
from urllib.robotparser import RobotParser
from urllib.request import urlopen

rp = RobotFileParser()
rp.parse(urlopen('url/robots.txt').read().decode('utf-8').split('\n'))
print(rp.can_fetch('*', url))

问候不一定要郑重其事,但一定要真诚感人。

本文标题:python的HTTP请求库-Urllib

文章作者:TTYONG

发布时间:2020年04月04日 - 15:04

最后更新:2022年03月06日 - 18:03

原始链接:http://tianyong.fun/%E5%B4%94%E5%BA%86%E6%89%8Dpython3%E7%88%AC%E8%99%AB-HTTP%E8%AF%B7%E6%B1%82%E5%BA%93-Urllib.html

许可协议: 转载请保留原文链接及作者。

多少都是爱
0%