终于要开始写爬虫代码了
咱们首先理解一下 Urllib 库,它是 Python 内置的 HTTP 申请库,也就是说咱们不须要额定装置即可应用,它蕴含四个模块:
第一个模块 request,它是最根本的 HTTP 申请模块,咱们能够用它来模仿发送一申请,就像在浏览器里输出网址而后敲击回车一样,只须要给库办法传入 URL 还有额定的参数,就能够模仿实现这个过程了。
第二个 error 模块 即异样解决模块,如果呈现申请谬误,咱们能够捕捉这些异样,而后进行重试或其余操作保障程序不会意外终止。
第三个 parse 模块 是一个工具模块,提供了许多 URL 解决办法,比方拆分、解析、合并等等的办法。
第四个模块是 robotparser,次要是用来辨认网站的 robots.txt 文件,而后判断哪些网站能够爬,哪些网站不能够爬的,其实用的比拟少。
在这里重点对前三个模块进行下解说。
一、发送申请
应用 Urllib 的 request 模块咱们能够不便地实现 Request 的发送并失去 Response
1、urlopen()
urllib.request 模块提供了最根本的结构 HTTP 申请的办法,利用它能够模仿浏览器的一个申请发动过程,同时它还带有解决 authenticaton(受权验证),redirections(重定向),cookies(浏览器 Cookies)以及其它内容。
咱们来感受一下它的弱小之处,以 Python 官网为例,咱们来把这个网页抓下来:
import urllib.request
response = urllib.request.urlopen('https://www.python.org')
print(response.read().decode('utf-8'))
运行后果如下:
接下来咱们看下它返回的到底是什么,利用 type() 办法输入 Response 的类型。
import urllib.request
response = urllib.request.urlopen('https://www.python.org')
print(type(response))
输入后果如下:
<class 'http.client.HTTPResponse'>
通过输入后果能够发现它是一个 HTTPResposne 类型的对象,它次要蕴含的办法有 read()、readinto()、getheader(name)、getheaders()、fileno() 等办法和 msg、version、status、reason、debuglevel、closed 等属性。
失去这个对象之后,咱们把它赋值为 response 变量,而后就能够调用这些办法和属性,失去返回后果的一系列信息了。
例如调用 read() 办法能够失去返回的网页内容,调用 status 属性就能够失去返回后果的状态码,如 200 代表申请胜利,404 代表网页未找到等。
上面再来一个实例感受一下:
import urllib.request
response = urllib.request.urlopen('https://www.python.org')
print(response.status)
print(response.getheaders())
print(response.getheader('Server'))
运行后果如下:
200
[('Server', 'nginx'), ('Content-Type', 'text/html; charset=utf-8'), ('X-Frame-Options', 'SAMEORIGIN'), ('X-Clacks-Overhead', 'GNU Terry Pratchett'), ('Content-Length', '47397'), ('Accept-Ranges', 'bytes'), ('Date', 'Mon, 01 Aug 2016 09:57:31 GMT'), ('Via', '1.1 varnish'), ('Age', '2473'), ('Connection', 'close'), ('X-Served-By', 'cache-lcy1125-LCY'), ('X-Cache', 'HIT'), ('X-Cache-Hits', '23'), ('Vary', 'Cookie'), ('Strict-Transport-Security', 'max-age=63072000; includeSubDomains')]
nginx
可见,三个输入别离输入了响应的状态码,响应的头信息,以及通过调用 getheader() 办法并传递一个参数 Server 获取了 headers 中的 Server 值,后果是 nginx,意思就是服务器是 nginx 搭建的。
利用以上最根本的 urlopen() 办法,咱们能够实现最根本的简略网页的 GET 申请抓取。
如果咱们想给链接传递一些参数该怎么实现呢?咱们首先看一下 urlopen() 函数的 API:
urllib.request.urlopen(url, data=None, [timeout,]*, cafile=None, capath=None, cadefault=False, context=None)
能够发现除了第一个参数能够传递 URL 之外,咱们还能够传递其它的内容,比方 data(附加数据)、timeout(超时工夫)等等。
上面咱们具体阐明下这几个参数的用法:
data 参数
data 参数是可选的,如果要增加 data,它要是字节流编码格局的内容,即 bytes 类型,通过 bytes() 办法能够进行转化,另外如果传递了这个 data 参数,它的申请形式就不再是 GET 形式申请,而是 POST。
上面用一个实例来感受一下:
import urllib.parse
import urllib.request
data = bytes(urllib.parse.urlencode({'word': 'hello'}), encoding='utf8')
response = urllib.request.urlopen('http://httpbin.org/post', data=data)
print(response.read())
在这里咱们传递了一个参数 word,值是 hello。它须要被转码成 bytes(字节流)类型。其中转字节流采纳了 bytes() 办法
第一个参数须要是 str(字符串)类型,须要用 urllib.parse 模块里的 urlencode() 办法来将参数字典转化为字符串。
第二个参数指定编码格局,在这里指定为 utf8。
timeout 参数
timeout 参数能够设置超时工夫,单位为秒,意思就是如果申请超出了设置的这个工夫还没有失去响应,就会抛出异样,如果不指定,就会应用全局默认工夫。它反对 HTTP、HTTPS、FTP 申请。
因而咱们能够通过设置这个超时工夫来管制一个网页如果长时间未响应就跳过它的抓取,利用 try except 语句就能够实现这样的操作,代码如下:
import socket
import urllib.request
import urllib.error
try:
response = urllib.request.urlopen('http://httpbin.org/get', timeout=0.1)
except urllib.error.URLError as e:
if isinstance(e.reason, socket.timeout):
print('TIME OUT')
其余参数
还有 context 参数,它必须是 ssl.SSLContext 类型,用来指定 SSL 设置。
cafile 和 capath 两个参数是指定 CA 证书和它的门路,这个在申请 HTTPS 链接时会有用。
cadefault 参数当初曾经弃用了,默认为 False。
以上解说了 urlopen() 办法的用法,通过这个最根本的函数能够实现简略的申请和网页抓取,如需更加具体理解,能够参见官网文档:
https://docs.python.org/3/lib…。
2. Request
由上咱们晓得利用 urlopen() 办法能够实现最根本申请的发动,但这几个简略的参数并不足以构建一个残缺的申请,如果申请中须要退出 Headers 等信息,咱们就能够利用更弱小的 Request 类来构建一个申请。
首先咱们用一个实例来感受一下 Request 的用法:
import urllib.request
request = urllib.request.Request('https://python.org')
response = urllib.request.urlopen(request)
print(response.read().decode('utf-8'))
能够发现,咱们仍然是用 urlopen() 办法来发送这个申请,只不过这次 urlopen() 办法的参数不再是一个 URL,而是一个 Request 类型的对象,通过结构这个这个数据结构,一方面咱们能够将申请独立成一个对象,另一方面可配置参数更加丰盛和灵便。
上面咱们看一下 Request 都能够通过怎么的参数来结构,它的构造方法如下:
class urllib.request.Request(url, data=None, headers={}, origin_req_host=None, unverifiable=False, method=None)
第一个 url 参数是申请 URL,这个是必传参数,其余的都是可选参数。
第二个 data 参数如果要传必须传 bytes(字节流)类型的,如果是一个字典,能够先用 urllib.parse 模块里的 urlencode() 编码。
第三个 headers 参数是一个字典,这个就是 Request Headers 了,你能够在结构 Request 时通过 headers 参数间接结构,
也能够通过调用 Request 实例的 add_header() 办法来增加, Request Headers 最罕用的用法就是通过批改 User-Agent 来假装浏览器,默认的 User-Agent 是 Python-urllib,咱们能够通过批改它来假装浏览器。
第四个 origin_req_host 参数指的是申请方的 host 名称或者 IP 地址。
第五个 unverifiable 参数指的是这个申请是否是无奈验证的,默认是 False。意思就是说用户没有足够权限来抉择接管这个申请的后果。例如咱们申请一个 HTML 文档中的图片,然而咱们没有主动抓取图像的权限,这时 unverifiable 的值就是 True。
第六个 method 参数是一个字符串,它用来批示申请应用的办法,比方 GET,POST,PUT 等等。
写个例子:
from urllib import request, parse
url = 'http://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='utf8')
req = request.Request(url=url, data=data, headers=headers, method='POST')
response = request.urlopen(req)
print(response.read().decode('utf-8'))
在这里咱们通过四个参数结构了一个 Request,url 即申请 URL,在 headers 中指定了 User-Agent 和 Host,传递的参数 data 用了 urlencode() 和 bytes() 办法来转成字节流,另外指定了申请形式为 POST。
通过观察后果能够发现,咱们胜利设置了 data,headers 以及 method。
另外 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)')
如此一来,咱们就能够更加不便地结构一个 Request,实现申请的发送啦。
二、解决异样
咱们理解了 Request 的发送过程,然而在网络状况不好的状况下,呈现了异样怎么办呢?这时如果咱们不解决这些异样,程序很可能报错而终止运行,所以异样解决还是非常有必要的。
Urllib 的 error 模块定义了由 request 模块产生的异样。如果呈现了问题,request 模块便会抛出 error 模块中定义的异样。
次要有这两个解决异样类,URLError
,HTTPError
上面写个例子:
from urllib import request, error
try:
response = request.urlopen('http://cuiqingcai.com/index.htm')
except error.URLError as e:
print(e.reason)
咱们关上一个不存在的页面,照理来说应该会报错,然而这时咱们捕捉了 URLError 这个异样,运行后果如下:
Not Found
from urllib import request,error
try:
response = request.urlopen('http://cuiqingcai.com/index.htm')
except error.HTTPError as e:
print(e.reason, e.code, e.headers, seq='\n')
运行后果:
Not Found
404
Server: nginx/1.4.6 (Ubuntu)
Date: Wed, 03 Aug 2016 08:54:22 GMT
Content-Type: text/html; charset=UTF-8
Transfer-Encoding: chunked
Connection: close
X-Powered-By: PHP/5.5.9-1ubuntu4.14
Vary: Cookie
Expires: Wed, 11 Jan 1984 05:00:00 GMT
Cache-Control: no-cache, must-revalidate, max-age=0
Pragma: no-cache
Link: <http://cuiqingcai.com/wp-json/>; rel="https://api.w.org/"
HTTPError, 它有三个属性。
- code,返回 HTTP Status Code,即状态码,比方 404 网页不存在,500 服务器外部谬误等等。
- reason,同父类一样,返回谬误的起因。
- headers,返回 Request Headers。
因为 URLError 是 HTTPError 的父类,所以咱们能够先抉择捕捉子类的谬误,再去捕捉父类的谬误,所以上述代码更好的写法如下:
from urllib import request, error
try:
response = request.urlopen('http://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')
这样咱们就能够做到先捕捉 HTTPError,获取它的谬误状态码、起因、Headers 等详细信息。如果非 HTTPError,再捕捉 URLError 异样,输入谬误起因。最初用 else 来解决失常的逻辑,这是一个较好的异样解决写法。
三、实例抓取豆瓣电影排行榜
import urllib.parse
import urllib.request
url='https://movie.douban.com/'
herders={'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36',
'Referer':'https://movie.douban.com/',
'Connection':'keep-alive'}
req=urllib.request.Request(url,headers=herders)
response=urllib.request.urlopen(req)
html=response.read().decode('utf8')
print(html)
后果
<!DOCTYPE html>
<html lang="zh-CN" class="ua-windows ua-webkit">
<head>
...
<title> 豆瓣电影 </title>
<meta name="keywords" content="电影、经典电影、热映、电视剧、美剧、影评、电影院、电影票、排行、举荐"/>
<meta name="description" content="豆瓣电影提供最新的电影介绍及评论包含上映影片的影讯查问及购票服务。你能够记录想看、在看和看过的电影电视剧,顺便打分、写影评。依据你的口味,豆瓣电影会举荐好电影给你。" />
....
以下省略
四、剖析 Robots 协定
利用 urllib 的 robotparser 模块,咱们能够实现网站 Robots 协定的剖析
1Robots 协定
Robots 协定也称为爬虫协定、机器人协定,它的全名叫做网络爬虫排除规范(Robots Exclusion Protocol),用来通知爬虫和搜索引擎哪些网页能够抓取,哪些不能够抓取,它通常是一个 robots.txt 的文本文件,个别放在网站的根目录下。
当搜寻爬虫拜访一个站点时,它首先会查看这个站点根目录下是否存在 robots.txt 文件,如果存在,搜寻爬虫会依据其中定义的爬去范畴来爬取,如果没有找到,搜寻爬虫会拜访所有可间接拜访的页面
咱们来看下 robots.txt 的样例:
User-agent: *
Disallow: /
Allow: /public/
它实现了对所有搜寻爬虫只容许爬取 public 目录的性能,将上述内容保留为 robots.txt 文件放在网站根目录下,和网站的入口文件(index.html)放在一起
User-agent 形容了搜寻爬虫的名称,将其设置为 * 则代表协定对任何爬虫无效,如设置为 Baiduspider 则代表规定对百度爬虫无效,如果有多条则对多个爬虫受到限制,但至多须要指定一条
一些常见的搜寻爬虫名称:
BaiduSpider 百度爬虫 www.baidu.com
Googlebot Google 爬虫 www.google.com
360Spider 360 爬虫 www.so.com
YodaoBot 有道爬虫 www.youdao.com
ia_archiver Alexa 爬虫 www.alexa.cn
Scooter altavista 爬虫 www.altavista.com
Disallow 指定了不容许抓取的目录,如上例中设置的 / 则代表不容许抓取所有的页面
Allow 个别和 Disallow 一起应用,用来排除独自的某些限度,如上例中设置为 /public/ 则示意所有页面不容许抓取,但能够抓取 public 目录
设置示例:
# 禁止所有爬虫
User-agent: *
Disallow: /
#容许所有爬虫拜访任何目录, 另外把文件留空也能够
User-agent: *
Disallow:
#禁止所有爬虫拜访某那些目录
User-agent: *
Disallow: /home/
Disallow: /tmp/
#只容许某一个爬虫拜访
User-agent: BaiduSpider
Disallow:
User-agent: *
Disallow: /
2 robotparser
rebotparser 模块用来解析 robots.txt,该模块提供了一个类 RobotFileParser,它能够依据某网站的 robots.txt 文件来判断一个抓取爬虫时都有权限来抓取这个网页
urllib.robotparser.RobotFileParser(url='')
robotparser 类罕用的办法:
set_url():用来设置 robots.txt 文件的连贯,如果在创立 RobotFileParser 对象是传入了连贯,就不须要在应用这个办法设置了
read():读取 reobts.txt 文件并进行剖析,它不会返回任何内容,但执行那个了读取和剖析操作
parse():用来解析 robots.txt 文件,传入的参数是 robots.txt 某些行的内容,并装置语法规定来剖析内容
can_fetch():该办法传入两个参数,第一个是 User-agent,第二个是要抓取的 URL,返回的内容是该搜索引擎是否能够抓取这个 url, 后果为 True 或 False
mtime():返回上次抓取和剖析 robots.txt 的工夫
modified():将以后工夫设置为上次抓取和剖析 robots.txt 的工夫
#!/usr/bin/env python
#coding:utf8
from urllib.robotparser import RobotFileParser
rp = RobotFileParser() #创建对象
rp.set_url('https://www.cnblogs.com/robots.txt') #设置 robots.txt 连贯,也能够在创建对象时指定
rp.read() #读取和解析文件
print(rp.can_fetch('*','https://i.cnblogs.com/EditPosts.aspx?postid=9170312&update=1'))
心愿各位同学当前在写爬虫时候能够遵循 Robot 协定,为了本人的平安着想。
IT 入门 感激关注 | 练习地址:www.520mg.com/it