从今天起开始进入爬虫的世界。网络爬虫这种东西都了解能干嘛,我第一次知道时感觉特别棒,希望自己能写一个,不管功能强大与否。Python语言就是写爬虫的得力工具。我预计通过两篇博文来介绍一下Python基础库:urllib和requests(后者为重)。然后爬取豆瓣Top250电影信息。
在Python2中实现发送请求的库有urllib和urllib2,到了Python3这两个库统一为了urllib。urllib包含如下4个模块:
**作用:**模拟浏览器发送一个请求,实现对目标url的访问。
语法:urllib.request.urlopen(url, data=None, [timeout, ]*, cafile=None, capath=None, cadefault=False, context=None)
参数:
import urllib.request
response = urllib.request.urlopen("https://python.org")
print(response.read().decode("UTF-8"))#使用utf-8对网页解码,如果出现错误,再加上"ignore"参数
部分结果:
urlopen返回值是
。它是HTTPResponse对象,主要包含read()、readinto()、getheader(name)、getheaders()、fileno()等方法,以及msg、version、status、reason、debuglevel、closed等属性。通过read()得到返回的内容,访问status属性得到返回码,200代表成功,404代表网页未找到。常用返回码可以查看:https://www.runoob.com/http/http-status-codes.html。
示例:
import urllib.request
response = urllib.request.urlopen("https://python.org")
print("返回码:",response.status)
print("获取所有请求头信息:",response.getheaders())
print("获取指定请求头信息:",response.getheader("Server"))
import urllib.request
response = urllib.request.urlopen("https://python.org",timeout=0.1)#设置0.1s,这一定会超时
print(response.status)
有时候由于网络的不稳定等原因会导致请求超时,如果不设置对应的处理方法程序就终止了。我们可以使用try except语句捕获超时异常,从而跳过某一网站的爬取或重复爬取。
import urllib.request
from urllib.error import URLError
import socket
try:
response = urllib.request.urlopen("https://python.org",timeout=0.1)
except URLError as e:
if isinstance(e.reason,socket.timeout):
print("请求超时,做其他处理")
import urllib.request
req = urllib.request.Request("https://python.org")#构建一个Request对象
response = urllib.request.urlopen(req)#传递Request对象
print(response.read().decode("UTF-8"))
还是同样的方法,只是不同的配方,传递Request对象了。
urllib.request.Request()详情如下:
语法:urllib.request.Request(url, data=None, headers={},origin_req_host=None, unverifiable=False,method=None)
参数:
Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36 QIHU 360SE
现在我们伪装成浏览器发送POST请求,并且传递data:
import urllib.request
import urllib.parse
url = "https://httpbin.org/post" #https://httpbin.org可以供我们测试http请求
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36 QIHU 360SE'}#伪装成浏览器
dataDic = {'name':'Tom','age':20}
data = bytes(urllib.parse.urlencode(dataDic),'UTF-8')
req = urllib.request.Request(url,data=data,headers=headers,method='POST')#构建一个Request对象
response = urllib.request.urlopen(req)#传递Request对象
print(response.read().decode("UTF-8"))
以上三种情况的处理我们都需要使用Handler来处理,Python提供了许多的Handler来帮助我们完成任务:
还要很多的Handler,这就需要大家查看官方文档了。
现在有了Handler,但是我们如何使用呢?接下来再介绍一个重要的类-----OperDirector,或者Opener。Opener也可以调用open()方法,返回值和urlopen一样。记住一个要点:Handler构建Opener。
from urllib.request import HTTPBasicAuthHandler,HTTPPasswordMgrWithDefaultRealm,build_opener
from urllib.error import URLError
import ssl
ssl._create_default_https_context = ssl._create_unverified_context#取消ssl验证
url="https://desktop-05hc07a:8443/svn/Test/"
username=""
passeord = ""
p = HTTPPasswordMgrWithDefaultRealm()#构建一个密码管理对象,用来保存需要处理的用户名和密码
p.add_password(None,url,user=username,passwd=passeord)#添加账户信息,第一个参数realm是与远程服务器相关的域信息,一般都是写None
auth_handler = HTTPBasicAuthHandler(p)#创建处理验证的Handler
opener = build_opener(auth_handler)#通过Handler构建opener
try:
result = opener.open(url)
html = result.read().decode("UTF-8",'ignore')#不是所有网页编码都是UTF-8的,我们可以使用开发者工具→控制台输入document.charset()查看编码
print(html)
except URLError as e:
print(e.reason)
from urllib.request import ProxyHandler,build_opener
from urllib.error import URLError
proxy_handler = ProxyHandler({'http':'219.141.153.41:80','https':'118.190.145.138:9001'})
opener = build_opener(proxy_handler)
try:
resp = opener.open("https://www.baidu.com")
print(resp.read().decode('utf-8'))
except URLError as e:
print(e.reason)
ProxyHandler传递一个字典类型,里面是我们设置的代理IP,我们可以添加许多代理IP。后面我们可以自己实现一个代理IP池,玩转代理。
import http.cookiejar,urllib.request
cookie = http.cookiejar.CookieJar()#声明CookieJar对象
handler = urllib.request.HTTPCookieProcessor(cookie)
opener = urllib.request.build_opener(handler)
resp = opener.open("https://www.baidu.com")
for item in cookie:
print(item.name +'='+item.value)
获取到cookie信息后可以将它以文本信息保存:
import http.cookiejar,urllib.request
filename = 'cookies.txt'
#原来的CookieJar要缓存MozillaCookisJar,它是CookieJar的子类,在生成文件时会用它,
#将cookie信息保存为Mozilla型浏览器格式
cookie = http.cookiejar.MozillaCookieJar(filename)
handler = urllib.request.HTTPCookieProcessor(cookie)
opener = urllib.request.build_opener(handler)
resp = opener.open('https://www.baidu.com')
cookie.save(ignore_discard=True,ignore_expires=True)#保存cookie直至废弃;保存cookie直至过期
我们可以使用load()方法获取保存的cookie文件:
import http.cookiejar,urllib.request
filename = 'cookies.txt'
cookie = http.cookiejar.MozillaCookieJar(filename)
cookie.load(filename,ignore_discard=True,ignore_expires=True)
handler = urllib.request.HTTPCookieProcessor(cookie)
opener = urllib.request.build_opener(handler)
resp = opener.open('https://www.baidu.com')
URLError类是OSError子类,是error异常模块的基类,有request模块产生的异常多可以通过捕获这个类来处理。它有一个reason属性,返回错误的原因。
from urllib import error,request
try:
request.urlopen("http://catwithwing.s1.natapp.cc/index.html")#访问一个不存在的网站
except error.URLError as e:
print(e.reason)
print("执行其他操作")
出现异常错误后程序没有崩掉,继续执行其他操作,这才是我们想要的结果。
HTTPError类是URLError类的子类,它用来专门处理HTTP请求错误,比如认证失败等。它有三个属性:
from urllib import error,request
try:
request.urlopen("http://catwithwing.s1.natapp.cc/index.html")#访问一个不存在的网站
except error.HTTPError as e:
print(e.code,e.reason,e.headers,sep='\n')
print("执行其他操作")
我们可以先捕获HTTPError,再捕获URLError。使程序更加健壮。
首先我们要知道一个协议-----Robots协议。它也称作爬虫协议,机器人协议,全称为网络爬虫排除标准(Robots Exclusion Protocol),它告诉爬虫或者搜索引擎哪些页面可以爬取,哪些不可以。它通常是一个robots.txt的文件,放在网站的根目录下。比如豆瓣的robots.txt就是https://www.douban.com/robots.txt
。
内容如下:
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: /trailer/
Disallow: /doubanapp/card
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设置此协议对哪些爬虫有效,*则表示所有的爬虫都要接受这个协议。
Disallow设置哪些目录是不允许爬取的,与之相对的是Allow,它设置哪些目录可以爬取。
Sitemap,主要是因为很多网站的内容都没有其他链接,为了把这些链接更好的连接起来,让爬虫能抓取更多的资源。
看看豆瓣的这个协议我们就会发现啥都不可以爬了。所以我们一般是不听的。
既然看了我们就来个示例,解析一下robots.txt文件吧:
from urllib import robotparser
rp = robotparser.RobotFileParser()
rp.set_url("https://www.douban.com/robots.txt")#设置robots..txt文件的url
rp.read()#读取robots文件并进行分析,这一步是必须的,如果不调用这个方法,后续的一切判断多少False,即使可以爬取的网页也是
#can_fetch()判断能否爬取指定URL,第一个参数是User-Agent,第二个参数是URL
print(rp.can_fetch('*',"https://www.douban.com/location/drama/search"))#判断https://www.douban.com/location/drama/search能不能爬取
有时候我们会在url上填写中文字符,如果我们不做任何处理是会导致乱码的,我们需要用quote()
方法将中文字符转化为URL编码。
import urllib.parse
url = "https://www.baidu.com/s?wd="+urllib.parse.quote("美女")
print(url)
将URL编码后的字符转为中文字符我们可使用unquote()
方法:
import urllib.parse
url = "https://www.baidu.com/s?wd=%E7%BE%8E%E5%A5%B3"
print(urllib.parse.unquote(url))