(十五)Python爬虫基础库:urllib

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_25343557/article/details/81591368

      从今天起开始进入爬虫的世界。网络爬虫这种东西都了解能干嘛,我第一次知道时感觉特别棒,希望自己能写一个,不管功能强大与否。Python语言就是写爬虫的得力工具。我预计通过两篇博文来介绍一下Python基础库:urllib和requests(后者为重)。然后爬取豆瓣Top250电影信息。


      在Python2中实现发送请求的库有urllib和urllib2,到了Python3这两个库统一为了urllib。urllib包含如下4个模块:

  • request:它是最基本的HTTP请求模块,可以用来模拟发送请求。给予库方法URL和参数就可以顺利的发送请求,跟人工操作一样。(重点)
  • **error:**异常处理模块。使用它我们可以捕获爬取过程中产生的异常,从而防止程序的意外终止。
  • **parse:**工具模块。提供URL处理方法,譬如拆分,解析、合并等等。
  • **robotparser:**它主要用来识别robot.txt文件,判断哪些网站我们可以爬取,哪些又不可以。但是我们都是硬上的,即使设置了不可爬取也爬,任性。

urllib.request

urlopen()

**作用:**模拟浏览器发送一个请求,实现对目标url的访问。
语法:urllib.request.urlopen(url, data=None, [timeout, ]*, cafile=None, capath=None, cadefault=False, context=None)
参数:

  • **url参数:**目标资源在网络中的位置。可以是一个表示URL的字符串(如:http://www.python.org/),也可以是一个urllib.request对象。
  • data参数:data用来指明发往服务器请求中的额外的信息。HTTP是python中实现的众多网络通信http、https、ftp等协议中,唯一一个使用data参数的,也就是说只有打开的是http网址的时候,自定义data参数才会有作用。另外,官方API手册介绍指出:
    • data必须是一个字节数据对象(Python的bytes object);
    • data必须符合标准the standard application/x-www-form-urlencoded format,怎么得到这种标准结构的data呢?使用urllib.parse.urlencode()将自定义的data转换成标准格式,而这个函数所能接收的参数类型是pyhon中的mapping object(键/值对,如dict) or a sequence of two-element tuples(元素是tuple的列表);
    • data也可以是一个可迭代的对象,这种情况下就需要配置response对象中的Conten-length,指明data的大小;
    • data默认是None,此时以GET方式发送请求;当用户给出data参数的时候,改为POST方式发送请求。
  • **timeout参数:**设置超时时间,单位为秒。如果请求超出了设置的数据还没得到响应就好抛出异常。如果不指定该参数则使用全局默认时间。
  • **cafile、capath、cadefault 参数:**用于实现可信任的CA证书的HTTP请求。(基本上很少用)
  • **context参数:**实现SSL加密传输。(基本上很少用)

小试牛刀

现在我来爬取Python官网,把它的首页爬取下来。
import urllib.request

response = urllib.request.urlopen("https://python.org")
print(response.read().decode("UTF-8"))#使用utf-8对网页解码,如果出现错误,再加上"ignore"参数

部分结果:
这里写图片描述
      urlopen返回值是<class ‘http.client.HTTPResponse’>。它是HTTPResponse对象,主要包含read()、readinto()、getheader(name)、getheaders()、fileno()等方法,以及msg、version、status、reason、debuglevel、closed等属性。通过read()得到返回的内容,访问status属性得到返回码,200代表成功,404代表网页未找到。常用返回码可以查看:http://www.runoob.com/http/http-status-codes.html。
示例:

import urllib.request

response = urllib.request.urlopen("https://python.org")
print("返回码:",response.status)
print("获取所有请求头信息:",response.getheaders())
print("获取指定请求头信息:",response.getheader("Server"))

这里写图片描述

timeout参数示例

设置一个特别短的时间,制造异常:
import urllib.request

response = urllib.request.urlopen("https://python.org",timeout=0.1)#设置0.1s,这一定会超时
print(response.status)

这里写图片描述
有时候由于网络的不稳定等原因会导致请求超时,如果不设置对应的处理方法程序就终止了。我们可以使用try except语句捕获超时异常,从而跳过某一网站的爬取或重复爬取。

import urllib.request
from urllib.error import URLError
import socket

try:
    response = urllib.request.urlopen("https://python.org",timeout=0.1)
except URLError as e:
    if isinstance(e.reason,socket.timeout):
        print("请求超时,做其他处理")

这里写图片描述

传递Request对象

基础用法
      事实上,上面介绍的那个参数传递方法是不会使用的,除非你爬取的网站压根就不反爬。再不济我们都要给请求加上Headers等信息,这就需要Request类来构建。 首先将 小试牛刀代码改为:
import urllib.request

req = urllib.request.Request("https://python.org")#构建一个Request对象
response = urllib.request.urlopen(req)#传递Request对象
print(response.read().decode("UTF-8"))

还是同样的方法,只是不同的配方,传递Request对象了。
urllib.request.Request()详情如下:
语法:urllib.request.Request(url, data=None, headers={},origin_req_host=None, unverifiable=False,method=None)
参数:

  • **url:**必选参数。用于请求的URL。
  • **data:**传递的数据。如果要传必须传递bytes(字节流)类型的。如果它是字典类型我们可以使用urllib.parse模块里的urlencode()编码。
  • **headers:**设置请求头信息,这个参数我们会经常接触。如果不在这里设置我们也可以在后续使用add_header()方法添加。在请求头信息中我们最常设置的是User-Agent,默认的User-Agent是Python-urllib,这相当于自报家门:嘿!我是爬虫。我们可以修改它来伪装浏览器。比如我们要伪装火狐,我们可以设置为:Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36 QIHU 360SE
  • **origin_req_host:**设置请求方的host名称或IP地址。
  • **unverifiable:**表示该请求是否是无法验证的,默认是False。
  • **method:**表示这次请求使用的方法,比如GET、POST和PUT等。

现在我们伪装成浏览器发送POST请求,并且传递data:

import urllib.request
import urllib.parse

url = "https://httpbin.org/post" #https://httpbin.org可以供我们测试http请求
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36 QIHU 360SE'}#伪装成浏览器
dataDic = {'name':'Tom','age':20}
data = bytes(urllib.parse.urlencode(dataDic),'UTF-8')

req = urllib.request.Request(url,data=data,headers=headers,method='POST')#构建一个Request对象
response = urllib.request.urlopen(req)#传递Request对象
print(response.read().decode("UTF-8"))

这里写图片描述

高级用法
  • 如果爬虫使用某一IP爬取次数过多有的网站就认为是爬虫在爬取数据,狠点的直接将IP封禁了,那就凉了,对应的应对手段是使用代理IP爬取。
  • 有的网站我们是需要登录才能爬取的,我们需要爬虫能帮我们自动登录,借助HTTPBasicAuthHandler就可以完成。
  • 登录成功后一般会使用Cookie保存用户登录信息,所以我们需要保存cookie。

以上三种情况的处理我们都需要使用Handler来处理,Python提供了许多的Handler来帮助我们完成任务:

  • **HTTPDefaultErrorHandler:**用于处理HTTP响应错误,错误都会抛出HTTPError类型的异常。
  • **HTTPRedirectHandler:**用于处理重定向。
  • **HTTPCookieProcessor:**用于处理Cookies。
  • **ProxyHandler:**用于设置代理,默认代理为空。
  • **HTTPPasswordMgr:**用于管理密码,它维护了用户名和密码的表。
  • **HTTPBasicAuthHandler:**用于管理认证,如果一个链接打开时需要认证,那么可以用来解决认证问题。

还要很多的Handler,这就需要大家查看官方文档了。

现在有了Handler,但是我们如何使用呢?接下来再介绍一个重要的类-----OperDirector,或者Opener。Opener也可以调用open()方法,返回值和urlopen一样。记住一个要点:Handler构建Opener。

登录验证
![这里写图片描述](https://img-blog.csdn.net/20180812105503514?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzI1MzQzNTU3/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70) 遇到这种情况我们就需要进行验证了,示例代码如下:
from urllib.request import HTTPBasicAuthHandler,HTTPPasswordMgrWithDefaultRealm,build_opener
from urllib.error import URLError
import ssl

ssl._create_default_https_context = ssl._create_unverified_context#取消ssl验证
url="https://desktop-05hc07a:8443/svn/Test/"
username=""
passeord = ""

p = HTTPPasswordMgrWithDefaultRealm()#构建一个密码管理对象,用来保存需要处理的用户名和密码
p.add_password(None,url,user=username,passwd=passeord)#添加账户信息,第一个参数realm是与远程服务器相关的域信息,一般都是写None
auth_handler = HTTPBasicAuthHandler(p)#创建处理验证的Handler

opener = build_opener(auth_handler)#通过Handler构建opener
try:
    result = opener.open(url)
    html = result.read().decode("UTF-8",'ignore')#不是所有网页编码都是UTF-8的,我们可以使用开发者工具→控制台输入document.charset()查看编码
    print(html)
except URLError as e:
    print(e.reason)

获得验证后页面内容:
这里写图片描述

代理
      以后的爬虫项目避免不了使用代理,否则我们自己的IP很容易被封掉。有许多免费的高匿代理,譬如[西刺代理](http://www.xicidaili.com/nn)。虽然不稳定。还是要花钱呐! 示例:
from urllib.request import ProxyHandler,build_opener
from urllib.error import URLError

proxy_handler = ProxyHandler({'http':'219.141.153.41:80','https':'118.190.145.138:9001'})
opener = build_opener(proxy_handler)
try:
    resp = opener.open("https://www.baidu.com")
    print(resp.read().decode('utf-8'))
except URLError as e:
    print(e.reason)

ProxyHandler传递一个字典类型,里面是我们设置的代理IP,我们可以添加许多代理IP。后面我们可以自己实现一个代理IP池,玩转代理。

Cookie
首先我们用一段示例演示获取cookie:
import http.cookiejar,urllib.request

cookie = http.cookiejar.CookieJar()#声明CookieJar对象
handler = urllib.request.HTTPCookieProcessor(cookie)
opener = urllib.request.build_opener(handler)
resp = opener.open("https://www.baidu.com")
for item in cookie:
    print(item.name +'='+item.value)

这里写图片描述

获取到cookie信息后可以将它以文本信息保存:

import http.cookiejar,urllib.request

filename = 'cookies.txt'
#原来的CookieJar要缓存MozillaCookisJar,它是CookieJar的子类,在生成文件时会用它,
#将cookie信息保存为Mozilla型浏览器格式
cookie = http.cookiejar.MozillaCookieJar(filename)
handler = urllib.request.HTTPCookieProcessor(cookie)
opener = urllib.request.build_opener(handler)
resp = opener.open('https://www.baidu.com')
cookie.save(ignore_discard=True,ignore_expires=True)#保存cookie直至废弃;保存cookie直至过期

保存的cookie文件:
这里写图片描述

我们可以使用load()方法获取保存的cookie文件:

import http.cookiejar,urllib.request

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

urllib.error

URLError

      URLError类是OSError子类,是error异常模块的基类,有request模块产生的异常多可以通过捕获这个类来处理。它有一个reason属性,返回错误的原因。

from urllib import error,request

try:
    request.urlopen("http://catwithwing.s1.natapp.cc/index.html")#访问一个不存在的网站
except error.URLError as e:
    print(e.reason)
print("执行其他操作")

这里写图片描述
出现异常错误后程序没有崩掉,继续执行其他操作,这才是我们想要的结果。

HTTPError

      HTTPError类是URLError类的子类,它用来专门处理HTTP请求错误,比如认证失败等。它有三个属性:

  • **code:**返回HTTP状态吗。
  • **reason:**返回错误原因。
  • **headers:**返回请求头。
from urllib import error,request

try:
    request.urlopen("http://catwithwing.s1.natapp.cc/index.html")#访问一个不存在的网站
except error.HTTPError as e:
    print(e.code,e.reason,e.headers,sep='\n')
print("执行其他操作")

这里写图片描述

我们可以先捕获HTTPError,再捕获URLError。使程序更加健壮。

urllib.robotparser##

      首先我们要知道一个协议-----Robots协议。它也称作爬虫协议,机器人协议,全称为网络爬虫排除标准(Robots Exclusion Protocol),它告诉爬虫或者搜索引擎哪些页面可以爬取,哪些不可以。它通常是一个robots.txt的文件,放在网站的根目录下。比如豆瓣的robots.txt就是https://www.douban.com/robots.txt
内容如下:

User-agent: *
Disallow: /subject_search
Disallow: /amazon_search
Disallow: /search
Disallow: /group/search
Disallow: /event/search
Disallow: /celebrities/search
Disallow: /location/drama/search
Disallow: /forum/
Disallow: /new_subject
Disallow: /service/iframe
Disallow: /j/
Disallow: /link2/
Disallow: /recommend/
Disallow: /trailer/
Disallow: /doubanapp/card
Sitemap: https://www.douban.com/sitemap_index.xml
Sitemap: https://www.douban.com/sitemap_updated_index.xml
# Crawl-delay: 5

User-agent: Wandoujia Spider
Disallow: /

User-Agent设置此协议对哪些爬虫有效,*则表示所有的爬虫都要接受这个协议。
Disallow设置哪些目录是不允许爬取的,与之相对的是Allow,它设置哪些目录可以爬取。
Sitemap,主要是因为很多网站的内容都没有其他链接,为了把这些链接更好的连接起来,让爬虫能抓取更多的资源。

看看豆瓣的这个协议我们就会发现啥都不可以爬了。所以我们一般是不听的。
既然看了我们就来个示例,解析一下robots.txt文件吧:

from urllib import robotparser

rp = robotparser.RobotFileParser()
rp.set_url("https://www.douban.com/robots.txt")#设置robots..txt文件的url
rp.read()#读取robots文件并进行分析,这一步是必须的,如果不调用这个方法,后续的一切判断多少False,即使可以爬取的网页也是
#can_fetch()判断能否爬取指定URL,第一个参数是User-Agent,第二个参数是URL
print(rp.can_fetch('*',"https://www.douban.com/location/drama/search"))#判断https://www.douban.com/location/drama/search能不能爬取

其他要点

      有时候我们会在url上填写中文字符,如果我们不做任何处理是会导致乱码的,我们需要用quote()方法将中文字符转化为URL编码。

import urllib.parse

url = "https://www.baidu.com/s?wd="+urllib.parse.quote("美女")
print(url)

这里写图片描述
将URL编码后的字符转为中文字符我们可使用unquote()方法:

import urllib.parse

url = "https://www.baidu.com/s?wd=%E7%BE%8E%E5%A5%B3"
print(urllib.parse.unquote(url))

猜你喜欢

转载自blog.csdn.net/qq_25343557/article/details/81591368
今日推荐