目录清单
- 入门程序了解爬虫采集数据步骤
- 底层操作Request对象
- 请求头设置之UserAgent用户代理
- 请求头设置
- 用户代理——UserAgent
- 自定义请求头消息
- 请求方式之GET/POST请求
- GET请求处理
- POST请求处理
- Handler处理器自定义开锁人opener
- 自定义HTTP OPENER
- 自定义PROXY OPENER
- 会话跟踪之cookie操作
- cookie基本操作
- cookie写入数据操作
- cookie读取数据操作
- 异常和错误处理
- 异常处理
- 错误处理
- HTTPS数字签名问题
1. 入门
首先,这里通过一个简单的程序认识什么是爬虫!
程序清单:demo01.py
# -*- coding:utf-8 -*-
# 添加指定编码的注释,表示当前源代码支持中文操作
import urllib2
# 引入需要的模块
response = urllib2.urlopen("http://www.taobao.com")
# 通过urlopen()方法请求淘宝网数据,获取到的响应数据保存在response中
print(response.read())
# 打印展示获取到的数据
运行程序
python2 demo01.py
在控制台中,就可以看到获取到了淘宝网网页的源代码数据
<!doctype html>
<html>
<head>
<title>淘宝网 - 淘!我喜欢</title>
...
...
</html>
至此,我们可以描述爬虫程序,就是用来根据一定的规则采集获取网络中的数据的!
整个采集过程主要步骤如下:
- 分析确定目标网址
- 连接并向目标网址发送请求
- 获取目标服务器返回的数据
2. Request对象
上述程序我们了解了爬虫程序的操作步骤,底层操作过程中其实是将请求和响应两部分都是分步骤进行的:
# -*- coding:utf-8 -*-
# 添加 注释,指定当前源代码支持中文操作
from urllib2 import Request, urlopen
# 引入需要的模块
request = Request("http://www.taobao.com")
# 访问目标网址,封装请求对象
response = urlopen(request)
# 发送请求,得到服务器响应数据,存储在变量response中
print(response.read())
# 打印展示响应的数据内容
3. 请求头设置之UserAgent操作
书接上回,爬虫程序已经开发并成功采集数据,但是我们通过抓包工具进行发送的请求查看时,发现一个非常严重的问题:请求的UserAgent(用户代理),也就是指带客户端身份的描述字段,赤果果显示的是python urllib2的字样,这不是迅雷不及掩耳盗铃吗,用自己爬虫的身份直接访问服务器~很容被服务器进行分析过滤并屏蔽访问的!
所以,要对我们发起请求的爬虫程序进行伪造,将爬虫程序发送的请求伪造程浏览器发送的请求,通过设置请求中的UserAgent就可以实现
# 添加注释,源代码支持中文
# -*- coding:utf-8 -*-
# 引入需要的模块
from urllib2 import Request, urlopen
# 定义访问的目标url地址
url = "http://www.taobao.com"
# 定义请求头信息
headers = {
"User-agent": "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0;",
"info": "自定义消息",
}
# 构建请求对象
request = Request(url, headers=headers)
# 发送请求获得响应对象
response = urlopen(response)
# 打印展示信息
print(response.read())
4. 请求处理方式get/post
请求常规处理方式GET/POST操作,通过两个案例进行操作,并通过抓包工具进行数据分析:
# 添加注释,python2支持中文编码
# -*- coding:utf-8 -*-
# 引入需要的模块
from urllib2 import Request, urlopen
from urllib import urlencode
# 定义访问url地址和传递的数据
url = "http://www.baidu.com/s?"
data = {
"wd": "火车票",
}
# 处理完整的请求
fullurl = url + urlencode(data)
# 构建请求对象
request = Request(fullurl)
# 发送请求获取响应数据
response = urlopen(request)
# 打印 展示 响应数据
print response.read()
POST请求的处理方式,我们通过有道在线翻译词典的操作来完成,首先打开浏览器访问有道在线翻译http://fanyi.youdao.com
,在翻译页面输入词语之后会自动翻译,抓包分析访问路径可以得到真实路径;有道在线翻译由于近年来被爬虫频繁操作,所以做了简单的反爬虫措施,通过加密混淆参数和cookie标记完成反爬虫,我们简单破解一下就可以了,干货如下:
# -*- coding:utf-8 -*-
from urllib2 import Request, urlopen
from urllib import urlencode
import time
import random
import hashlib
# 引入需要的模块
url = "http://fanyi.youdao.com/translate_o?smartresult=dict&smartresult=rule"
# 真实翻译地址
n = raw_input("请输入要翻译的词汇:")
c = "fanyideskweb"
x = "aNPG!!u6sesA>hBAW1@(-"
r = str(int(time.time() * 1000) + random.randint(1, 10))
# 处理加密字段
sign = hashlib.md5(c + n + r + x).hexdigest()
# 加密字段
headers = {
# "Host": "fanyi.youdao.com",
# "Content-Length": "201",
"Accept": "application/json, text/javascript, */*; q=0.01",
"Origin": "http://fanyi.youdao.com",
# "X-Requested-With": "XMLHttpRequest",
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36",
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
"Referer": "http://fanyi.youdao.com/",
# "Accept-Encoding": "gzip, deflate",
# "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
"Cookie": "OUTFOX_SEARCH_USER_ID_NCOO=1092450716.3850443; OUTFOX_SEARCH_USER_ID=-1512266810@10.168.1.241; JSESSIONID=aaal2mcR1N1zx5rcovkdw; fanyi-ad-id=39535; fanyi-ad-closed=1; ___rl__test__cookies=1515335942852"
}
# 请求头设置
form_data = {
"i": n,
"from": "AUTO",
"to": "AUTO",
"smartresult": "dict",
"client": c,
"salt": r,
"sign": sign,
"doctype": "json",
"version": "2.1",
"keyfrom": "fanyi.web",
"action": "FY_BY_REALTIME",
"typoResult": "false",
}
data = urlencode(form_data)
# 请求的表单数据
request = Request(url, data=data, headers=headers)
# 构建请求对象
response = urlopen(request)
# 访问指定的url,得到响应对象数据
print(response.read())
# 打印展示获取的数据
5. 自定义Opener开锁人
在前面的所有操作案例中,都是直接使用urllib2模块的操作函数进行的处理,处理的方式一般都集中在HTTP或者HTTPS请求,那么urllib2.urlopen()底层具体做了什么样的操作呢?
def urlopen(url, data=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
cafile=None, capath=None, cadefault=False, context=None):
global _opener
if cafile or capath or cadefault:
if context is not None:
raise ValueError(
"You can't pass both context and any of cafile, capath, and "
"cadefault"
)
if not _have_ssl:
raise ValueError('SSL support not available')
context = ssl.create_default_context(purpose=ssl.Purpose.SERVER_AUTH,
cafile=cafile,
capath=capath)
https_handler = HTTPSHandler(context=context)
opener = build_opener(https_handler)
elif context:
https_handler = HTTPSHandler(context=context)
opener = build_opener(https_handler)
elif _opener is None:
_opener = opener = build_opener()
else:
opener = _opener
return opener.open(url, data, timeout)
观察底层代码操作,无非就是通过HTTPSHandler创建了一个HTTPS协议的处理器对象,然后通过build_opener()函数构建了一个opener实现和指定服务器url地址之间的数据访问操作。鉴于此,我们也可以自定义opener的实现过程:
# -*- coding:utf-8 -*-
import urllib2
# 引入需要的模块
handler = urllib2.HTTPSHandler()
# 创建一个HTTPS处理器对象
opener = urllib2.build_opener(handler)
# 构建一个开锁人对象
response = opener.open("https://www.taobao.com")
# 访问指定的数据
print response.read()
# 打印展示获取的数据
可能大家对于上述代码并没有什么太多的感受,认为我们就是通过手工编码的方式,将官方底层源代码重新实现了一次而已
在反爬虫的操作过程中,有一种反爬虫操作是针对出现异常访问量的ip地址进行封锁的操作,这样的情况下,你如果使用自己真实ip地址就很可能会导致自己的ip地址被封,再也不能访问目标数据了,此时~我们需要使用代理ip地址帮助我们实现对于目标数据的访问
代理ip地址的操作,主要处理和代理服务器之间的数据交互,就需要使用到urllib2模块中的代理操作对象ProxyHandler,如果是常规的HTTPHandler/HTTPSHandler是不能满足我们的需要的
# -*- coding:utf-8 -*-
# 引入需要的模块
import urllib2
# 创建代理处理器
proxy_handler = urllib2.ProxyHandler({'http': '61.135.217.7:80'})
# 创建Opener对象
proxy_opener = urllib2.urlopen(proxy_handler)
# 打开指定的网址
response = proxy_opener.open("https://www.taobao.com")
# 打印展示获取的数据
print response.read()
这里对于代理ip地址做一个简要的说明
通常情况下,我们会看到代理服务器会有高匿、匿名、透明的一个描述,这个描述主要是用来作什么的呢?其实主要是在代理服务器这一侧,通过变量控制被代理的客户端是否对于访问的服务器可见,主要通过三个参数进行控制[REMOTE_ADDR, HTTP_VIA,HTTP_X_FORWARDED_FOR]
透明代理:服务器明确知道你用了代理,你的ip和代理的ip服务器都能直接获取
- REMOTE_ADDR:proxy_ip
- HTTP_VIA:proxy_ip
- HTTP_X_FORWARDED_FOR:client_ip
匿名代理:服务器知道你用了代理,可以获取到代理服务器ip,但是获取不到你的ip
- REMOTE_ADDR:proxy_ip
- HTTP_VIA:proxy_ip
- HTTP_X_FORWARDED_FOR:proxy_ip
高匿代理:服务器根本不知道你是否代理访问
- REMOTE_ADDR:proxy_ip
- HTTP_VIA:not datemined
- HTTP_X_FORWARDED_FOR:not datemined
尽管我们可以通过代理的方式来将我们自己隐藏在幕后进行数据的采集工作,但是不得不说~免费的东西总是会存在一定的问题的,网络上公布的所有的免费代理ip地址,稳定性非常的差,可持续性较低,流量很小速度很慢并且存活时间较短
那么 ~, 有人看到了商机,专门有公司开始从事代理IP的操作,设置各种私密代理IP,然后设置不同的限制规则,给爬虫工程师或者其他需要的人提供代理服务!
私密代理需要设置对应的账号和密码的验证操作,在实际操作过程中,需要简单的设置即可,和以后爬虫其他高级使用方式基本是一致的,在创建自定义Handler时进行如下的操作
proxy_url = "188.68.16.55:80"
user = "damu"
passwd = "mu@!#13"
# format()函数和%符号一样,都是通过占位符的方式替换数据的
proxy_handler = urllib2.ProxyHandler("http://{}:{}@{}".format(proxy_url, user, passwd))
6. 会话跟踪之cookie操作
在很多网站上,都使用了基于cookie的会话跟踪技术,如有道在线翻译的操作过程中就是用cookie进行了状态保持的操作;
在进行爬虫操作的过程中,我们会大量的使用到cookie的操作,对于cookie的操作,最原始的方式就是在请求头中设置cookie字段,通过抓包的数据给cookie设置固定的数据即可,但是在实际操作过程中,cookie数据如果动态发生变化时候就会变得异常繁琐
python提供了cookielib模块,可以很方便的实现cookie数据的读写操作,核心提供了CookieJar、FileCookieJar、MozillaCookieJar等模块实现对cookie的不同操作方式
服务器和客户端的交互仅限于请求/响应过程,结束之后便断开,在下一次请求时,服务器会认为新的客户端。
为了维护他们之间的链接,让服务器知道这是前一个用户发送的请求,必须在一个地方保存客户端的信息。
Cookie:通过在 客户端 记录的信息确定用户的身份。
Session:通过在 服务器端 记录的信息确定用户的身份。
* 获取访问网站的cookie数据
-- coding:utf-8 --
引入需要的模块
import urllib2
import cookielib
创建一个基于cookie的核心操作对象
cookie = cookielib.CookieJar()
创建一个基于cookie的操作对象
cookie_handler = urllib2.HTTPCookieProcessor(cookie)
创建一个可以操作cookie的opener对象
cookie_opener = urllib2.build_opener(cookie_handler)
打开目标网站
response cookie_opener.open("http://fanyi.youdao.com")
查看cookie数据
for item in cookie:
print item.name + "--" + item.value
* 将访问到的cookie数据存储到文件中
-- coding:utf-8 --
引入需要的模块
import urllib2
imorot cookielib
创建基于cookie的核心操作对象
cookie = urllib2.MozillaCookieJar("youdao.txt")
创建基于cookie的操作对象
cookie_handler = urllib2.HTTPCookieProcessor(cookie)
创建可以操作Cookie的opener对象
cookie_opener = urllib2.build_opener(cookie_handler)
访问目标网站
response = cookie_opener.open("http://fanyi.youdao.com")
保存cookie到文件中
cookie.save()
* 从文件中加载cookie数据
-- coding:utf-8 --
引入需要的模块
import urllib2
import cookielib
创建基于cookie的核心对象
cookie = cookielib.CookieJar()
加载cookie文件中的数据
cookie.load("youdao.txt")
创建基于cookie的操作对象
cookie_handler = urllib2.HTTPCookieProcessor(cookie)
创建可以操作cookie的opener对象
cookie_opener = urllib2.build_opener(cookie_handler)
打开目标地址
response = cookie_opener.open("http://fanyi.youdao.com")
..其他代码请参考有道在线翻译爬虫程序
### 7\. 爬虫异常行为
在进行目标网址的访问和数据采集过程中,由于目标网址拼写、网络阻塞等等各种问题,可能会导致目标网址访问失败的情况,在python爬虫模块中,主要通过URLError类型来规范定义爬虫访问url地址出现的问题,同时提供了继承自URLError的HTTPError类型专门针对HTTP请求进行的异常行为的处理
但是切记,一切服务器返回的异常行为,都是服务器内部直接出现错误行为并且返回错误状态码导致的异常行为,如果服务器本身已经处理了访问的异常操作的话,爬虫程序是不会得到异常数据的
* 访问异常行为
-- coding:utf-8 --
引入需要的模块
import urllib2
定义请求对象
requset = urllib2.Request("http://www.dailiyun.com/damu")
使用try-except包裹请求
try:
response = urllib2.urlopen(request)
print(response.read())
except URLError, err
print err.code
print err
print "程序运行完成"
最终运行的结果如下:
404
HTTP Error 404: Not Found
程序运行完成
* 访问异常行为[服务器处理了异常]
-- coding:utf-8 --
引入需要的模块
import urllib2
定义请求对象
requset = urllib2.Request("http://www.baidu.com/damu")
使用try-except包裹请求
try:
response = urllib2.urlopen(request)
print(response.read())
except URLError, err
print err.code
print err
print("程序运行完成")
最终执行的结果如下:
<!DOCTYPE html>
<html>
...
<html>
程序运行完成
我们可以看到程序并没有出现任何和异常行为相关的错误信息,因为百度的服务器已经将404的异常行为在服务器中进行了处理并且返回了指定的404网页数据,所以爬虫在访问时获取到了404网页的数据
尽管一个完善的服务器端代码可以将很多异常行为直接补货并且处理掉,将异常行为掐死在摇篮中给用户提供更加友好的体验,但是在实际爬虫操作过程中,还是有大量的网站并没有对异常访问进行处理,所以我们要通过异常处理的方式得到异常访问代码,方便排查解决爬虫遇到的各种问题!
* 特殊的异常行为:HTTPS数字签名
所有的HTTPS安全网站,都会有自己的数字签名证书,由第三方数字证书认证中心发放并管理,主要目的是防止请求数据被恶意篡改和伪造,通过双向非对称加密的方式保证数据传输的安全性
但是不是所有的网站都在第三方数字签名认证中心登记并颁发证书的,所以某些https开头的网站在访问的时候浏览器后提示不安全的信息提示,必须主动忽略警告信息才能正常访问,如[http://www.12306.cn](https://link.jianshu.com?t=http%3A%2F%2Fwww.12306.cn)就是如此,我们通过如下程序进行访问时就会出错:
import urllib2
request = urllib2.Request("https://www.12306.cn")
response = urllib2.urlopen(request)
print response.read()
出现如下的错误提示:
urllib2.URLError: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:661)>
这样的情况下,我们必须手工操作忽略警告信息并继续访问,可以通过Python内置的ssl模块忽略数字签名验证访问
import ssl
..
创建一个忽略警告的上下文对象
context = ssl._create_unverify_context()
..
将忽略验证的环境包含在请求中发送
response = urllib2.urlopen(url, context=context)
..
### 跟踪案例:爬取豆瓣电影上选电影中各个板块的电影名称和评分
**提示1:** [https://movie.douban.com/explore#!type=movie&tag=%E7%83%AD%E9%97%A8&sort=recommend&page_limit=20&page_start=40](https://link.jianshu.com?t=https%3A%2F%2Fmovie.douban.com%2Fexplore%23%21type%3Dmovie%26tag%3D%25E7%2583%25AD%25E9%2597%25A8%26sort%3Drecommend%26page_limit%3D20%26page_start%3D40)
**提示2:** 针对每个类型尽可能多的爬取:热门 最新 经典 可播放 豆瓣高分 冷门佳片 华语 欧美 韩国 日本 动作 喜剧 爱情 科幻 悬疑 恐怖 文艺
**提示3:** 正则表达式筛选数据
**提示4:** 统计爬取的时间和数据量的大小