【urllib】介绍python的urllib,实现网页爬取

本文主要参考北航硕士崔庆才的《Python3网络爬虫开发实战》。

urllib,利用它我们可以实现 HTTP 请求的发送,而不用去关心 HTTP 协议本身甚至更低层的实现。它是 Python 内置的 HTTP 请求库,也就是说不需要额外安装即可使用。它包含如下 4 个模块:

  • request:它是最基本的 HTTP 请求模块,可以用来模拟发送请求。
  • error:异常处理模块
  • parse:一个工具模块,提供了许多 URL 处理方法,比如拆分、解析和合并等
  • robotparser:主要用来识别网站的 robots.txt 文件,然后判断哪些网站可以爬,哪些网站不可以爬

1 发送请求

1.1 urlopen()

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

# 爬取python官网
import urllib.request

response = urllib.request.urlopen('https://www.python.org')
print(type(response))	# 打印出响应的类型
print(response.status)	# 打印状态码,200即为爬取成功
print(response.getheaders())
print(response.getheader('Server'))
# print(response.read().decode('utf-8'))  #打印html代码

运行结果如下所示:

【urllib】介绍python的urllib,实现网页爬取_第1张图片

可以看到,Response是一个HTTPResposne 类型的对象,主要包含 readreadintogetheadergetheadersfileno 等方法,以及 msgversionstatusreasondebuglevelclosed 等属性

urlopen方法的API:

urllib.request.urlopen(url, data=None, [timeout,]*, cafile=None, capath=None, cadefault=False, context=None)
  • data:该参数是可选的,如果要使用需要通过bytes方法将参数转化为字节流编码格式的内容,即bytes类型。如果传递了这个参数,则请求方式为POST,而不再是GET。
  • timeout:设置超时时间,单位为秒,如果请求超出了设置的这个时间,还没有得到响应,就会抛出异常。支持 HTTP、HTTPS、FTP 请求。
  • 除了 data 参数和 timeout 参数外,还有 context 参数,它必须是 ssl.SSLContext 类型,用来指定 SSL 设置。
  • 此外,cafilecapath 这两个参数分别指定 CA 证书和它的路径,这个在请求 HTTPS 链接时会有用。cadefault 参数现在已经弃用了,其默认值为 False

1.2 Request

利用 urlopen 方法可以实现最基本请求的发起,但这几个简单的参数并不足以构建一个完整的请求。如果请求中需要加入 Headers 等信息,就可以利用更强大的 Request 类来构建。

import urllib.request

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

依然用 urlopen 方法来发送这个请求,只不过这次该方法的参数不再是 URL,而是一个 Request 类型的对象。

Request的构造方法如下:

class urllib.request.Request(url, data=None, headers={}, origin_req_host=None, unverifiable=False, method=None)
  • 参数 headers 是一个字典,它就是请求头。我们在构造请求时,既可以通过 headers 参数直接构造,也可以通过调用请求实例的 add_header() 方法添加。添加请求头最常用的方法就是通过修改 User-Agent 来伪装浏览器。
  • 参数 origin_req_host 指的是请求方的 host 名称或者 IP 地址。
  • 参数 unverifiable 表示这个请求是否是无法验证的,默认是 False,意思就是说用户没有足够权限来选择接收这个请求的结果。
  • 参数 method 是一个字符串,用来指示请求使用的方法,比如 GET、POST 和 PUT 等
from urllib import request, parse

url = 'https://httpbin.org/post'
headers = {
    'User-Agent': 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)',
    'Host': 'httpbin.org'
}
dict = {'name': 'germey'}
data = bytes(parse.urlencode(dict), encoding='utf-8')

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

req = request.Request(url=url, data=data, headers=headers, method='POST')
response = request.urlopen(req)
print(response.read().decode('utf-8'))

运行结果如下所示:

【urllib】介绍python的urllib,实现网页爬取_第2张图片

1.3 高级用法

Handler,可以把它理解为各种处理器,有专门处理登录验证的,有处理 Cookie 的,有处理代理设置的。利用它们,我们几乎可以做到 HTTP 请求中所有的事情。

urllib.request 模块里的 BaseHandler 类,它是所有其他 Handler 的父类,它提供了最基本的方法,例如 default_openprotocol_request 等。

