本篇随便记录学习崔庆才老师编著的《Python3 网络爬虫开发实战》以及urllib标准库使用
urllib库是Python内置的HTTP请求库,包含四个模块:
- request:最基本的HTTP请求模块,可以用来模拟发送请求。
- error:异常处理模块,包含request模块HTTP请求过程抛出的异常
- parse:一个工具模块,提供了许多URL处理的方法,比如拆、解析、合并等
- robotparser:主要用来识别网站的robot.txt文件,然后判断网站哪些部分可以爬取
发送请求
urllib.request.urlopen()
urllib.request.urlopen(url, data=None, [timeout, ]*, cafile=None, capath=None, cadefault=False, context=None)
- url:请求的网址,必选参数
- data:可选参数,如果设定,则该请求为POST请求方法。data必须为字节流数据(可用内置函数bytes()方法转换为字节流),数据内容如果是字典,可以通过parse模块的urlencode()方法将其转换为url格式的字符串
- timeout:可选参数,设置请求超时时间,单位为秒
- cafile:可选参数,CA证书文件
- capath:CA证书路径
- cadefault:python3中已经弃用
- context:可选参数,指定SSL设置,必须为ssl.SSLContext类型
import urllib.request import urllib.parse name = bytes(urllib.parse.urlencode({"name":"Jane"}),encoding = "utf-8") response = urllib.request.urlopen("https://www.python.org",data = name,timeout = 1)
urllib.request.Request()
urllib.request.open()方法可以实现最基本的请求,但是该方法中只有几个参数,有时候不能满足我们的要求。因此我们可以通过urllib.request.Request()构建一个请求
Request(url, data=None, headers={}, origin_req_host=None, unverifiable=False, method=None)
- url:必选参数,请求的网址
- data:可选参数,如果设定,则该请求为POST请求方法。data必须为字节流数据(可用内置函数bytes()方法转换为字节流),数据内容如果是字典,可以通过parse模块的urlencode()方法将其转换为url格式的字符串
- headers:可选参数,请求头
- origin_req_host:客户端主机名称或者IP地址
- unverifiable:用户没有足够的权限接收这个请求的结果,默认为False,即我们有权限接收
- method:请求方法
from urllib import request,parse headers = { "User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36", "accept-encoding":"gzip", "Cookie":"_ga=GA1.2.1498502246.1545274514" } data = bytes(parse.urlencode({"Language":"Python"}),encoding = "utf-8") my_request = request.Request("https://www.python.org",headers = headers,data = data,method = "POST") response = request.urlopen(my_request)
handler和opener
常用的handler:
- HTTPDefaultErrorHandler:用于处理HTTP响应错误,错误都会抛出HTTPError类型的异常
- HTTPRedirectHandler:用于处理重定向
- HTTPCookieProcessor:用于处理Cookies
- ProxyHandler:用于设置代理,默认代理为空
- HTTPPasswordMgr:用于管理密码,维护用户名与密码的表单
- HTTPBasicAuthHandler:用于管理认证,如果打开一个连接需要认真,那么可以用它来解决认证的问题
- HTTPHandler:用来用处URL
opener:
urllib.request.
build_opener
([handler, ...]):实例化一个opener,使用各种功能的处理器urllib.request.
install_opener
(opener) :设定一个全局opener,即程序里所有的请求都调用该opener
自定义的一般步骤:
- 使用相关功能的Handle处理器,来创建特定功能的处理器对象
- 然后通过如request.build_opener()方法,来使用这些处理器对象,创建自定义opener对象
- 使用自定义的opener对象,调用其open()方法向服务器发送请求
from urllib import request http_handler = request.HTTPHandler() # 实例化一个具有处理url功能的处理器 my_opener = request.build_opener(http_handler) # 创建一个能使用该处理器的opener,此时该opener就具备了处理url功能 response = my_opener.open("https://www.python.org")
# 设置代理 from urllib import request my_proxy = {"http":"120.76.55.49:8088"} proxy_handler = request.ProxyHandler(my_proxy) proxy_opener = request.build_opener(proxy_handler) response = proxy_opener.open("https://www.python.org") #如果有多个代理 from urllib import request import random my_proxies = [ {"http" : "124.88.67.81:80"}, {"http" : "124.88.67.81:80"}, {"http" : "124.88.67.81:80"}, {"http" : "124.88.67.81:80"}, {"http" : "124.88.67.81:80"} ] proxy = random.choice(my_proxies) proxy_handler = request.ProxyHandler(proxy) proxy_opener = request.build_opener(proxy_handler) response = proxy_opener.open("https://www.python.org")
平时我们设置Cookies都是在输入账户、密码登录之后,然后在浏览器保存这些信息(Cookies),然后再去查看源码找到这个Cookies(抓包),然后将它加入到请求头,根据源码再构建的请求。
而用handler的话,对于python2来说可以用cookielib模块和urllib.request.HTTPCookieProcessor来处理,对于cookielib模块有四个对象。在python3中,替换为http.cookiejar库,四个子类,同python2四个对象相同:
- CookieJar:管理HTTP cookie值、存储HTTP请求生成的cookie、向传出的HTTP请求添加cookie的对象。整个cookie都存储在内存中,对CookieJar实例进行垃圾回收后cookie也将丢失。
- FileCookieJar:FileCookieJar(filename,delayload=None,policy=None):从CookieJar派生而来,用来创建FileCookieJar实例,检索cookie信息并将cookie存储到文件中。filename是存储cookie的文件名。delayload为True时支持延迟访问文件,即只有在需要时才读取文件或在文件中存储数据。
- MozillaCookieJar:MozillaCookieJar(filename, delayload=None, policy=None):从FileCookieJar派生而来,创建与Mozilla浏览器 cookies.txt兼容的FileCookieJar实例
- LWPCookieJar:LWPCookieJar(filename, delay=None, policy=None):从FileCookieJar派生而来,创建与libwww-perl标准的Set-Cookie3文件格式兼容的FileCookieJar实例。
python2中基本用法:
from urllib import request import cookielib cookiejar = cookielib.CookieJar() # 创建一个Cookiejar对象来保存cookie cookie_handler = request.HTTPCookieProcessor(cookiejar) # 创建一个cookie处理器对象 my_opener = request.build_opener(cookie_handler) # 创建一个opener来使用这个处理器 response = my_opener.open("https://www.baidu.com")
python3中基本用法(CookieJar()):
from urllib import request from http import cookiejar my_cookiejar = cookiejar.CookieJar() # CookieJar()可以管理,储存HTTP请求生成的cookie,我们把这个保存的cookie实例化 my_cookiehandler = request.HTTPCookieProcessor(my_cookiejar) # 创建一个处理cookiejar的处理器,也就是处理器处理这个cookie opener = request.build_opener(my_cookiehandler) # 创建一个opener,此时这个opener就包含了这个cookie response = opener.open("https://www.baidu.com")
大多数情况我们使用CookieJar(),但是如果需要同本地文件进行交互(也就是从本地读取,或者在本地创建),那么可以使用MozillaCookieJar()和LWPCookieJar(),两者用法一样,只需要将MozillaCookieJar和LWPCookieJar名称互换
from urllib import request from http import cookiejar my_cookiejar = cookiejar.MozillaCookieJar(file) # 创建一个符合Mozilla浏览器标准的cookiejar对象,叫做my_cookiejar,此时它是一个对象,一个MozillaCookieJar实例化对象。file,即我们要创建的cookie文件的文件名字 my_cookiehandler = request.HTTPCookieProsessor(my_cookiejar) # 创建一个处理这个cookiejar实例的处理器对象 openr = request.build_opener(my_cookiehandler) # 创建一个opener,来使用这个处理器对象 response = opener.open("https://www.baidu.com") # sava()将这个实例化对象my_cookiejar包含的cookies信息,保存成本地文件,文件名之前已经确定了。另外,还可以通过load()方法从一个文件导入cookies # save()方法中两个参数,ignore_discard:保存需要被丢弃的cookies;ignore_expires:保存过期的cookies # 通过下面代码可以发现本地电脑中创建了一个名为cookie的文本文件 my_cookiejar.save(ignore_discard = True,ignore_expires = True)
对于某些网站需要输入账户、密码才能访问,此处我们可以用HTTPPasswordMgrWithDefaultReadlm(管理账户、密码)、HTTPBasicAuthHandler
from urllib import request username = "Maria" password = "123456" np = request.HTTPPasswordMgrWithDefaultReadlm() # 创建一个账户密码管理器对象 np.add_password(username,password) # 账户密码管理器中加入账户密码 auth_handler = request.HTTPBasicAuthHandler(np) # 创建一个验证该账户密码的处理器对象 opener = request.build_opener(auth_handler) response = opener.open("https://www.baidu.com")
异常
异常:urrlib库的error模块定义了由request模块产生的异常,如果request模块应用过程出现了问题,request模块便会抛出error模块定义的异常。即,error模块能捕获request模块使用过程中出现的问题,error模块主要有三类:
- ContentTooShortError
- HTTPError
- URLError
urllib.error.URLError
当各种处理器(handler)运行出现问题的时候,一般是没有网络连接或者是无法连接服务器处理器会抛出异常。它是OSError的子类,它具有一个属性:reason
reason:这个错误的原因,它可以是一个字符信息或者是一个异常实
urllib.error.HTTPError
它是URLError的子类,这一种异常通常是连接服务器后,服务器响应出现问题,如返回错误信息或者重定向错误,网页验证错误等等。虽然它是一个异常,但是它也可以像非异常文件样的函数方法一样返回(像urlopen()方法返回值一样),这在处理外部HTTPError非常有用,比如 说验证请求错误。这个类包含三个属性:code、reason、headers
code:HTTP状态码
reason:通常是一个字符串信息,解释这个错误的原因
headers:导致HTTPError的HTTP请求后服务器返回的HTTP响应的头部信息。
urllib.error.ContentTooShort
当urlretrieve()函数检测到下载的数据量小于预期(这个预期在头部信息,Content-Length给定)时,抛出该异常。其中,content属性储存着下载的(或删减过后的)数据
from urllib import request from urllib import error try: response = request.urlopen("https://www.sdkfjla.com") except error.URLError as ue: print(ue.reson) print(ue) except error.HTTPError as he: print(he.code) print(he.reason) print(he.headers) print(he) except Exception as e: print(e)
解析链接
urlparse()
将一个URL链接解析为6部分:<协议>://<域名>/<路径>;<访问参数>?<查询条件>#<锚点>。结果返回包含6个元素的元组。
urlparse(url, scheme='', allow_fragments=True) Parse a URL into 6 components: <scheme>://<netloc>/<path>;<params>?<query>#<gragment> Return a 6-tuple: (scheme, netloc, path, params, query, fragment). Note that we don't break the components up in smaller bits (e.g. netloc is a single string) and we don't expand % escape.
- url:必选参数,即带解析的URL
- scheme:默认的协议,如果待解析的URL不含协议信息,则会将该参数作为默认的协议
- allow_fragment:即是否忽略fragment。如果设置为False,则fragment则会被忽略,而原本该解析为fragment的这一部分会被解析为path、parameters或者query的一部分,fragment则显示为空
from urllib import parse result = parse.urlparse("https://i.cnblogs.com/EditPosts.aspx?postid=10813489&update=1") print(type(result),result)
result1 = parse.urlparse("http://www .baidu .com/index .html;user?id=5#commen",allow_fragment = False)
print(result1)
运行结果:
<class 'urllib.parse.ParseResult'> ParseResult(scheme='https', netloc='i.cnblogs.com', path='/EditPosts.aspx', params='', query='postid=10813489&update=1', fragment='')
ParseResult(scheme='http', netloc='www,baidu.com', path='/index.html', params='user', query='5#commen', fragment='')
urlunparse()
与urlparse()正好相反,构造一个URL链接,其接收的参数是一个可迭代对象,但是它的长度必须为6,否则会抛出参数数量不足或者过多的问题。
urlunparse(components) Put a parsed URL back together again. This may result in a slightly different, but equivalent URL,
if the URL that was parsed originally had redundant delimiters,
e.g. a ? with an empty query (the draft states that these are equivalent).
如:
from urllib import parse components = ["http","www.baidu.com","index.html","user","id=5","comment"] URL = parse.urlunparse(components) print(URL)
运行结果为:
"http://www.baidu.com/index.html;user?id=5#comment"
urlsplit()
这个方法和urlparse()非常相似,只不过它不再单独解析params这部分,只返回5个结果。
from urllib import parse result = parse.urlsplit("http://www .baidu .com/index .html;user?id=5#commen") print(result)
运行结果:
SplitResult(scheme='http', netloc='www.baidu.com', path='/index.html;user', query='id=5', fragment='comment')
urlunsplit()
该方法与urlunparse()非常相似,正好与urlsplit()相反。将链接各组分组合成一个完整的链接,传入的参数是一个可迭代对象,但是长度必须为5
from urllib import parse components = ("http","www.baidu.com","/index.html;name=Jane","id=5","comment") URL = parse.urlunsplit(components) print(URL)
运行结果:
http://www.baidu.com/index.html;name=Jane?id=5#comment
urljoin()
urljoin(base, url, allow_fragments=True) Join a base URL and a possibly relative URL to form an absolute interpretation of the latter.
- base:基础链接
- url:待构建的一个链接
- allow_fragment:是否包含锚点
首先提供一个基础链接作为第一个参数,将一个新链接作为第二个参数,该方法会分析基础链接的scheme、netloc、path这三个部分内容,然后根据这三个组分对新链接中的缺失部分进行补充,让后者形成一个完整的链接
from urllib import parse URL = parse.urljoin("https://www.baidu.com","/index.html;name=Jane?id=2#comment") print(URL)
运行结果:
https://www.baidu.com/index.html;name=Jane?id=2#comment
urlencode()
常用来构建GET请求参数,即标准链接中的params组分。为了方便我们常会现用字典表示参数,然后用该方法将它构建为URL中的params组分。
from urllib import parse infomation = { "Maria":15, "Jane":16, "Mecheal":16 } base_url = "https://www.baidu.com?" new_url = base_url+parse.urlencode(infomation) print(new_url)
运行结果:
https://www.baidu.com?Maria=15&Jane=16&Mecheal=16
parse_qs()
反序列化,假如我们有一串GET请求参数,利用parse_qs()方法,我们可以将这些参数转回为字典
from urllib import parse params = "Maria=15&Jane=16&Mecheal=16" print(parse.parse_qs(params))
运行结果:
{'Maria': ['15'], 'Jane': ['16'], 'Mecheal': ['16']}
parse_qsl()
同parse_qs()方法相似,只是parse_qsl()方法是将请求参数转化为元组组成的列表
from urllib import parse params = "Maria=15&Jane=16&Mecheal=16" print(parse.parse_qsl(params))
运行结果:
[('Maria', '15'), ('Jane', '16'), ('Mecheal', '16')]
quote()
该方法可以将内容转化为URL编码,如URL中带有中文参数时,可能会出现乱码问题,我们可以用该方法将中文字符转换为URL编码
from urllib import parse keyword = "冯宝宝" url = "https://www.baidu.com/seach?wd="+parse.quote(keyword) print(url)
运行结果:
https://www.baidu.com/seach?wd=%E5%86%AF%E5%AE%9D%E5%AE%9D
unquote()
与quote()方法相反,unquote()方法可以将URL中的中文编码解析还原为中文
from urllib import parse url = "https://www.baidu.com/seach?wd=%E5%86%AF%E5%AE%9D%E5%AE%9D" print(parse.unquote(url))
运行结果:
https://www.baidu.com/seach?wd=冯宝宝
解析Robots协议
Robots协议,全称为网络爬虫排除标准(Robots Exclusion Protocol),用来告诉爬虫或者搜索引擎哪些页面可以抓取,哪些页面不可以抓取。通常是一个Robots.txt文件,一般放在网站的根目录下
robotparser.RobotFileParser类
该类提供了很多方法:
- can_fetch(self, useragent, url):返回的内容是该搜索引擎是否可以抓取这个 URL ,返回结果是 True 或 False
- modified(self):将当前时间设置为上次抓取和分析robot.text文件的时间
- mtime(self):返回的是上次抓取和分析robot.text文件的时间
- parse(self, lines):用来解析 robots. txt 文件,传人的参数是 robots .txt某些行的内容 ,它会按照 robots . txt的语法规则来分析这些内容
- read(self):读取 robots . txt 文件并进行分析 。 注意,这个方法执行一个读取和分析操作,如果不调用这个方法 , 接下来的判断都会为 False ,所以一定记得调用这个方法 。 这个方法不会返回任何内容,但是执行了读取操作
from urllib.robotparser import RobotFileParser rp = RobotFileParser() # 对RobotFileParser类实例化 rp.set_url("https://www.taobao.com/robots.text") # 设置robot.txt文件链接 rp.read() #读取该robot.txt文件并分析 # 分析我们是否能抓取该网站的某个页面 print(rp.can_fetch("*","https://www.taobao.com/markets/3c/tbdc?spm=a21bo.2017.201867-main.12.5af911d9DPQkf4"))
运行结果:
False