爬虫系列-urllib基本库的使用

学习爬虫,最基础便是模拟浏览器向服务器发出请求,请求需要来我们自己来构造?以及如何构造?具体怎么实现?服务器的响应和应答原理是什么样的?
可能我们对这些一无所知,但python提供了功能齐全的类库来帮助我们完成这些请求。常用的HTTP库有urlllib.requests.trep等。
而我们今天主要介绍的就是urllib库,这个库只需要我们传入相应的参数和相应的链接。首先我给出官方文档的链接,所有的学习都是基于官方文档来的。

urllib库基本介绍

python内置的的HTTP请求库,不需要额外安装,主要分为下面四个模块:
1.urllib.request 用于打开和请求URL
2.urllib.error 异常处理模块,如果出现请求错误,我们可以捕获这些异常,进行重试防止意外终止
3.urllib.parse 用于解析URL,合并,拆分
4.urllib.robotparser用于识别robots.txt文件,判断哪些网站可以爬,哪些不可以爬,它其实用的比较少。

1.发送请求

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

urlopen()函数

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

import urllib.request
response =urllib.request.urlopen('https://docs.python.org/3/library/urllib.html')#请求urllib官方文档为例子
print(response.read().decode('utf-8'))#打印出返回的结果

这里我截取部分结果,这个就是返回的网页源代码。
在这里插入图片描述这里我们用了两行代码就获取了网页的源代码,那么我们想要的信息就可以直接从里面提取出来。我们也可以看一下返回的到底是什么?我们可以用type()查看响应的类型。

print(type(response))
#<class 'http.client.HTTPResponse'>

可以发现,它是一个HTTPResponse类型的对象,主要包含read()、readinto()、getheader(name)、getheaders()、fileno()等方法,以及msg、version、status、reason、debuglevel、closed等属性。当我们请得到这个对象,然后赋值就可以调用这些属性和方法,得到返回的结果的一系列信息了。
例如利用status、getheaders方法可以查看输出了响应的状态和响应的头信息。

print(response.status)#查看返回的状态码,比如200表示请求正常,404表示网页未找到
print(response.getheaders())#查看响应的信息
print(response.getheader('Sever'))#查看服务器是用什么搭建的(一般不常用)

这是我返回的结果,状态码返回200表示正常请求到了。其他就是返回的头信息。
在这里插入图片描述
利用最基本的urlopen()函数,可以完成最基本的GET请求抓取。但urlopen()函数的功能其实不止这样还有很多,比如post请求等以及一些相关参数。
urlopen()常用的参数也是下面三个,其他基本很少涉及,有需要的话可以看官网详解。

urlopen(url, data, timeout)

第一个参数url即为URL,第二个参数data是访问URL时要传送的数据,第三个timeout是设置超时时间。
第二三个参数是可以不传送的,data默认为空None,timeout默认为 socket._GLOBAL_DEFAULT_TIMEOUT
第一个参数URL是必须要传送的,在这个例子里面我们传送了百度的URL,执行urlopen方法之后,返回一个response对象,返回信息便保存在这里面。
下面我举一个例子需要post请求的,大家可能会问post请求是什么,通俗的来说就是发送表单信息,相当于发送一些你个人信息什么,比如登录的时候。但现在post请求一般也很少见,我找一个简单 的例子给大家看一下。

import urllib.request
import urllib.parse
url='http://www.iqianyue.com/mypost'
postdata =urllib.parse.urlencode({'name':"zf","pass":123456}).encode('utf-8')#将表单以j字典形式发哦送
r =urllib.request.Request(url,postdata)
data =urllib.request.urlopen(r).read()#此时的r是一个request对象
print(data)

在这里插入图片描述
这上面的request.Request和parse.urlencode我后面会一一讲解。大家主要就是了解post请求的形式。
urlopen()函数中的第三个参数timeout,是用于设置超时时间,意思就是在请求超出这个时间会抛出异常,如果不设定的话采用默认时间,这个参数针对一些请求需要时间比较长的网站是很有效的。
下面这个是我做的一个实验,和之前那个例子相比,我就改了一下timeout参数就报错了,说请求超时。
在这里插入图片描述在urlopen()函数中除了这几个参数外,还有context参数,它必须是ssl.SSLContext类型用来指定SSL设置。此外还有cafile和capath两个参数指定CA证书和它的路径,这个在请求HTTPS链接会很有用。想了解更多可以看看官方文档

