一、Handler处理器 和 自定义Opener(引擎们)
-
opener是 urllib2.OpenerDirector 的实例,我们之前一直都在使用的urlopen,它是一个特殊的opener(也就是模块帮我们构建好的)。
-
但是基本的urlopen()方法不支持代理、cookie等其他的HTTP/HTTPS高级功能。所以要支持这些功能:
- 使用相关的
Handler处理器来创建特定功能的处理器对象; - 然后通过
urllib2.build_opener()方法使用这些处理器对象,创建自定义opener对象; - 使用自定义的opener对象,调用
open()方法发送请求。
- 使用相关的
-
如果程序里所有的请求都使用自定义的opener,可以使用
urllib2.install_opener()将自定义的 opener 对象 定义为 全局opener,表示如果之后凡是调用urlopen,都将使用这个opener(根据自己的需求来选择)
自定义简单的opener
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import urllib2
# 构建一个HTTPHandler处理器对象,支持处理HTTP的请求
#http_handler = urllib2.HTTPHandler()
# 不加参数时,和使用urllib2.urlopen()发送HTTP/HTTPS请求得到的结果是一样的。
# 在HTTPHandler增加参数"debuglevel=1"将会自动打开Debug log 模式,程序在执行的时候会打印收发包的报头(请求头)的信息
http_handler = urllib2.HTTPHandler(debuglevel=1)
# 调用build_opener()方法构建一个自定义的opener对象,参数是构建的处理器对象
opener = urllib2.build_opener(http_handler)
request = urllib2.Request("http://www.baidu.com/")
response = opener.open(request)
# print response.read()
ProxyHandler处理器(代理设置)
使用代理IP,这是爬虫/反爬虫的第二大招,通常也是最好用的。
很多网站会检测某一段时间某个IP的访问次数(通过流量统计,系统日志等),如果访问次数多的不像正常人,它会禁止这个IP的访问。
所以我们可以设置一些代理服务器,每隔一段时间换一个代理,就算IP被禁止,依然可以换个IP继续爬取。
urllib2中通过ProxyHandler来设置使用代理服务器,下面代码说明如何使用自定义opener来使用代理:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import urllib2
# 代理开关,表示是否启用代理
proxyswitch = False
# 构建一个Handler处理器对象,参数是一个字典类型,包括代理类型和代理服务器IP+PROT
# httpproxy_handler = urllib2.ProxyHandler({"http" : "124.88.67.54:80"})
# 如果代理服务器有密码
authproxy_handler = urllib2.ProxyHandler({"http" : "mr_mao_hacker:sffqry9r@114.215.104.49:16816"})
# 构建了一个没有代理的处理器对象
nullproxy_handler = urllib2.ProxyHandler({})
if proxyswitch:
opener = urllib2.build_opener(httpproxy_handler)
else:
opener = urllib2.build_opener(nullproxy_handler)
# 构建了一个全局的opener,之后所有的请求都可以用urlopen()方式去发送,也附带Handler的功能
urllib2.install_opener(opener)
request = urllib2.Request("http://www.baidu.com/")
response = urllib2.urlopen(request)
#print response.read().decode("gbk")
print response.read()
免费代理网站:
如果代理IP足够多,就可以像随机获取User-Agent一样,随机选择一个代理去访问网站。
HTTPPasswordMgrWithDefaultRealm()
HTTPPasswordMgrWithDefaultRealm()类将创建一个密码管理对象,用来保存 HTTP 请求相关的用户名和密码,主要应用两个场景:
- 验证代理授权的用户名和密码 (
ProxyBasicAuthHandler()) - 验证Web客户端的的用户名和密码 (
HTTPBasicAuthHandler())
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import urllib2
test = "test"
password = "123456"
webserver = "192.168.21.52"
test2 = "x5456"
password2 = "5456"
webserver2 = "119.130.115.226:808"
# 构建一个密码管理对象,可以用来保存和HTTP请求相关的授权账户信息
passwordMgr1 = urllib2.HTTPPasswordMgrWithDefaultRealm()
passwordMgr2 = urllib2.HTTPPasswordMgrWithDefaultRealm()
# 添加授权账户信息,第一个参数realm如果没有指定就写None,后三个分别是站点IP,账户和密码
passwordMgr1.add_password(None, webserver, test, password)
# 代理服务器的ip:port、用户名、密码
passwordMgr2.add_password(None, webserver2, test2, password2)
# HTTPBasicAuthHandler() HTTP基础验证处理器类
httpauth_handler = urllib2.HTTPBasicAuthHandler(passwordMgr1)
# 处理代理基础验证相关的处理器类
proxyauth_handler = urllib2.ProxyBasicAuthHandler(passwordMgr2)
# 构建自定义opener,里面可以加多个参数
opener = urllib2.build_opener(httpauth_handler, proxyauth_handler)
# urllib2.install_opener(opener) # 加上这句话,urllib2.urlopen()使用的是我们自写的opener
request = urllib2.Request("http://192.168.21.52/")
# 用加上授权验证信息
response = opener.open(request)
# 没有授权验证信息
#response = urllib2.urlopen(request)
print response.read()
Cookie的应用
Cookies在爬虫方面最典型的应用是判定注册用户是否已经登录网站,用户可能会得到提示,是否在下一次进入此网站时保留用户信息以便简化登录手续。
cookielib库 和 HTTPCookieProcessor处理器
在Python处理Cookie,一般是通过cookielib模块和 urllib2模块的HTTPCookieProcessor处理器类一起使用。
cookielib模块:主要作用是提供用于存储cookie的对象
HTTPCookieProcessor处理器:主要作用是处理这些cookie对象,并构建handler对象。
cookielib 库
该模块主要的对象有CookieJar、FileCookieJar、MozillaCookieJar、LWPCookieJar。
CookieJar:管理HTTP cookie值、存储HTTP请求生成的cookie、向传出的HTTP请求添加cookie的对象。整个cookie都存储在内存中,对CookieJar实例进行垃圾回收后cookie也将丢失。
FileCookieJar (filename,delayload=None,policy=None):从CookieJar派生而来,用来创建FileCookieJar实例,检索cookie信息并将cookie存储到文件中。filename是存储cookie的文件名。delayload为True时支持延迟访问访问文件,即只有在需要时才读取文件或在文件中存储数据。
MozillaCookieJar (filename,delayload=None,policy=None):从FileCookieJar派生而来,创建与
Mozilla浏览器 cookies.txt兼容的FileCookieJar实例。LWPCookieJar (filename,delayload=None,policy=None):从FileCookieJar派生而来,创建与
libwww-perl标准的 Set-Cookie3 文件格式兼容的FileCookieJar实例。
其实大多数情况下,我们只用CookieJar(),如果需要和本地文件交互,就用 MozillaCookjar() 或 LWPCookieJar()
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import urllib,urllib2,cookielib
# 通过CookieJar()类构建一个cookieJar()对象,用来保存cookie的值
cookie = cookielib.CookieJar()
# 通过HTTPCookieProcessor()处理器类构建一个处理器对象,用来处理cookie
# 参数就是构建的CookieJar()对象
cookie_handler = urllib2.HTTPCookieProcessor(cookie)
# 构建一个自定义的opener
opener = urllib2.build_opener(cookie_handler)
# 通过自定义opener的addheaders的参数,可以添加HTTP报头参数
opener.addheaders = [("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36")]
# renren网的登录接口
url = "http://www.renren.com/PLogin.do"
# 需要登录的账户密码
data = {"email":"mr_mao_hacker@163.com", "password":"alarmchime"}
# 通过urlencode()编码转换
data = urllib.urlencode(data)
# 第一次是post请求,发送登录需要的参数,获取cookie
request = urllib2.Request(url, data = data)
# 发送第一次的post请求,生成登录后的cookie(如果登录成功的话)
response = opener.open(request)
# 获取个人主页
print response.read()
# 已经有cookie了,那还不干点别的事 ,第二次可以是get请求,这个请求将保存生成cookie一并发到web服务器,服务器会验证cookie通过
response_deng = opener.open("http://www.renren.com/410043129/profile")
# 获取登录后才能访问的页面信息
print response_deng.read()
小Tips:
两种添加报头的方法 request.add_header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36") opener.addheaders = [("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36")]
二、Re模块
实际上爬虫一共就四个主要步骤:
- 明确目标 (要知道你准备在哪个范围或者网站去搜索)
- 爬 (将所有的网站的内容全部爬下来)
- 取 (去掉对我们没用处的数据)
- 处理数据(按照我们想要的方式存储和使用)
对于文本的过滤或者规则的匹配,最强大的就是正则表达式,是Python爬虫世界里必不可少的神兵利器。
什么是正则表达式
正则表达式,又称规则表达式,通常被用来检索、替换那些符合某个模式(规则)的文本。
正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。
给定一个正则表达式和另一个字符串,我们可以达到如下的目的:
- 给定的字符串是否符合正则表达式的过滤逻辑(“匹配”);
- 通过正则表达式,从文本字符串中获取我们想要的特定部分(“过滤”)。
正则表达式匹配规则
python中的re模块
pattern = re.compile(r"\d") # r表示字符串不受\n,\d,\w等转义字符影响,u表示是unicode字符串
pattern.match(str,begin,end) # 起始位置,终止位置(是以切片的形式顾头不顾尾)
# 从起始位置开始往后找,返回第一个符合规则的(返回的是match对象),只匹配一次
pattern.search(str,begin,end)
# 从随机位置开始往后找,返回第一个符合规则的(返回的也是match对象),只匹配一次
pattern.findall(str,begin,end)
# 匹配全部字符串,返回列表
pattern.split(str,count) # 切割次数
# 分割字符串
pattern.sub('替换文字',"字符串")
re.I ==> 忽略大小写 re.S ==> 全文匹配
用法
match
search
findall
split
sub
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 4 import urllib2,urllib,re 5 6 class Spider(object): 7 def __init__(self): 8 self.page = 1 9 self.switch = True 10 11 def loadPage(self): 12 """ 13 下载页面 14 :return: 15 """ 16 url = "http://www.neihan8.com/article/list_5_"+str(self.page)+".html" 17 # headers = {"User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"} 18 # request = urllib2.Request(url,headers=headers) 19 # response = urllib2.urlopen(request) 20 headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"} 21 request = urllib2.Request(url, headers=headers) 22 print("正在下载...") 23 response = urllib2.urlopen(request) 24 html = response.read().decode('gbk') 25 self.dealPage(html) 26 27 def dealPage(self,html): 28 """ 29 处理页面 30 :return: 31 """ 32 pattern = re.compile('<div\sclass="f18 mb20">(.*?)</div>',re.S) 33 content_list = pattern.findall(html.replace("<p>","").replace("</p>","").replace("<br />","").replace("<br>",""). 34 replace("…","").replace('”','').replace("“",'')) 35 for i in content_list: 36 self.writePage(i) 37 38 def writePage(self,duanzi): 39 """ 40 保存段子 41 :return: 42 """ 43 with open('duanzi.txt','a') as f: 44 print("正在保存...") 45 f.write(duanzi.encode('utf-8')) 46 47 def startWork(self): 48 """ 49 控制爬虫是否运行 50 :return: 51 """ 52 while self.switch: 53 self.loadPage() 54 userinput = raw_input("任意键继续爬取,输入q停止爬取") 55 if userinput == 'q': 56 self.switch = False 57 print("谢谢使用...") 58 self.page += 1 59 60 if __name__ == '__main__': 61 duanziSpider = Spider() 62 duanziSpider.startWork()