本节内容为基础库的使用,内容涵盖:Urllib库基本使用,Requests库基本使用以及正则表达式基础。

3.1 Urllib

内置http请求库
request请求模块,error异常处理模块,parse工具模块,robotparser 识别网站robots.txt,识别哪些可以爬

3.1.1 发送请求

1- urlopen
urllib.request 模块提供了最基本的构造 HTTP 请求的方法,利用它可以模拟浏览器的一个请求发起过程,同时它还带有处理authenticaton(授权验证),redirections(重定向),cookies(浏览器Cookies)以及其它内容。
通过输出结果可以发现它是一个 HTTPResposne 类型的对象,它主要包含的方法有 read()、readinto()、getheader(name)、getheaders()、fileno() 等方法和 msg、version、status、reason、debuglevel、closed 等属性。
 urlopen详解
urllib.request.urlopen(url, data=None, [timeout, ]*, cafile=None, capath=None, cadefault=False, context=None)
1)data(附加数据)
可添加form信息
2)timeout(超时时间)
可以通过设置这个超时时间来控制一个网页如果长时间未响应就跳过它的抓取,利用 try except 语句就可以实现这样的操作

还有 context 参数,它必须是 ssl.SSLContext 类型,用来指定 SSL 设置。
cafile 和 capath 两个参数是指定 CA 证书和它的路径,这个在请求 HTTPS 链接时会有用。
 
2- Request
urlopen() 方法可以实现最基本请求的发起,但这几个简单的参数并不足以构建一个完整的请求,如果请求中需要加入 Headers 等信息
 
详解Request
class urllib.request.Request(url, data=None, headers={}, origin_req_host=None, unverifiable=False, method=None)
· headers参数是一个字典,Request Headers,常用于修改User-Agent来伪装浏览器如:

Mozilla/5.0 (X11; U; Linux i686) Gecko/20071127 Firefox/2.0.0.11

可直接设置,也可以通过实例req add_header()来设置

· method为请求方法:get,post,put等

3- 高级用法
Handler 各种处理器,有专门处理登录验证的,有处理 Cookies 的,有处理代理设置的,利用它们我们几乎可以做到任何 HTTP 请求中所有的事情。
 
大体流程
先根据需求,用不同方法,构建(设置)出不同处理器(handler)
然后利用handler构造一个opener = build_opener(xx_handler),之后发送请求
 
BaseHandler类,所有Handler的父类。提供了最基本的 Handler 的方法,例如 default_open()、protocol_request() 方法等。
OpenerDirector,我们可以称之为 Opener,类似于urlopen()
 
认证、代理、Cookies

python | 爬虫笔记(三)- 基本库使用 

3.1.2 处理异常

Urllib 的 error 模块定义了由 request 模块产生的异常
 
1- URLError
URLError 类来自 Urllib 库的 error 模块,它继承自 OSError 类,是 error 异常模块的基类,由 request 模块生的异常都可以通过捕获这个类来处理。
具有一个属性reason
 
2- HttpError
它是 URLError 的子类,专门用来处理 HTTP 请求错误,比如认证请求失败等等。
它有三个属性。
· 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 来处理正常的逻辑,这是一个较好的异常处理写法。

3.1.3 解析链接

parse模块,定义处理URL的标准接口。例如实现 URL 各部分的抽取,合并以及链接转换。
1- urlparse() 解析网址
返回结果是一个 ParseResult 类型的对象,它包含了六个部分,分别是 scheme、netloc、path、params、query、fragment。

 API:

urllib.parse.urlparse(urlstring, scheme='', allow_fragments=True)
网址,协议,片段
2- urlunparse() 构造
3- urlsplit()
和urlparse类似,返回一个元组
4- urlunsplit() 拼接
5- urljoin()
前面提到的urlunparse和unsplit等可以链接合并,不过前提要有特定长度的对象
另外一个办法是利用 urljoin() 方法。我们可以提供一个 base_url(基础链接),新的链接作为第二个参数,方法会分析 base_url 的 scheme、netloc、path 这三个内容对新链接缺失的部分进行补充,作为结果返回。
#更方便,易操作
6- urlencode()
将构造好的参数字典,转化为URL的参数
7- parse_qs()
和6对应,实现序列参数转化成字典
8- parse_qsl()
参数转化为元组列表
9- quote()
将内容转化为 URL 编码的格式
10- unquote() 还原

3.1.4 分析robots协议

利用 Urllib 的 robotparser 模块我们可以实现网站 Robots 协议的分析
1- Robots协议
爬虫协议,网络爬虫排除标准
用来告诉爬虫和搜索引擎哪些页面可以抓取,哪些不可以抓取。它通常是一个叫做 robots.txt 的文本文件,放在网站的根目录下。
User-agent: *
Disallow: /
Allow: /public/
2- robotparser
使用RobotFileParser解析 robots.txt
urllib.robotparser.RobotFileParser(url='')
先创建一个robofileparser对象,然后通过set_url()方法设置 robots.txt链接。
下一步利用can_fetch()方法判断王爷是否可以被抓取
也可以利用parser() 方法读取和分析

3.2 Request

在前面一节我们了解了 Urllib 的基本用法,但是其中确实有不方便的地方。比如处理网页验证、处理 Cookies 等等,需要写 Opener、Handler 来进行处理。为了更加方便地实现这些操作,在这里就有了更为强大的库 Requests,有了它,Cookies、登录验证、代理设置等等操作简单