2.Request

我们知道利用urlopen()方法可以实现最基本请求的发起,只不过这次参数不再是URL,而是一个Request类型的对象,通过构造这个数据结构,一方面我们可以将请求独立成一个对象,另外一方面也可以更加丰富和灵活配置参数。
函数参数大致如下:

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

Request这个类可以设置请求头等参数,可以将请求构造更加完整。

import urllib.request
request =urllib.request.Request('https://docs.python.org/3/library/urllib.html')
response =urllib.request.urlopen(request)
print(response.read().decode('utf-8'))

Request中的第一个参数url用于请求URL,这是必传参数,其他都是可选参数。第二个参数data如果要传,必须传bytes(字节流)类型。如果它是字典,先用urllib.parse模块里的urlencode()编码。第三个参数headers是一个字典,它就是请求头。可以直接构造或者通过实例的add_header()方法添加。第四个参数origin_req_host 指的是请求方的host名称或者IP地址。第五个第五个参数unverifiable表示这个请求是否是无法验证的,默认是False,意思就是说用户没有足够权限来选择接收这个请求的结果。例如,我们请求一个HTML文档中的图片,但是我们没有自动抓取图像的权限,这时unverifiable的值就是True。第六个参数method是一个字符串,用来指示请求使用的方法,比如GET、POST和PUT等。
下面我们看一个例子:

from urllib import request, parse

url = 'http://httpbin.org/post'
headers = {
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10; rv:33.0) Gecko/20100101 Firefox/33.0',
    'Host': 'httpbin.org'
}
dict = {
    'name': 'Germey'
}
data = bytes(parse.urlencode(dict), encoding='utf8')
req = request.Request(url=url, data=data, headers=headers, method='POST')
response = request.urlopen(req)
print(response.read().decode('utf-8'))

在这里插入图片描述
这个例子中我们定义了headers,data,method,url这些参数,指定了请求方式为post。
另外,headers也可以用add_header()方法来添加:

req = request.Request(url=url, data=data, method='POST')
req.add_header('User-Agent', 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)')
Cookies

Cookies指某些网站为了辨别用户身份、进行会话跟踪而存储在用户本地终端上的数据。通俗的来说也就是一个用户身份的ID一样,保存着我们的信息。比如登录之后,浏览器可能下次就直接利用这个cookies,下次就不需要登录。
那么cookies的获取怎么做,举个例子:

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)

HTTPCookieProcessor这个类用于处理Cookies。首先,我们必须声明一个CookieJar对象。接下来,就需要利用HTTPCookieProcessor来构建一个Handler,最后利用build_opener()方法构建出Opener,执行open()函数即可。

BAIDUID=852B733A72A7E6DA5CEC9ED6C4B7A9B6:FG=1
BIDUPSID=852B733A72A7E6DA5CEC9ED6C4B7A9B6
H_PS_PSSID=1434_21102_29523_29521_29720_29567_29220_28701
PSTM=1568728617
delPer=0
BDSVRTM=0
BD_HOME=0

这里输出了每条cookies的名称和值。但有的时候我们希望能够将上面的cookies保存下来。

filename = 'cookies.txt'
cookie = http.cookiejar.MozillaCookieJar(filename)
handler = urllib.request.HTTPCookieProcessor(cookie)
opener = urllib.request.build_opener(handler)
response = opener.open('http://www.baidu.com')
cookie.save(ignore_discard=True, ignore_expires=True)

这时CookieJar就需要换成MozillaCookieJar,它在生成文件时会用到,是CookieJar的子类,可以用来处理Cookies和文件相关的事件,比如读取和保存Cookies,可以将Cookies保存成Mozilla型浏览器的Cookies格式。

