一、urllib模块
1.urllib简介
在Python2版本中,有urilib和urlib2两个库可以用来发送request(请求)。而在Python3中,统一为urllib
urllib中包括了四个模块
-urllib.request可以用来发送request获取request结果
-urllib.parse用来解析和处理url
-urllib.error包含了urllib.request产生的异常
-urllib.rebotparse用来解析页面的robots.txt
2.爬取网页
现需要导入到用的模块,urllib.request
导入模块后,我们需要用到urlib.request.urlopen打开并爬取一个网页
读取内容常见的有3种方式:
-read()读取文件的全部内容,与readlines()不同的是,read()会把读取到的内容赋给一个字符串变量。
-readlines()读取文件的全部内容,readlines()会把读取的内容赋值给一个列表变量
-readline()读取文件的一行内容
实例:
import urllib.request
html=urllib.request.urlopen('http://www.baidu.com')
html.readline()
html.read(4096)
html.readlines()
可以体会一下输出结果
3.下载网络资源
urilib不仅可以下载网页,也可以下载其他网络资源
有些文件比较大,需要像读取文件一样,每次读取一部分数据,并依次存入文件中
- html = urlopen(http://www.baidu.com)
- with open('/tmp/baidu.html', 'wb') as fobj:
- while True:
- data = html.read(4096)
- if not data:
- break
- fobj.write(data)
- html.close()
4.数据编码
一般来说,URL标准中只允许一部分ASCII字符,比如数字、字母、部分符号
而其他的一些字符,比如汉字,是不合URl标准的,此时需要做编码
使用urllib.reqeust.quote()进行
urllib.request.quote('hello world!') #字符编码
'hello%20world%21'
urllib.request.unquote(''hello%20world%21'') #字符反编码
hello world!
5.HTTP异常处理
如果访问的页面不存在或拒绝访问,程序将抛出异常,捕获异常需导入urllib.error模块
创建get_web3.py文件,实现访问不存在的路径和ban目录时,捕获404和403错误
,同时404错误打印“无此页面”,403错误打印“无权访问”,代码如下:
- import sys
- from urllib.request import urlopen
- from urllib.error import HTTPError #导入urllib.error模块,用HTTPError捕获异常信息
- def get_web(url, fname):
- try:
- html = urlopen(url) #打开网址时即可知道是否有异常,所以将本语句放入try语句
- except HTTPError as e: #捕获返回HTTPError类的实例e
- print(e)
- if e.code == 403: #捕获异常状态码如果等于403
- print('权限不足') #输出'权限不足'
- elif e.code == 404: #捕获异常状态码如果等于404
- print('没有那个地址') #输出'没有那个地址'
- return #return后面代码均不执行
- open(fname, 'wb') as fobj:
- while True:
- data = html.read(4096)
- if not data:
- break
- fobj.write(data)
- html.close()
- if __name__ == '__main__':
- get_web(sys.argv[1], sys.argv[2])
测试脚本执行:
访问不存在页面:
- [root@localhost day11]# python3 get_web.py http://127.0.0.1/abc/ /tmp/abc.html
- HTTP Error 404: Not Found
- 没有那个地址
访问ban目录:
- [root@localhost day11]# chmod 000 /tmp/abc.html
- [root@localhost day11]# python3 get_web.py http://127.0.0.1/ban/ /tmp/abc.html
- HTTP Error 403: Forbidden
- 权限不足
二、小实验--爬取网页并存为文件
要求:
编写一个get_web.py脚本,实现以下功能:
- 爬取的网页为http://www.baidu.com
- 保存的文件名为baidu.html
方案:
导入sys模块,用sys.argv方法获取get_web函数实参,让用户在命令行上提供http://www.baidu.com和/tmp/baidu.html两个参数,调用get_web函数实现如下功能:
- 1.导入urllib模块,使用urllib模块的urlopen函数打开url(即网址),赋值给html
- 2.以写方式打开/tmp/baidu.html文件
- 3.以循环方式:
- 读html获取的数据,保存到data
- 将data写入/tmp/baidu.html
- 4.关闭html
步骤一:编写脚本
- [root@localhost /]# vim get_web.py
- #!/usr/bin/env python3
- import sys
- from urllib.request import urlopen
- def get_web(url, fname):
- html = urlopen(url) #使用urllib模块的urlopen函数打开url,赋值给html
- with open(fname, 'wb') as fobj:
- while True:
- data = html.read(4096)
- if not data:
- break
- fobj.write(data)
- html.close()
- if __name__ == '__main__':
- get_web(sys.argv[1], sys.argv[2]) #让用户在命令行上提供网址和下载数据保存位置
步骤二:测试脚本执行
- [root@localhost /]# python3 get_web.py http://baidu.com /tmp/baidu.html
- [root@localhost /]# cat /tmp/tedu.html
- 执行cat命令可以看到/tmp/tedu.html文件中爬取到的内容
三、小实验2--爬取网页图片
我们上一个实验是爬取网页内容,那么大家有没有想过爬取网页当中的图片或视频文件?
这个实验主要是教大家爬取网页图片
首先回顾一下上面的实验,我们通过urllib.request.urlopen("网址")获取到网页内容
然后通过sys模块的存文件将获取到的网页内容存到文件中,如下
1.怎样获取图片?
要回答这个问题,大家应该回顾一下html,在html页面中,图片使用<img/>标签,在这个标签里有一个属性我src,而src里的链接,就是图片的保存路径,如图
那就可以这样解释,其实图片也相当于一个url,或者一个网页,我们可以使用urllib.request.urlopen("网址")来获取内容
2.所以我们可以按照这样的顺序来做这个实验:
- 1.获取整个网页的内容,并保存到一个文件当中
- 2.获取步骤1中的文件内容,并挑选出图片的路径(使用正则表达式)
- 3.使用urllib请求图片内容,并下载到一个文件夹中
- 4.查看图片
3.图片的后缀名:
常用图片后缀名有png和jpg,还有jpeg和gif
4.那么我们来分析一下图片的url
【python正则表达式讲解】
看下面图片的url
根据这一的格式,我们可以写出正则表达式
'http://[\w/-.]+\.(jpg|png|jpeg|gif)'
分析一下这个表达,‘http://’这一部分是固定的,正则表达式直接照抄就行
那‘cdn.tmooc.cn/tmooc-web/css/img/’这一部分是变动的,那么就需要一些匹配符,仔细看这个路径出现了什么类型的字符,
是不是出现了:字符 '.' '/' '-',可能出现的还有数字,那根据正则表达式规则我们知道
\w | 匹配 任何 字母和数字,相当于[a-z A-Z 0-9 _ ] 注:还有下划线 |
[abc] | 匹配 ‘a’ 或 ‘b’ 或 ‘c’中任意一个 |
+ | 匹配1个或多个的字符串 |
通过整合以上表达式,得出结果: [\w./-]+
这样我们就完成了“http://cdn.tmooc.cn/tmooc-web/css/img/tmooc-logo” 这个部分的内容
最后还有一个后缀,我之前说了图片的几种常见类型,那么可以写成:\.(jpg|png|jpeg|gif)
‘\.’匹配点,(jpg|png|jpeg|gif)匹配任意一种类型
得出的完整正则为'http://[\w/-.]+\.(jpg|png|jpeg|gif)'
5.更多的图片url格式
有的人就问了,有些网站不是http开头的,而是https开头的
很简单,多给一个选择即可:'(http|https)://[\w/-.]+\.(jpg|png|jpeg|gif)'
我们多找一点网站看看图片显示url格式
华为的:
淘宝的:
京东:
百度:
看到一个共同点没有,都没有http或https,为什么呢,因为这些网站都是用的相对路径,而不是绝对路径,怎么解决?
很好解决,看下面的正则表达式
(http|https)?:?//[\w./-]+\.(jpg|png|jpeg|gif)
我使用了一个?做处理
? | 匹配0个或1个,为非贪婪方式 |
那么眼尖的人又看到了华为的图片url自有一个'/',怎么处理?
很简单
+ | 匹配1个或多个的字符串 |
那么我们最终的表达式如下:
(http|https)?:?/+[\w./-]+\.(jpg|png|jpeg|gif)
完成了这一步,50%以上的网站你都可以爬取图片了
为什么说是50%?
如果我们用上面的表达式,则会匹配出这样的内容
没有http或https,那有的人就说了,加一个判断,判断是否有http或https,没有就加上
可以加上,我们可以这样加,
做一个字符串拼接
那问题就来了,有些url是只有一个'/'的,那我是怎么加呢,如果我强加就是这样的结果
只有一个'/',这样我们的urllib是获取不到图片内容的
那怎么办?
我们需要做一个二次正则匹配
匹配表达式:[\w]+[\w./-]+\.(jpg|png|jpeg|gif)
意思是获取'//'之后的内容
二次匹配后得到
那再做字符串拼接
问题解决了下面看代码
#!/usr/bin/env python3
import sys
import re
import os
from urllib.request import urlopen
#获取图片链接(url)
def get_url(fname):
cpatt = re.compile("(http|https)?:?/+[\w./-]+\.(jpg|png|jpeg|gif)") #第一个正则用于匹配出图片链接
cpatt2 = re.compile("[\w]+[\w./-]+\.(jpg|png|jpeg|gif)") #第二个正则用于过滤链接
result = [] #保存链接集
with open(fname) as fobj: #获取文件内容
for line in fobj: #一行一行读
m = cpatt.search(line) #第一次匹配
if m: #如果匹配到进一步处理
m2 = cpatt2.search(m.group()) #第二次匹配,用于过滤链接
https = "https://"+""+m2.group() # 由于我不知道是http还是https,所以这里我们
http = "http://"+""+m2.group() #都保存到集合
result.append(http) #添加到集合
result.append(https)
return result
#下载网页或图片信息
def get_web(url,fname):
try:
html=urlopen(url) #下载图片
with open(fname,'wb') as f: #保存到文件
while True:
data = html.read(4096)
if not data:
break
f.write(data)
html.close()
except:
return
if __name__=="__main__":
get_web('', '/mnt/tedu.html') #首先下载这个网页的内容,保存到文件
urls = get_url('/mnt/tedu.html') #再获取图片的链接保存到列表
img_dir = '/mnt/images' #存放文图片的目录
if not os.path.exists(img_dir): #没有该目录就创建
os.mkdir(img_dir)
for url in urls:
fname = os.path.join(img_dir, url.split('/')[-1]) #保存方式:目录/图片链接
get_web(url, fname) #下载图片
执行脚本,成功后获取图片:
- [root@localhost day11]# python3 download.py
- [root@localhost day11]# nautilus /tmp/images #查看图片