urllib库是python内置的HTTP请求库,包含以下四个模块:
- request:最基本的HTTP请求模块,可以用来模拟发送请求。只需要给库方法传入URL以及额外的参数,就可以模拟实现这个过程了。
- error:异常处理模块,如果出现请求错误,我们可以捕获这些异常,然后进行重试或其他操作以保证程序不会意外终止。
- parse:工具模块,提供了许多URL处理方法,比如拆分、解析、合并等。
- robotparser(用得比较少):主要是用来识别网站的robots.txt文件,然后判断哪些网站可以爬,哪些网站不可以爬。
一、发送请求(request模块)
(1)urlopen()(实现最基本的发送请求,官方参考文档:https://docs.python.org/3/library/urllib.request.html)
import urllib.request
response=urllib.request.urlopen('https://www. python. org')
print(response. read(). decode(' utf-8'))
它是一个HTTPResposne类型的对象,主要包含read()、readinto()、getheader(name)、getheaders()、fileno()等方法,以及msg、version、status、reason、debugleve1、closed等属性。
urlopen()函数的API:
urllib.request.urlopen(url,data-None,[timeout,]*,cafile=None,capath=None,cadefault=False,context=None)
1、data参数
data参数是可选的。如果要添加该参数,并且如果它是字节流编码格式的内容,即bytes类型,则需要通过bytes()方法转化。另外,如果传递了这个参数,则它的请求方式就不再是GET方式,而是POST方式。
import urllib. parse
import urllib.request
data=bytes(urllib. parse. urlencode({' word':' hello'}), encoding=' utf-8')
response=urllib.request.urlopen('http://httpbin. org/post', data=data)
print(response. read())
bytes()方法的第一个参数需要是str(字符串)类型,需要用urllib.parse模块里的urlencode()方法来将参数字典转化为字符串;第二个参数指定编码格式,这里指定为utf8。
2、timeout参数
用于设置超时时间,单位为秒。如果请求超出了设置的这个时间,还没有得到响应,就会抛出异常。
设置超时时间过后,服务器依然没有响应,于是抛出了URLError异常,该异常属于urllib.error模块,错误原因是超时。用try except语句实现抓取异常。
3、其他参数
context参数,它必须是ss1.SSLContext类型,用来指定SSL设置。
cafile和capath这两个参数分别指定CA证书和它的路径,这个在请求HTTPS链接时会有用。
cadefault 参数现在已经弃用了,其默认值为False。
(2)Request(高级请求,如加入header等信息)
import urllib. request
request=urllib. request. Request("https://python. org')
response=urllib. request. urlopen(request)
print(response. read(). decode(' utf-8'))
依然用urlopen()方法来发送这个请求,只不过这次该方法的参数不再是URL,而是一个Request类型的对象。通过构造这个数据结构,一方面我们可以将请求独立成一个对象,另一方面可更加丰富和灵活地配置参数。
Ruquest()函数的API:
class urllib.request. Request(url, data-None, headers={}, origin req host-None, unverifiable-False, method-None)
- 参数url用于请求URL,这是必传参数,其他都是可选参数。
- 参数data如果要传,必须传bytes(字节流)类型的。如果它是字典,可以先用ur1lib.parse模块里的urlencode()编码。
- 参数headers是字典,就是请求头,=可以在构造请求时通过headers参数直接构造,也可以通过调用请求实例的add header()方法添加。
- 参数origin req host指的是请求方的host名称或者IP地址。
- 参数unverifiable表示这个请求是否是无法验证的,默认是False,意思就是说用户没有足够权限来选择接收这个请求的结果。例如,我们请求一个HTML文档中的图片,但是我们没有自动抓取图像的权限,这时unverifiable的值就是True。
- 参数method是一个字符串,用来指示请求使用的方法,比如GET、POST和PUT等。
(3)高级用法
1、Handler处理器(Cookies处理,代理设置)
urllib.request模块里的BaseHandler类,它是所有其他Handler的父类,它提供了最基本的方法,例如default_open()、protocol_request()等。
各种Handler子类继承这个BaseHandler类,比如:
- HTTPDefaultErrorHandler:用于处理HTTP响应错误,错误都会抛出HTTPError类型的异常。
- HTTPRedirectHandler:用于处理重定向。
- HTTPCookieProcessor:用于处理 Cookies。
- ProxyHandler:用于设置代理,默认代理为空。
- HTTPPasswordMgr:用于管理密码,它维护了用户名和密码的表。
- HTTPBasicAuthHandler:用于管理认证,如果一个链接打开时需要认证,那么可以用它来解决认证问题。
其他的Handler类,参考官方文档:https://docs.python.org/3/library/urllib.request.html#urllib.request.BaseHandler。
2、OpenerDirector(比Request模块更高级的功能)
OpenerDirector也称为Opener,之前用过urlopen()这个方法,实际上它就是urllib为我们提供的一个Opener。
Opener可以使用open()方法,返回的类型和urlopen()相同,利用Handler来构建Opener。
- 验证
from urllib.request import HTTPPasswordMgrWithDefaultRealm, HTTPBasicAuthHandler, build_opener
from urllib. error import URLError username=' username' password=' password'
url=' http://localhost:5000/'
p=HTTPPas swordMgrwithDefaultRealm()
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)
首先实例化HTTPBasicAuthHandler对象,其参数是HTTPPasswordMgrwithDefaultRealm对象,它利用add_password()添加进去用户名和密码,这样就建立了一个处理验证的Handler。
然后利用这个Handler并使用build_opener()方法构建一个Opener,这个Opener在发送请求时就相当于已经验证成功了。
最后利用0pener的open()方法打开链接,就可以完成验证了。这里获取到的结果就是验证后的页面源码内容。
- 代理
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)
使用了ProxyHandler,参数是一个字典,键名是协议类型(比如HTTP或者HTTPS等),键值是代理链接,可以添加多个代理。
然后利用这个Handler及build_opener()方法构造一个Opener,之后发送请求即可。
- 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)
首先必须声明一个CookieJar对,然后利用HTTPCookieProcessor来构建一个Handler,最后利用build_opener()方法构建出Opener,执行open()函数即可。
二、处理异常(error模块)
(1)URLError
URLError类来自urllib库的error模块,它继承自0SError类,是error异常模块的基类,由request模块生的异常都可以通过捕获这个类来处理。
from urllib import request, error
try:
response=request.urlopen(' https://cuiqingcai. com/index. htm')
except error. URLError as e:
print(e. reason)
如果打开一个不存在的网页会报错,但是捕获了这个异常之后,运行之后就会显示:Not Found,如果没有报错,程序就会正常运行,这样就避免了程序异常终止。
(2)HTTPError
HTTPError是URLErTor的子类,专门用来处理HTTP请求错误,比如认证请求失败等,它有如下3个属性:
code:返回HTTP状态码,比如404表示网页不存在,500表示服务器内部错误等。
reason:同父类一样用于返回错误的原因。
header:返回请求头。
from urllib import request, error
try:
response=request.urlopen("https://cuiqingcai. com/index. htm')
except error. HTTPError as e:
print(e. reason,e. code,e. headers, sep='\n')
except error. URLError as e:
print(e. reason)
else:
print(' Request Successfully")
由于URLError是HTTPError的父类,所以可以先选择捕获子类的错误,再去捕获父类的错误,这样就可以做到先捕获HTTPError,获取它的错误状态码、原因、headers等信息。如果不是HTTPError异常,就会捕获URLError异常,输出错误原因。最后,用else来处理正常的逻辑。
三、解析链接(parse模块)
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()
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.html',params='user',query='id=5',fragment='comment')
利用urlparse()方法进行了一个URL的解析。先输出了解析结果的类型,然后将结果也输出出来。返回结果是一个ParseResult类型的对象,它包含6个部分,分别是scheme、netloc、path、params、query和fragment。
标准的URL链接格式:scheme://netloc/path;params?query#fragment
urlparse()的API用法:
urllib.parse.urlparse(urlstring,scheme='",allow_fragments=True)
- urlstring:这是必填项,即待解析的URL。
- scheme:它是默认的协议(比如http或https等)。假如这个链接没有带协议信息,会将这个作为默认的协议。
- allowfragments:即是否忽略fragment。如果它被设置为False,fragment部分就会被忽略,它会被解析为path、parameters或者query的一部分,而fragment部分为空。
(2)urlunparse()
URL的构造urlunparse()接受的参数是一个可迭代对象,但是它的长度必须是6,否则会抛出参数数量不足或者过多的问题。
(3)urlsplit()
urlsplit()和urlparse()方法相似,只不过它不再单独解析params这一部分,只返回5个结果,params会合并到path中。
返回结果是SplitResult,也是一个元组类型,既可以用属性获取值,也可以用索引来获取。
(4)urlunsplit()
urlunsplit()也是将链接各个部分组合成完整链接的方法,传人的参数也是一个可迭代对象,例如列表、元组等,唯一的区别是长度必须为5。
(5)urljion()
urljoin()方法。我们可以提供一个base_url(基础链接)作为第一个参数,将新的链接作为第二个参数,该方法会分析base_ur1的scheme、netloc和path这3个内容并对新链接缺失的部分进行补充,最后返回结果。
from urllib.parse import urljoin
print(urljoin('http://www.baidu.com',‘FAQ.html'))
print(urljoin('http://www.baidu.com',‘https://cuiqingcai.com/FAQ.html'))
print(urljoin('http://www.baidu.com/about.html','https://cuiqingcai.com/FAQ.html'))
print(urljoin('http://www.baidu.com/about.html','https://cuiqingcai.com/FAQ.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=2'))
运行结果如下:
http://www.baidu.com/FAQ.html
https://cuiqingcai.com/FAQ.htmlhttps://cuiqingcai.com/FAQ.html
https://cuiqingcai.com/FAQ.html?question=2
https://cuiqingcai.com/index.php
http://www.baidu.com?category=2#comment
www.baidu.com?category=2#comment
www.baidu.com?category=2
base_ur1提供了三项内容scheme、netloc和path。如果这3项在新的链接里不存在,就予以补充;如果新的链接存在,就使用新的链接的部分。而base_ur1中的params、query 和fragment是不起作用的。
(6)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://ww.baidu.com?name=germey&age=22
参数就成功地由字典类型转化为GET请求参数了。
(7)parse_qs()
parse_qs()反序列化将它转化为字典。
(8)parse_qsl()
将参数转化为元组组成的列表。
(9)quote()
可以将内容转化为URL编码的格式。URL中带有中文参数时,有时可能会导致乱码的问题,此时用这个方法可以将中文字符转化为URL编码。
(10)unquote()
进行URL的解码。
三、分析Robots协议
robotparser模块可以实现对网站Robots协议的分析。
(1)Robots协议
Robots 协议也称作爬虫协议、机器人协议,它的全名叫作网络爬虫排除标准(Robots Exclusion Protocol),用来告诉爬虫和搜索引擎哪些页面可以抓取,哪些不可以抓取。它通常是一个叫作robots.txt的文本文件,一般放在网站的根目录下。
当搜索爬虫访问一个站点时,它首先会检查这个站点根目录下是否存在robots.txt文件,如果存在,搜索爬虫会根据其中定义的爬取范围来爬取。如果没有找到这个文件,搜索爬虫便会访问所有可直接访问的页面。
User-agent:*
Disallow:/
Al1ow:/public/
User-agent 描述了搜索爬虫的名称,*代表该协议对任何爬取爬虫有效。
Allow一般和Disallow一起使用,一般不会单独使用,用来排除某些限制。/public/表示所有页面不允许抓取,但可以抓取public目录。
(2)爬虫名称
爬虫 名称 名称网站
BaiduSpider 百度 www.baidu.com
Googlebot 谷歌 www.google.com
360Spider 360搜索 www.so.com
YodaoBot 有道 www.youdao.com
ia_archiver Alexa www.alexa.cn
Scooter altavista www.altavista.com
(3)robotparser
robotparser模块提供了一个类RobotFileParser来解析robots.txt,它可以根据某网站的robots.txt文件来判断一个爬取爬虫是否有权限来爬取这个网页。
RobotFileParser的声明:
urllib.robotparser.RobotFileParser(url=")
- set_url():用来设置robots.txt文件的链接。如果在创建RobotFileParser对象时传入了链接,那么就不需要再使用这个方法设置了。
- read():读取robots.txt文件并进行分析。注意,这个方法执行一个读取和分析操作,如果不调用这个方法,接下来的判断都会为False,所以一定记得调用这个方法。这个方法不会返回任何内容,但是执行了读取操作。
- parse():用来解析robots.txt文件,传人的参数是robots.txt某些行的内容,它会按照robots.txt的语法规则来分析这些内容。
- can_fetch():该方法传人两个参数,第一个是User-agent,第二个是要抓取的URL。返回的内容是该搜索引擎是否可以抓取这个URL,返回结果是True或False。
- mtime():返回的是上次抓取和分析robots.txt的时间,这对于长时间分析和抓取的搜索爬虫是很有必要的,你可能需要定期检查来抓取最新的robots.txt。
- modified():它同样对长时间分析和抓取的搜索爬虫很有帮助,将当前时间设置为上次抓取和分析robots.txt的时间。