运行之后,可以发现生成了一个cookies.txt文件,其内容如下:
在这里插入图片描述
通过保存的cookies我们下次可以直接从文件中读取。但具体操作我就不过多的介绍了。大家可以看官方文档,urllib.request的功能还是很多,想具体了解可以去官网学习。

处理异常

1.URLError
上面我们大致了解了请求的过程,但是有的时候网络问题或者其他问题导致出现了异常,该怎么办呢?这时如果不处理这些异常,程序很可能因报错而终止运行,所以异常处理还是十分有必要的。
urllib中的error模块定义了由request模块产生的异常。如果出现问题,request模块便会抛出error模块中定义的异常。
下面举个例子,比如我把网址写错了:

from urllib import request, error
try:
    response = request.urlopen('https://docs.python.org/3/library/urllib.htm')
except error.URLError as e:
    print(e.reason)

返回的是:

Not Found

程序没有报错,这样我们避免程序异常终止。
2.HTTPError
它是URLError的子类,专门用来处理HTTP请求错误,比如认证请求失败等。它有如下3个属性。
code:返回HTTP状态码,比如404表示网页不存在,500表示服务器内部错误等。
reason:同父类一样,用于返回错误的原因。
headers:返回请求头。
我们还是看一个例子:

from urllib import request,error
try:
    response = request.urlopen('https://docs.python.org/3/library/urllib.htm')
except error.HTTPError as e:
    print(e.reason, e.code, e.headers, sep='\n')

返回的是:

Not Found
404
Server: nginx
Content-Type: text/html
X-Clacks-Overhead: GNU Terry Pratchett
Strict-Transport-Security: max-age=315360000; includeSubDomains; preload
Via: 1.1 varnish
Content-Length: 162
Accept-Ranges: bytes
Date: Tue, 17 Sep 2019 14:28:24 GMT
Via: 1.1 varnish
Age: 200
Connection: close
X-Served-By: cache-jfk8123-JFK, cache-hnd18738-HND
X-Cache: MISS, HIT
X-Cache-Hits: 0, 1
X-Timer: S1568730505.660194,VS0,VE7
Vary: Accept-Encoding

依然是同样的网址,这里捕获了HTTPError异常,输出了reason、code和headers属性。有很多出现错误的可能,比如timeout超时等,其他更多具体的还是官网了解,这里我只是大致介绍一下。
这一块我还举一个超时的例子:

import socket
import urllib.request
import urllib.error

try:
    response = urllib.request.urlopen('https://www.baidu.com', timeout=0.01)
except urllib.error.URLError as e:
    print(type(e.reason))
    if isinstance(e.reason, socket.timeout):
        print('TIME OUT')

返回的是:

<class 'socket.timeout'>
TIME OUT

解析链接

urllib库里还提供了parse这个模块,它定义了处理URL的标准接口,例如实现URL各部分的抽取、合并以及链接转换。它支持如下协议的URL处理:file、ftp、gopher、hdl、http、https、imap、mailto、 mms、news、nntp、prospero、rsync、rtsp、rtspu、sftp、 sip、sips、snews、svn、svn+ssh、telnet和wais。
1.urlparse()
这个方法主要实现对url的识别和分段,这里还是看一个例子:

from urllib.parse import urlparse
result = urlparse('https://docs.python.org/3/library/urllib.html')
print(type(result), result)

返回包括六个部分,对url进行了分段:

<class 'urllib.parse.ParseResult'> ParseResult(scheme='https', netloc='docs.python.org', path='/3/library/urllib.html', params='', query='', fragment='')

可以发现,urlparse()方法将其拆分成了6部分。大体观察可以发现,解析时有特定的分隔符。比如,: //前面的就是scheme,代表协议;第一个/前面便是netloc,即域名;分号;前面是params,代表参数。所以我们可以得出标准的链接格式:

scheme://netloc/path;parameters?query#fragment

一个标准的url都会符合这个规则,利用urlparse()方法可以将它拆分开来。
除了这种最基本的解析方式外,urlparse()方法还有其他配置

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

urlstring:这是必填项,即待解析的URL。
scheme:它是默认的协议(比如http或https等)。假如这个链接没有带协议信息,会将这个作为默认的协议。
allow_fragments:即是否忽略fragment。如果它被设置为False,fragment部分就会被忽略,它会被解析为path、parameters或者query的一部分,而fragment部分为空。
举一个例子:

