前边我们讲了使用urllib
的request
模块和parse
模块来分别完成网络请求的发起和URL
的正确处理。在这些过程中,我们不免会遇到一些错误,那么当错误发生时,我们应该怎么样避免程序出现我们意料之外的结果呢?其实答案与其他的场景没什么区别,那就是利用好异常捕获工具。
一、urllib
中的异常模块:error
捕获异常的主要目的是在异常发生时,让程序继续执行我们设定好的动作(比如打印详细日志、将内存中的数据写入数据库、重新抓取当前网页等),而非直接终止,从而让我们的程序更加稳健。
urllib.error
模块定义了在request
模块调用过程中会产生的异常。
URLError
是该模块的基类,由request
产生的所有异常都可以通过它来捕获。HTTPError
是URLError
的子类,专门用来处理HTTP
请求相关的错误,如认证失败、网页不存在等。ContentTooShortError
是指下载的数据量比预期小,它在我们使用urllib.request.urlretrieve()
方法下载网页到本地时可能会触发,触发条件就是下载的内容长度小于header
中Content-Length
的取值。
我们用一个简单的例子来演示下error
模块的使用。
from urllib.request import urlopen
from urllib.error import URLError, HTTPError
try:
response = request.urlopen('https://www.baidu.com/nothing.html')
except HTTPError as e:
print('******code*****')
print(e.code)
print('\n')
print('******reason*****')
print(e.reason)
print('\n')
print('******headers*****')
print(e.headers)
print('\n')
print('******error*****')
print(e)
print('\n')
except error.URLError as e:
print(e.reason)
print(e)
else:
print('看到我说明你请求成功了!')
finally:
print('无论如何你都会看到我!')
输出如下:
******code***** 404 ******reason***** Not Found ******headers***** Content-Length: 210 Content-Type: text/html; charset=iso-8859-1 Date: Wed, 01 Feb 2023 12:57:32 GMT Server: Apache Connection: close ******error***** HTTP Error 404: Not Found 无论如何你都会看到我!
可以看到,我们先检查是否出现了HTTPError
,然后再检查是否出现了URLError
,这是因为HTTPError
是URLError
的子类,包含更详细的信息,如HTTP
状态码、请求头等。如果不是HTTPError
那么我们再用URLError
来捕获,看一下错误的原因是什么。
如果没有出现URLError
,那理论上我们这次请求是成功的。
二、robotparser
模块:网站可以爬取吗?
事实上,我们在抓取网页内容时,经常会忽略一些网站的提示。但是这些提示很可能是重要的。
比如一个网站提示了某个网页禁止抓取,那么这个时候我们强行抓取可能是有侵权甚至违法的风险。
又比如一个网站告知了你建议的抓取频率,但你并不遵循,那很有可能你的IP地址会被永久封禁,以后就再也不能访问该网站了。
那么我们应该如何获取并解析网站的提示呢?这就要用到robotparser
模块了。
一、爬虫协议
robotparser
模块用于分析网站的Robots
协议,一般来说,该协议存放于站点根目录下,命名为robots.txt
,比如https://www.douban.com/robots.txt
就存储着豆瓣网站的Robots
协议。
Robots
协议又称爬虫协议、机器人协议,用于网站告诉搜索引擎和爬虫一些信息:
- 允许抓取路径和禁止抓取路径
- 允许哪些爬虫抓取网站内容
- 建议抓取频率
- 站点地图
- ……
比如豆瓣的爬虫协议为:
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: /doubanapp/card
Disallow: /update/topic/
Disallow: /share/
Disallow: /people//collect
Disallow: /people//wish
Disallow: /people//all
Disallow: /people//do
Allow: /ads.txt
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: Mediapartners-Google
Disallow: /subject_search
Disallow: /amazon_search
Disallow: /search
Disallow: /group/search
Disallow: /event/search
Disallow: /celebrities/search
Disallow: /location/drama/search
Disallow: /j/
二、robotparser
常用方法
使用robotparser
非常简单,我们用RobotFileParser
类接受一个robots.txt
文件链接参数即可。
urllib.robotparser.RobotFileParser(url='')
该类支持如下方法。
1. set_url()
方法
该方法用于设置robots.txt
文件的链接,实际上我们往往在创建RobotFileParser
对象时就已经传入了链接,所以该方法经常用不到。
2. read()
方法
该方法用于读取robots.txt
文件并进行分析,在创建RobotFileParser
对象后,我们必须要执行一次read()
方法,不然其他所有的调用结果都没有意义。
3. parse()
方法
该方法用来解析robots.txt
文件,传入的参数是robots.txt
某些行的内容,它会对其进行解析。
4. can_fetch()
方法
该方法接受useragent
和url
两个参数,返回该UA(User-Agent,如某爬虫引擎、某搜索引擎)是否可以抓取这个url
。
5. mtime()
方法
该方法返回上次抓取和分析robots.txt
文件的时间,这可以帮助我们判断是否应该更新robots.txt
的信息。
6. modified()
方法
该方法将当前时间设置为上次抓取和分析robots.txt
的时间,也就是说下次我们再调用mtime()
时,返回的就是此时的时间。
7. crawl_delay()
方法
该方法返回网站针对某个UA(User-Agent)建议的两次抓取的时间间隔。
8. request_rate()
方法
该方法返回网站指定的Request-rate: x/n
,比如Request-rate: 12/1m
代表每1分钟可以抓取12个页面。
9. site_maps()
方法
该方法以列表格式返回站点的站点地图。
怎么说呢,这个方法还是有用的,尤其是对于大型爬虫或者搜索引擎。它能帮助我们自动解析不同站点对爬虫的要求。
不过对我们个人来说,我们一般会直接打开站点的robots.txt
看一眼,大概了解以后就关掉。如果它的要求能满足我们的要求,那我们就满足它的要求。如果它的要求不能满足我们的要求,那我们自然就不会满足它的要求。
如果大家真的那么乖,也就没有所谓的反爬与反反爬了。