Python爬虫之基本库urllib的使用
在Python3中,有urllib库来实现请求的发送(将Python2中的urllib2已经统一至urllib库中)。
对于urllib库的疑问可以参照官网说明了解:https://docs.python.org/3/library/urllib.html
urllib库
urllib库是python内置的HTTP请求库,包含四个模块:
- request:最基本的HTTP请求模块,可以用来模拟发送请求。
- error:异常处理模块,如果出现请求错误,可以对这些异常进行捕获,防止程序意外终止。
- parse:一个工具模块,提供了许多URL的处理方法,如拆分、合并等。
- robotparser:主要用来识别网站的robot.txt文件。
发送请求
urlopen()
urllib.request的模块提供了最基本的构造HTTP请求的方法,利用它可以模拟浏览器的一个请求发起过程。
首先给出urlopen()方法的API:urllib.request.urlopen
(url, data=None, [timeout, ]***, cafile=None, capath=None, cadefault=False, context=None),后面会详细介绍各项参数意义。
下面我们简单的调用下urlopen()方法:
import urllib.request
response = urllib.request.urlopen('https://www.python.org')
print(response.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">
....#省略
</body>
</html>
接下来,我们看看urllib.request.urlopen()
方法返回的是个是什么
import urllib.request
response = urllib.request.urlopen('https://www.python.org')
print(type(response)) #打印response的类型
输出如下:
<class 'http.client.HTTPResponse'>
可以发现,返回的是一个HTTPResponse类型的对象。HTTPResponse类型的对象包含read()、readinto()、hetheader(name)、fileno()等方法和status等属性。如下打印了HTTPResponse类型的方法返回及其status属性。
import urllib.request
response = urllib.request.urlopen('https://www.python.org')
print(response.status)
print(response.getheaders())
输出了状态码和相应的头信息,如下:
200
[('Connection', 'close'), ('Content-Length', '48730'), ('Server', 'nginx'), ('Content-Type', 'text/html; charset=utf-8'), ('X-Frame-Options', 'DENY'), ('Via', '1.1 vegur'), ('Via', '1.1 varnish'), ('Accept-Ranges', 'bytes'), ('Date', 'Mon, 27 Jul 2020 07:55:36 GMT'), ('Via', '1.1 varnish'), ('Age', '2423'), ('X-Served-By', 'cache-bwi5134-BWI, cache-hkg17928-HKG'), ('X-Cache', 'HIT, HIT'), ('X-Cache-Hits', '30, 2173'), ('X-Timer', 'S1595836536.216510,VS0,VE0'), ('Vary', 'Cookie'), ('Strict-Transport-Security', 'max-age=63072000; includeSubDomains')]
输出状态码200代表访问成功,常见的状态码还有404(页面未找到)、400(服务器无法解析请求)、**500(服务器遇到错误)**等。
urlopen()——data参数
data参数是可选的,若添加此参数,需要使用bytes()方法将参数转化为bytes类型。并且请求方式将变为POST。
为将其转化为bytes类型,我们可以用bytes()方法,同时利用urllib.parse模块里的urlencode()方法将参数字典转化为字符串作为第一个参数;第二个参数是指定编码格式。
import urllib.request
import urllib.parse
mydict = {
'name':'Small_Fish'}
mydata = bytes(urllib.parse.urlencode(mydict),encoding='utf8')
response = urllib.request.urlopen('http://httpbin.org/post',data=mydata)#该链接可以用来测试POST请求
print(response.status)
print(response.read())
输出如下:
200
b'{\n "args": {}, \n "data": "", \n "files": {}, \n "form": {\n "name": "Small_Fish"\n }, \n "headers": {\n "Accept-Encoding": "identity", \n "Content-Length": "15", \n "Content-Type": "application/x-www-form-urlencoded", \n "Host": "httpbin.org", \n "User-Agent": "Python-urllib/3.7", \n "X-Amzn-Trace-Id": "Root=1-5f1e8dbc-6aa88726e6a39100e23797ed"\n }, \n "json": null, \n "origin": "117.136.19.0", \n "url": "http://httpbin.org/post"\n}\n'
可见返回的内容的"form"即为我们传递的参数,表面了是模拟表单的提交方式,以POST方式传输数据。
urlopen()——timeout参数
timeout参数根据字面意义大家应该就能猜到其意义了,该参数用于设置超时时间(单位:s),当发出的请求超出了该时间依然没有得到相应时,就会抛出异常,我们可以用try-except来捕获异常。
import urllib.request
import urllib.parse
import urllib.error
import socket
if __name__ =="__main__":
mydata = bytes(urllib.parse.urlencode({'name':'Small_Fish'}),encoding='utf8')
try:
response = urllib.request.urlopen('http://httpbin.org/post',data = mydata,timeout = 0.1)#设置超时时间为0.1s,当超过改时间没有得到相应将抛出异常
except urllib.error.URLError as e:
if isinstance(e.reason,socket.timeout):#判断,如果异常原因是socket.timeout则打印TIME OUT
print('TIME OUT')
输出如下:
TIME OUT
Request()
当请求中需要加入Headers等信息时,我们就需要用到更强大的Request类来构建请求。
API:class urllib.request.Request
(url, data=None, headers={}, origin_req_host=None, unverifiable=False, method=None)
下面直接来说参数吧:
- url:请求的URL
- data:同urlopen中的data,为bytes类型。如果是字典需要先用urllib.parse.urlencode()进行编码
- headers:请求头,是一个字典
- origin_req_host:请求方的host名称或IP地址
- unverifiable:表示请求是否是无法验证的,默认False。
- method:请求的方法(GET、POST、PUT等)
还是举一个实例:
import urllib.request
import urllib.parse
if __name__ == '__main__':
url = 'http://httpbin.org/post' #参数URL
headers = {
'User-Agent': 'Mozilla/4.0(compatible; MISE 5.5;Windows NT)',
'Host':'httpbin.org'}#参数Headers
dict ={
'name':'Small_Fish'} #参数data
mydata = bytes(urllib.parse.urlencode(dict),encoding='utf-8')
request =urllib.request.Request(url,data = mydata,headers = headers, method = 'POST')
response = urllib.request.urlopen(request)#依然使用了urlopen()
print(response.read().decode('utf8'))
输出如下:
{
"args": {},
"data": "",
"files": {},
"form": {
"name": "Small_Fish"
},
"headers": {
"Accept-Encoding": "identity",
"Content-Length": "8",
"Content-Type": "application/x-www-form-urlencoded",
"Host": "httpbin.org",
"User-Agent": "Mozilla/4.0(compatible; MISE 5.5;Windows NT)",
"X-Amzn-Trace-Id": "Root=1-5f1e9458-03e22590f337243009a04b88"
},
"json": null,
"origin": "117.136.19.0",
"url": "http://httpbin.org/post"
}
观察发现我们成功设置了相应的data、headers、method,其中我们构造headers的"User-Agent"为"Mozilla/4.0(compatible; MISE 5.5;Windows NT)"是用来伪装成火狐浏览器。
同时,我们也发现我们依然用了urlopen(),只不过这次的参数不再是url,而是一个request类型的对象。
处理异常
URLError
URLError类来自urllib库的error模块,它继承自OSError类,是Error异常模块类的基类,由request模块产生的异常可以通过捕获这个类来处理。
from urllib import request, parse, error
import urllib
mydict = {
'name':'Small_Fish'}
mydata = bytes(urllib.parse.urlencode(mydict),encoding='utf8')
try:
response = urllib.request.urlopen('http://sbsbsbsbsb.com',data=mydata,timeout=0.1)
print(response.status)
print(response.read())
except error.URLError as e:
print(e.reason)
输出:
timed out
可以看到程序没有直接报错,而是打印出了异常信息,通过这样我们可以有效避免程序异常终止。
HTTPError
HTTPError异常类是URLError类的子类,专门用来处理HTTP请求错误。
HTTPError有三个属性:
- code:返回HTTP的状态码
- reason:返回错误的原因
- headers:返回请求头
from urllib import request, parse, error
import urllib
try:
response = urllib.request.urlopen('https://a.com')
print(response.status)
except error.HTTPError as e:
print(e.reason,e.code,e.Headers,sep='\n')
解析链接
urlparse()
API:urllib.parse.urlparse(urlstring,scheme=' ',allow_fragment=True)
该方法可以实现URL的识别和分段。
from urllib.parse import urlparse
res = urlparse('http://www.baidu.com/index/html;user?id=5#comment')
print(type(res),res,sep='\n')
输出如下:
<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
- scheme:协议(http、https)
- netloc:域名,第一个/符合前便是netloc
- path::访问路径
- params:参数, ;号后面便是params
- query:查询条件,?后是查询条件
- fragment:锚点,用于直接定位页面内部的下拉位置
因此我们得到了一个标准的链接格式:scheme://netloc/path;params?query#fragment
urlunparse()
urlunparse()构造一个URL,接受的参数是一个可迭代对象,但必须长度为6.
from urllib.parse import urlunparse
data = ['http','www.baidu.com','index.html','user','id=6','comment']
res = urlunparse(data)
print(type(res),res,sep='\n')
输出如下:
<class 'str'>
http://www.baidu.com/index.html;user?id=6#comment
返回的结果为str类型的一个对象,并且成功构造了相应的URL。
urlsplit()
urlsplit()与urlparse()非常相似,只不过不在单独解析params,只返回5个结果。
from urllib.parse import urlsplit
res = urlsplit('http://www.baidu.com/index/html;user?id=5#comment')
print(type(res),res,sep='\n')
输出如下:
<class 'urllib.parse.SplitResult'>
SplitResult(scheme='http', netloc='www.baidu.com', path='/index/html;user', query='id=5', fragment='comment')
由结果可知uesr被加入path中了。
urlunsplit()
urlunsplit()构造一个URL,接受的参数是一个可迭代对象,但必须长度为,类似urlunparse()就不再放代码了。
urljoin()
urljoin()就是用一个链接(参数1)对另外一个新链接(参数2)缺失部分进行补充,最后返回结果。
from urllib.parse import urljoin
res = urljoin('http://www.baidu.com/index.php?id=5#comment','//www.tianmao.com')
print(res)
输出:
http://www.tianmao.com
urlencode()
urlencode()常常用于构造GET请求参数。
from urllib.parse import urlencode
dicts = {
'name':'Small_Fish',
'years':21}
base_url = 'http://www.baidu.com/'
url = base_url + urlencode(dicts)
print(url)
输出:
http://www.baidu.com/name=Small_Fish&years=21
这个方法非常常用。有时为了更加方便地构造参数,我们会事先用字典来表示。要转化为URL参数时,只需要调用方法就行。
quote()和unquote()
quote()该方法可以将内容转化为URL编码的格式。因为URL中带有中文参数时,可能会导致乱码的问题,利用此方法可以将中文转化为URL编码。
相应的unquote()可以对URL编码进行解码。
from urllib.parse import urlencode,quote,unquote
name = '帅哥'
base_url = 'http://www.baidu.com/'
print(base_url+name)
url = base_url +quote(name)
print(url)
print(unquote(url))
输出如下:
http://www.baidu.com/帅哥
http://www.baidu.com/%E5%B8%85%E5%93%A5
http://www.baidu.com/帅哥
如上便实现了编码和解码的过程。
Python爬虫之requests库的使用
requests库是在python3爬虫中相对于urllib库更强大的一个库,有了它我们可以轻松进行Cookies、登录验证、代理设置等操作。
requests库的安装
windows下在命令行窗口下输入pip3 install requests
命令即可完成安装,非常简单。
requests的GET请求
HTTP中最常见的就是GET请求,下面介绍一下利用requests构建GET请求的方法。
requests.get()
requests中以GET方式请求网页的方法就是get,是不是非常直观呢?(相对于urllib库)
如↓:
import requests
response = requests.get('https://www.baidu.com')
print(type(response))
print(response.status_code)
print(response.text)
输出如下:
<class 'requests.models.Response'>
200
<!DOCTYPE html>
<!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8><meta http-equiv=X-UA-Compatible content=IE=Edge><meta content=always name=referrer><link rel=stylesheet type=text/css href=https://ss1.bdstatic.com/5eN1bjq8AAUYm2zgoY3K/r/www/cache/bdorz/baidu.min.c
....#省略OTZ
src=//www.baidu.com/img/gs.gif> </p> </div> </div> </div> </body> </html>
可见requests.get()返回的是一个Response类型的对象,同时也可以调用其状态码、内容等属性了解相应结果。
如果我们想向请求中添加参数,通过get()方法也可以很轻松实现:
import requests
param = {
'name':'Small_Fish',
'years':21
}
headers ={
'User-Agent':'Mozilla/5.0',
}
response = requests.get('http://httpbin.org/get',params = param,headers=headers)
print(type(response))
print(response.status_code)
print(response.text)
输出如下:
<class 'requests.models.Response'>
200
{
"args": {
"name": "Small_Fish",
"years": "21"
},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Host": "httpbin.org",
"User-Agent": "Mozilla/5.0",
"X-Amzn-Trace-Id": "Root=1-5f1fcc0b-b1818391d5a51ffc4dd76471"
},
"origin": "117.136.19.0",
"url": "http://httpbin.org/get?name=Small_Fish&years=21"
}
requests的POST请求
另外一种比较常见的请求方式是POST请求,使用方法也非常简单。
import requests
param = {
'name':'Small_Fish',
'years':'21'
}
response = requests.post('http://httpbin.org/post',data = param)
print(response.text)
输出如下:
{
"args": {},
"data": "",
"files": {},
"form": {
"name": "Small_Fish",
"years": "21"
},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Content-Length": "24",
"Content-Type": "application/x-www-form-urlencoded",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.24.0",
"X-Amzn-Trace-Id": "Root=1-5f1fcdba-bd70c740c324e9401d5cf270"
},
"json": null,
"origin": "117.136.19.0",
"url": "http://httpbin.org/post"
}