urlparse('http://www.baidu.com/index.html;user?id=5#comment', scheme='https')

返回的结果是这样的:

ParseResult(scheme='http', netloc='www.baidu.com', path='/index.html', params='user', query='id=5', fragment='comment')

如果我们没有带协议

from urllib.parse import urlparse
result = urlparse('www.baidu.com/index.html;user?id=5#comment', scheme='https')
print(result)

则返回的是这样的

ParseResult(scheme='https', netloc='', path='www.baidu.com/index.html', params='user', query='id=5', fragment='comment')

可见,scheme参数只有在URL中不包含scheme信息时才生效。如果URL中有scheme信息,就会返回解析出的scheme。
接下来我们来看看第三个参数:

from urllib.parse import urlparse
result = urlparse('http://www.baidu.com/index.html;user?id=5#comment', 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的一部分。
2.urlunparse()
有了urlparse(),相应地就有了它的对立方法urlunparse()。它接受的参数是一个可迭代对象,但是它的长度必须是6,否则会抛出参数数量不足或者过多的问题。先用一个实例看一下:

from urllib.parse import urlunparse
data = ['http', 'www.baidu.com', 'index.html', 'user', 'a=6', 'comment']
print(urlunparse(data))

返回的就是我们希望能够拼接成的url

http://www.baidu.com/index.html;user?a=6#comment

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

from urllib.parse import urlunsplit
data = ['http', 'www.baidu.com', 'index.html', 'a=6', 'comment']
print(urlunsplit(data))

拼接后的url如下:

http://www.baidu.com/index.html?a=6#comment

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

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

5.urlencode()
这里我们再介绍一个常用的方法——urlencode(),它在构造GET请求参数的时候非常有用,示例如下:

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

这个方法非常常用。有时为了更加方便地构造参数,我们会事先用字典来表示。要转化为URL的参数时,只需要调用该方法即可。
6.parse_qs()
既然有了构造,当然也可以分解,这个函数就是将其分解成字典:

from urllib.parse import parse_qs
query = 'name=germey&age=22'
print(parse_qs(query))

分解之后就是这样

{'name': ['germey'], 'age': ['22']}

除此之外我们还以将它转成元组组成的列表。parse_qsl()方法,它用于将参数转化为元组组成的列表。
7.quote()
该方法可以将内容转化为URL编码的格式。URL中带有中文参数时,有时可能会导致乱码的问题,此时用这个方法可以将中文字符转化为URL编码,示例如下:

from urllib.parse import quote
keyword = '我爱中国'
url = 'https://www.baidu.com/s?wd=' + quote(keyword)
print(url)
https://www.baidu.com/s?wd=%E6%88%91%E7%88%B1%E4%B8%AD%E5%9B%BD

这个在我们构建关键词,然后希望它可以一一的形成url然后去爬取是很有用的。当然我们可以对中文进行编码当然也可以对其进行解码。
8.unquote()
它可以进行URL解码,示例如下:

from urllib.parse import unquote
url = 'https://www.baidu.com/s?wd=%E6%88%91%E7%88%B1%E4%B8%AD%E5%9B%BD'
print(unquote(url))

解码之后:

https://www.baidu.com/s?wd=我爱中国

这一块我们主要了解了一些url处理方法,方便我们对url进行解析和构造。

Robots协议

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

当搜索爬虫访问一个站点时,它首先会检查这个站点根目录下是否存在robots.txt文件,如果存在,搜索爬虫会根据其中定义的爬取范围来爬取。如果没有找到这个文件,搜索爬虫便会访问所有可直接访问的页面。
这一块也是很重要的,大家自己看看了解,以防在法律边缘试探。
urllib基本库大部分常用的也是这些了,我是参考了一个网络爬虫大佬崔庆才书籍总结,大家有兴趣可以去看看,然后想了解更多可以去官网看看。

猜你喜欢

转载自blog.csdn.net/qq_42370313/article/details/100904753
今日推荐