Python内置的HTTP请求库,包含如下4个模块:
import urllib.request
# 定义一个url,待访问的网址
url = 'http://www.baidu.com'
# 模拟浏览器向服务器发送请求
response = urllib.request.urlopen(url)
# read()方法返回的是字节形式的二进制数据,转换成字符串要解码decode()
content = response.read().decode('utf-8')
print(content)
一、response响应是HTTPResponse类型
import urllib.request
url = 'http://www.baidu.com'
response = urllib.request.urlopen(url)
print(type(response))
结果:
<class 'http.client.HTTPResponse'>
二、6个方法
- response.read() 一个字节一个字节的读,read()方法中的数字表示返回的字节的个数
- readline() 读取一行
- readlines() 一行一行的读直到读完
- getcode() 返回状态码,是200就没毛病
- geturl() 返回的是url地址
- getheaders() 返回的是状态信息及响应头
如果要添加该参数,它需要被转码成字节流类型,即 bytes 类型,通过 bytes()方法转化。一旦传递了这个参数,它的请求方式不再是GET方式,而是POST方式。
timeout参数用于设置超时时间(单位:秒),支持HTTP,HTTPS,FTP请求。如果请求超出了设置的时间还没有得到响应,就会抛出异常。不指定该参数就会使用全局默认时间。
import urllib.request
url = 'http://www.baidu.com'
response = urllib.request.urlopen(url=url, timeout=0.1)
content = response.read().decode('utf-8')
print(content)
报错:
socket.timeout: timed out
利用urlopen()可以实现最基本请求的发起,但是这些简单的参数不足以构建完整的请求。要在请求中加入Headers等信息,需要用更强大的Request类,可以查看一下该类需要的参数。
说明:
urllib库中的parse模块,他定义了处理URL的标准接口,url中各部分抽取,合并,转换。
可以实现url的识别与分段
import urllib.parse
url = 'https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&rsv_idx=1&tn=baidu&wd=%E5%91%A8%E6%9D%B0%E4%BC%A6'
response = urllib.parse.urlparse(url)
print(type(response), response)
<class 'urllib.parse.ParseResult'> ParseResult(scheme='https', netloc='www.baidu.com', path='/s', params='', query='ie=utf-8&f=8&rsv_bp=1&rsv_idx=1&tn=baidu&wd=%E5%91%A8%E6%9D%B0%E4%BC%A6', fragment='')
与urlparse()对立的方法,接受的参数是一个可迭代的对象,长度必须是6,否则会抛出参数不足或过多。
import urllib.parse
req = ['https', 'www.baidu.com', 'index.html', 'user', 'a=6', 'comment']
print(urllib.parse.urlunparse(req))
结果:
https://www.baidu.com/index.html;user?a=6#comment
用法urlparse()用法相似,不再单独解析params部分,合并到path,返回的参数只有5个。
相似于urlunparse(),长度是5。
urlunparse()与urlunsplit()方法可以完成链接的合并,但是需要特定的长度的对象,每一部分都要清晰分开。
urljoin()基本用法:提供一个基础链接作为参数1,新的链接作为参数2,该方法会分析两个链接中的scheme,netloc,path。将这3个部分缺失的地方补充到新连接中。
import urllib.parse
print(urllib.parse.urljoin('http://www.baidu.com', 'FAQ.html'))
结果:
http://www.baidu.com/FAQ.html
例如模拟在百度搜索’周杰伦’时
import urllib.request
url = 'https://www.baidu.com/s?wd=周杰伦'
# 请求对象定制
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1'
}
request = urllib.request.Request(url=url, headers=headers)
response = urllib.request.urlopen(request)
content = response.read().decode('utf-8')
print(content)
会报错,因为识别不了’周杰伦’
UnicodeEncodeError: 'ascii' codec can't encode characters in position 10-12: ordinal not in range(128)
quote():汉字或者其他字符变成Unicode编码
import urllib.parse
name = urllib.parse.quote('周杰伦')
print(name)
%E5%91%A8%E6%9D%B0%E4%BC%A6
真正的url:'https://www.baidu.com/s?wd=%E5%91%A8%E6%9D%B0%E4%BC%A6'
当需要转码的字符较多时,用quote()就会显得特别麻烦
应用场景:url中有多个参数时,以字典形式存放,如:‘https://www.baidu.com/s?wd=周杰伦&sex=男’
import urllib.parse
data = {
'wd': '周杰伦',
'sex': '男'
}
res = urllib.parse.urlencode(data)
print(res)
# 结果:
wd=%E5%91%A8%E6%9D%B0%E4%BC%A6&sex=%E7%94%B7
以百度翻译为例。
import urllib.request
import urllib.parse
url_page = 'http://fanyi.baidu.com/sug'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1'
}
data = {
'kw': 'spider'
}
# post的请求的参数必须进行编码
data = urllib.parse.urlencode(data)
# post的请求参数不会拼接在url后面,需要放在请求对象定制的参数中
request = urllib.request.Request(url=url_page, data=data, headers=headers)
response = urllib.request.urlopen(request)
content = response.read().decode('utf-8')
print(content)
TypeError: POST data should be bytes, an iterable of bytes, or a file object. It cannot be of type str.
问题出在第16行,改成:
data = urllib.parse.urlencode(data).encode('utf-8')
{"errno":1000,"errmsg":"\u672a\u77e5\u9519\u8bef"}
以上的结果是一个json数据,完整代码:
import urllib.request
import urllib.parse
import json
url_page = 'https://fanyi.baidu.com/sug'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1'
}
data = {
'kw': 'spider'
}
# post的请求的参数必须进行编码
data = urllib.parse.urlencode(data).encode('utf-8')
# post的请求参数不会拼接在url后面,需要放在请求对象定制的参数中
request = urllib.request.Request(url=url_page, data=data, headers=headers)
# 模拟浏览器请求
response = urllib.request.urlopen(request)
content = response.read().decode('utf-8')
# 字符串->json对象
obj = json.loads(content)
print(obj)
{'errno': 0, 'data': [{'k': 'spider', 'v': 'n. 蜘蛛; 星形轮,十字叉; 带柄三脚平底锅; 三脚架'}, {'k': 'Spider', 'v': '[电影]蜘蛛'}, {'k': 'SPIDER', 'v': 'abbr. SEMATECH process induced damage effect revea'}, {'k': 'spiders', 'v': 'n. 蜘蛛( spider的名词复数 )'}, {'k': 'spidery', 'v': 'adj. 像蜘蛛腿一般细长的; 象蜘蛛网的,十分精致的'}]}
用edge浏览器在Headers中找不到Form Data数据,可能是放在Payload中
百度翻译中有一种详细翻译
import urllib.request
import urllib.parse
import json
url = 'https://fanyi.baidu.com/v2transapi?from=en&to=zh'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1'
}
Data = {
'from': 'en',
'to': 'zh',
'query': 'love',
'transtype': 'enter',
'simple_means_flag': '3',
'sign': '198772.518981',
'token': '65e4e8433224437143da6972871e94fc',
'domain': 'common'
}
data = urllib.parse.urlencode(Data).encode('utf-8')
request = urllib.request.Request(url=url, data=data, headers=headers)
# 模拟浏览器向服务器发送请求
response = urllib.request.urlopen(request)
content = response.read().decode('utf-8')
obj = json.loads(content)
print(obj)
{'errno': 997, 'errmsg': '未知错误', 'query': 'lo', 'from': 'en', 'to': 'zh', 'error': 997}
请求头种的参数是真实浏览器发送请求时携带的参数,把请求头中数据都放进上面第7行的headers就欧克了?但是要记得看下图第2条Accept-Encoding中没有utf-8,所以要把它干掉
结果超多,不展示了,但是要找准Form Data中query对应的值是否是完整的单词,不然结果会是上面那个未知错误。
上图中起决定性作用的是Cookie,把其他参数都拖出去咔嚓掉只留下Cookie结果也一样
urllib的error模块定义了由request模块产生的异常。
URLError类来自urllib库的error模块,继承自OSError类,是error异常模块的基类。由request生成的异常都可以用这个类处理。
它具有一个属性reason,即返回错误的原因。
例如打开一个不存在的网页:
from urllib import request, error
try:
url = 'https://www.piaopiao.com'
response = request.urlopen(url)
except error.URLError as e:
print(e.reason)
打开如上那个不存在的页面时应该说要报错,但我们捕获了URLError这个异常,得到以下的结果。程序没有直接报错,可以避免程序异常终止,同时异常得到处理。
[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: Hostname mismatch, certificate is not valid for 'www.piaopiao.com'. (_ssl.c:1129)
HTTPError是URLError的子类,专门处理HTTP请求错误,它有3个属性:
在捕获异常的时候一般先是找子类的异常,找不到再找父类
**urllib.request.urlretieve()**方法直接将远程数据下载到本地
函数原型:urlretrieve(url, filename=None, reporthook=None, data=None)
import urllib.request
url_page = 'http://www.baidu.com'
# 下载网页
urllib.request.urlretrieve(url=url_page)
# 图片,视频也阔以...
或者写入文件
全称网络爬虫排除标准(Robots Exclusion Protocol),也叫做爬虫协议,机器人协议。用来告诉爬虫和搜索引擎哪些网页可以爬,哪些不能爬,robots.txt一般放在网站根目录下。在域名后加上robots.txt可以查看
当搜索爬虫访问一个网站时,首先检查该网站根目录下是否有robots.txt文件,若有,会根据其中定义的爬取范围爬取;否则,会访问所有可以访问的页面
查看是否有权限爬取这个网页,传入robots.txt的链接即可,或者在声明时不传入,用set_url)()方法设置
urllib.robotparse.RobotFileParser(url='')