另一个比较重要的类就是 OpenerDirector,我们可以称为 Opener。我们之前用过 urlopen 这个方法,实际上它就是 urllib 为我们提供的一个 Opener。为什么要引入 Opener 呢?因为需要实现更高级的功能。之前使用的 Requesturlopen 相当于类库为你封装好了极其常用的请求方法。

(1)验证

有些网站在进入时需要登录认证,借助 HTTPBasicAuthHandler 就可以完成。

from urllib.request import HTTPPasswordMgrWithDefaultRealm, HTTPBasicAuthHandler, build_opener
from urllib.error import URLError

username = 'admin'
password = 'admin'
url = 'https://ssr3.scrape.center/'

p = HTTPPasswordMgrWithDefaultRealm()
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 在发送请求时就相当于已经验证成功了。

接下来,利用 Opener 的 open 方法打开链接,就可以完成验证了。这里获取到的结果就是验证后的页面源码内容。

(2)代理

from urllib.error import URLError
from urllib.request import ProxyHandler, build_opener

proxy_handler = ProxyHandler({
    'http': 'http://127.0.0.1:8080',
    'https': 'https://127.0.0.1:8080'
})
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)

在本地需要先事先搭建一个 HTTP 代理,运行在 8080 端口上。

这里使用了 ProxyHandler,其参数是一个字典,键名是协议类型(比如 HTTP 或者 HTTPS 等),键值是代理链接,可以添加多个代理。

然后,利用这个 Handler 及 build_opener 方法构造一个 Opener,之后发送请求即可。

(3)Cookie

import http.cookiejar, urllib.request

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

首先,声明一个 CookieJar 对象。接下来,就需要利用 HTTPCookieProcessor 来构建一个 Handler,最后利用 build_opener 方法构建出 Opener,执行 open 函数即可。运行结果如下图所示:

在这里插入图片描述

如何将Cookie输出成文件格式?

import urllib.request, http.cookiejar

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

这时 CookieJar 就需要换成 MozillaCookieJar,它在生成文件时会用到,是 CookieJar 的子类,可以用来处理 Cookie 和文件相关的事件,比如读取和保存 Cookie,可以将 Cookie 保存成 Mozilla 型浏览器的 Cookie 格式。运行之后,可以在当前工作目录下看到一个名为cookie.txt的文件。

如果要保存成LWP格式,则在初始化对象时候,应该改为:

cookie = http.cookiejar.LWPCookieJar(filename)

如果要加载已经保存的cookie,应使用如下的代码

import urllib.request, http.cookiejar
cookie = http.cookiejar.LWPCookieJar()
cookie.load('cookie.txt', ignore_discard=True, ignore_expires=True)

2 处理异常

2.1 URLError

URLError 类来自 urllib 库的 error 模块,它继承自 OSError 类,是 error 异常模块的基类,由 request 模块产生的异常都可以通过捕获这个类来处理。

它具有一个属性 reason,即返回错误的原因。

from urllib import request, error

try:
    response = request.urlopen('https://cuiqingcai.com/404')
except error.URLError as e:
    print(e.reason)

2.2 HTTPError

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

  • code:返回 HTTP 状态码,比如 404 表示网页不存在,500 表示服务器内部错误等。
  • reason:同父类一样,用于返回错误的原因。
  • headers:返回请求头。
from urllib import request, error

try:
    response = request.urlopen('https://cuiqingcai.com/404')
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')

因为 URLErrorHTTPError 的父类,所以可以先选择捕获子类的a错误,再去捕获父类的错误,这样就可以做到先捕获 HTTPError,获取它的错误原因、状态码、headers 等信息。如果不是 HTTPError 异常,就会捕获 URLError 异常,输出错误原因。最后,用 else 来处理正常的逻辑。

有时候,reason 属性返回的不一定是字符串,也可能是一个对象。

3 解析链接

urllib 库里还提供了 parse 模块,它定义了处理 URL 的标准接口,例如实现 URL 各部分的抽取、合并以及链接转换。

from urllib.parse import urlparse

result = urlparse('https://www.baidu.com/index.html;user?id=5#comment')
print(type(result))
print(result)

运行结果如下所示:
在这里插入图片描述