3.2.1 基本使用

import requests

r = requests.get('https://www.baidu.com/')
print(type(r))
print(r.status_code)
print(type(r.text))
print(r.text)
print(r.cookies)
get() 方法即可实现和 urlopen() 相同的操作,得到一个 Response 对象,然后分别输出了 Response 的类型,Status Code,Response Body 的类型、内容还有 Cookies。
 
· GET请求
params 添加参数
调用r.json() 可以将返回结果转换为字典格式
· POST请求
import requests
data = {'name': 'germey', 'age': '22'}
r = requests.post("http://httpbin.org/post", data=data)
print(r.text)
返回中,form部分就是提交的数据
· Response
发送 Request 之后,得到的自然就是 Response,在上面的实例中我们使用了 text 和 content 获取了 Response 内容,不过还有很多属性和方法可以获取其他的信息,比如状态码 Status Code、Headers、Cookies 等信息。
statuscode 有好多,如200成功,404notfound

3.2.2 高级用法 (需重点掌握)

文件上传,代理设置,Cookies 设置
1- 文件上传
requests 设置files
2- Cookies设置
3- 会话维持
在 Requests 中,我们如果直接利用 get() 或 post() 等方法的确可以做到模拟网页的请求。但是这实际上是相当于不同的会话,即不同的 Session,也就是说相当于你用了两个浏览器打开了不同的页面。
requests.get('http://httpbin.org/cookies/set/number/123456789')
r = requests.get('http://httpbin.org/cookies')
->
s = requests.Session()
s.get('http://httpbin.org/cookies/set/number/123456789')
r = s.get('http://httpbin.org/cookies')
利用 Session 我们可以做到模拟同一个会话,而且不用担心 Cookies 的问题,通常用于模拟登录成功之后再进行下一步的操作。
可以用于模拟在一个浏览器中打开同一站点的不同页面,
4- SSL证书验证
Requests 提供了证书验证的功能,当发送 HTTP 请求的时候,它会检查 SSL 证书,我们可以使用 verify 这个参数来控制是否检查此证书,其实如果不加的话默认是 True,会自动验证。
5- 代理设置
某些网站大规模爬取是会封IP
request里的proxies参数
6- 超时设置 timeout() 参数
7- 身份认证
8- Prepared Request**
在前面介绍 Urllib 时我们可以将 Request 表示为一个数据结构,Request 的各个参数都可以通过一个 Request 对象来表示,在 Requests 里面同样可以做到,这个数据结构就叫 Prepared Request。

from requests import Request, Session

url = 'http://httpbin.org/post'
data = {
    'name': 'germey'
}
headers = {
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.116 Safari/537.36'
}
s = Session()
req = Request('POST', url, data=data, headers=headers)
prepped = s.prepare_request(req)
r = s.send(prepped)
print(r.text)
有了 Request 这个对象,我们就可以将一个个请求当做一个独立的对象来看待,这样在进行队列调度的时候会非常方便。

3.3 正则表达式

3.3.1 基本使用

实现字符串的检索、替换、匹配验证
URL: [a-zA-z]+://[^\s]*

 python | 爬虫笔记(三)- 基本库使用

3.3.2- re库

3.3.3- match() 开头匹配

非贪婪策略不适用于字符串结尾内容的匹配
 
修饰符
遇到换行符,需要加在match()中,第三个参数re.S

 python | 爬虫笔记(三)- 基本库使用

总结:尽量使用泛匹配;使用括号得到匹配目标;尽量使用非贪婪模式;有换行符就用re.S

3.3.4- search() 查询匹配

match从头开始查,不匹配返回none

result = re.search('<li.*?active.*?singer="(.*?)">(.*?)</a>', html, re.S)
if result:
    print(result.group(1), result.group(2))
由于绝大部分的 HTML 文本都包含了换行符,所以通过上面的例子,我们尽量都需要加上 re.S 修饰符,以免出现匹配不到的问题。
为了匹配方便,能用search就不用match

3.3.5- findall()

search() 可以返回匹配正则表达式的第一个内容,但如果想获得匹配的所有内容,需要findall()
还是上面的 HTML 文本,如果我们想获取所有 a 节点的超链接、歌手和歌名,就可以将 search() 方法换成 findall() 方法。如果有返回结果的话就是列表类型,所以我们需要遍历一下来获依次获取每组内容。

3.3.6- sub() 修改文本

想获取所有li节点所有歌名,直接正则比较麻烦,可以先用sub把a节点去掉,只留下文本,再利用findall提取
html = re.sub('<a.*?>|</a>', '', html)
print(html)
results = re.findall('<li.*?>(.*?)</li>', html, re.S)
for result in results:
    print(result.strip())

3.3.7-compile()

将字符串编译成正则表达式对象
给正则表达式做了一层封装,以便于我们更好地复用。
 

##本系列内容为《python3爬虫开发实战》学习笔记。本系列博客列表如下:

(零)学习路线

(一)开发环境配置

(二)爬虫基础

(三)基本库使用

(四)解析库使用

(五)数据存储

(六)Ajax数据爬取

(七)动态渲染页面爬取Selenium

持续更新...

对应代码请见:..