返回结果是一个 ParseResult 类型的对象,它包含 6 个部分,分别是 schemenetlocpathparamsqueryfragment

urlparse 方法将其拆分成了 6 个部分。大体观察可以发现,解析时有特定的分隔符。比如,:// 前面的就是 scheme,代表协议;第一个 / 符号前面便是 netloc,即域名,后面是 path,即访问路径;分号 ; 后面是 params,代表参数;问号 ? 后面是查询条件 query,一般用作 GET 类型的 URL;井号 # 后面是锚点,用于直接定位页面内部的下拉位置。

标准的链接(URL)格式:

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

4 Robots协议

4.1 协议

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

当搜索爬虫访问一个站点时,它首先会检查这个站点根目录下是否存在 robots.txt 文件,如果存在,搜索爬虫会根据其中定义的爬取范围来爬取。如果没有找到这个文件,搜索爬虫便会访问所有可直接访问的页面。

User-agent: *
Disallow: /
Allow: /public/

这实现了对所有搜索爬虫只允许爬取 public 目录的功能,将上述内容保存成 robots.txt 文件,放在网站的根目录下,和网站的入口文件(比如 index.php、index.html 和 index.jsp 等)放在一起。

上面的 User-agent 描述了搜索爬虫的名称,这里将其设置为 * 则代表该协议对任何爬取爬虫有效。我们可以设置:

User-agent: Baiduspider

这就代表我们设置的规则对百度爬虫是有效的。如果有多条 User-agent 记录,就有多个爬虫会受到爬取限制,但至少需要指定一条。

Disallow 指定了不允许抓取的目录,比如上例子中设置为 / 则代表不允许抓取所有页面。

Allow 一般和 Disallow 一起使用,一般不会单独使用,用来排除某些限制。上例中我们设置为 /public/,则表示所有页面不允许抓取,但可以抓取 public 目录。

4.2 爬虫名称

爬虫名是从哪儿来的?为什么就叫这个名?其实它是有固定名字的了,比如百度的就叫作 BaiduSpider。

爬虫名称 名称 网站
BaiduSpider 百度 www.baidu.com
Googlebot 谷歌 www.google.com
360Spider 360 搜索 www.so.com
YodaoBot 有道 www.youdao.com

4.3 robotparser

了解 Robots 协议之后,我们就可以使用 robotparser 模块来解析 robots.txt 了。该模块提供了一个类 RobotFileParser

  • set_url:用来设置 robots.txt 文件的链接。如果在创建 RobotFileParser 对象时传入了链接,那么就不需要再使用这个方法设置了。
  • read:读取 robots.txt 文件并进行分析。注意,这个方法执行一个读取和分析操作,如果不调用这个方法,接下来的判断都会为 False,所以一定记得调用这个方法。这个方法不会返回任何内容,但是执行了读取操作。
  • parse:用来解析 robots.txt 文件,传入的参数是 robots.txt 某些行的内容,它会按照 robots.txt 的语法规则来分析这些内容。
  • can_fetch:该方法用两个参数,第一个是 User-Agent,第二个是要抓取的 URL。返回的内容是该搜索引擎是否可以抓取这个 URL,返回结果是 TrueFalse
  • mtime:返回的是上次抓取和分析 robots.txt 的时间,这对于长时间分析和抓取的搜索爬虫是很有必要的,你可能需要定期检查来抓取最新的 robots.txt。
  • modified:它同样对长时间分析和抓取的搜索爬虫很有帮助,将当前时间设置为上次抓取和分析 robots.txt 的时间
from urllib.robotparser import RobotFileParser

rp = RobotFileParser()
rp.set_url('https://www.baidu.com/robots.txt')
rp.read()
print(rp.can_fetch('Baiduspider', 'https://www.baidu.com'))
print(rp.can_fetch('Baiduspider', 'https://www.baidu.com/homepage/'))
print(rp.can_fetch('Googlebot', 'https://www.baidu.com/homepage/'))

首先创建 RobotFileParser 对象,然后通过 set_url 方法设置了 robots.txt 的链接。也可以简单粗暴一点:

rp = RobotFileParser('https://www.baidu.com/robots.txt')

接着利用 can_fetch 方法判断网页是否可以被抓取。

你可能感兴趣的:(python基础,python,爬虫,网络爬虫,1024程序员节)