目录
3. 使用request将图片保存到本地以及会碰到的一些问题
4. 使用urllib 将图片保存到本地以及会碰到的一些问题
5. urllib和requests设置超时后添加headers的问题
本人略微学了一点python,主要是java。最近因为个人原因,需要爬虫抓取图片,近期面向百度编程,顺路总结一下。
写爬虫,首先需要获取网页信息,然后从网页的一堆标签中找到有图片信息(地址)的,然后再利用网址把图片保存起来。
1. 如何获取网页信息
1). 直接从网络读取
from bs4 import BeautifulSoup
url = "http://www.baidu.com"
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36'
}
# url 是网页地址
web_data = requests.get(url, headers=headers)
soup = BeautifulSoup(web_data.text, 'lxml')
读取的方法就是
web_data = requests.get(url, headers=headers)
soup = BeautifulSoup(web_data.text, 'lxml')
2). 先将网页源码保存到本地,再读取
from bs4 import BeautifulSoup
file = open('C:/Users/tj/Desktop/test.html','r',encoding='utf-8')
soup = BeautifulSoup(file,'lxml')
2. 分析获取到的网页信息,提取需要的信息(图片地址)
这里假设所有的图片都在img标签中,所有的img标签都在一个class属性名为beautiful的div(有且只有这么一个)中,图片的地址信息都在img的src属性中。
from bs4 import BeautifulSoup
import requests
# soup 有N多方法,find()、find_all()等 (具体用法百度),
img_list=soup.find('div', class_="beautiful").find_all('img')
# 对 img_list进行遍历,获取其中的信息保存到数组中
li=[]
for x in range(len(img_list)):
print(x+1,": ",img_list[x].attrs["src"])
li.append(img_list[x].attrs["src"])
3. 使用request将图片保存到本地以及会碰到的一些问题
1)获取图片信息,再保存到本地文件中
"""
描述
enumerate() 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在 for 循环当中。
Python 2.3. 以上版本可用,2.6 添加 start 参数。
语法
以下是 enumerate() 方法的语法:
enumerate(sequence, [start=0])
参数
sequence -- 一个序列、迭代器或其他支持迭代对象。
start -- 下标起始位置。
"""
from bs4 import BeautifulSoup
import requests
path="C:/Users/tj/Desktop/"
# i表示下标(从1开始), v表示数组的内容
for i,v in enumerate(li,start=1):
# 将 图片地址(即v) 再次放入request中
image = requests.get(v, timeout=10)
"""
存取图片过程中,出现不能存储int类型,故而,我们对他进行类型转换str()。
w:读写方式打开,b:二进制进行读写。图片一般用到的都是二进制。
"""
with open( path + str(i)+'.jpg', 'wb') as file:
# content:图片转换成二进制,进行保存。
file.write(image.content)
# 也可以使用如下方式保存到本地(和上面的保存到本地的方式其实一样)
dir = path + str(i)+'.jpg'
fp = open(dir, 'wb')
fp.write(image.content)
fp.close()
2). 超时处理
有些图片可能网址打不开,所以要添加超时处理:
from bs4 import BeautifulSoup
import requests
path="C:/Users/tj/Desktop/"
# i表示下标(从1开始), v表示数组的内容
for i,v in enumerate(li,start=1):
try:
# 将 图片地址(即v) 再次放入request中
image = requests.get(v, timeout=10)
except requests.exceptions.ConnectionError:
print('【错误】当前图片无法下载')
continue
with open( path + str(i)+'.jpg', 'wb') as file:
# content:图片转换成二进制,进行保存。
file.write(image.content)
网络请求不可避免会遇上请求超时的情况,在 requests 中,如果不设置你的程序可能会永远失去响应。
超时又可分为连接超时和读取超时。
连接超时指的是在你的客户端实现到远端机器端口的连接时(对应的是connect()
),Request 等待的秒数。就算不设置,也会有一个默认的连接超时时间(据说是21秒)。
3). 读写超时
读取超时指的就是客户端等待服务器发送请求的时间。(特定地,它指的是客户端要等待服务器发送字节之间的时间。在 99.9% 的情况下这指的是服务器发送第一个字节之前的时间)。
简单的说,连接超时就是发起请求连接到连接建立之间的最大时长,读取超时就是连接成功开始到服务器返回响应之间等待的最大时长。
如果你设置了一个单一的值作为 timeout,如下所示:
r = requests.get('https://github.com', timeout=5)
这一 timeout 值将会用作 connect 和 read 二者的 timeout。如果要分别制定,就传入一个元组:
r = requests.get('https://github.com', timeout=(3.05, 27))
读取超时是没有默认值的,如果不设置,程序将一直处于等待状态。我们的爬虫经常卡死又没有任何的报错信息,原因就在这里了。
4). 超时重试
一般超时我们不会立即返回,而会设置一个三次重连的机制。
def gethtml(url):
i = 0
while i < 3:
try:
html = requests.get(url, timeout=5).text
return html
except requests.exceptions.RequestException:
i += 1
其实 requests 已经帮我们封装好了。(但是代码好像变多了...)
import time
import requests
from requests.adapters import HTTPAdapter
s = requests.Session()
s.mount('http://', HTTPAdapter(max_retries=3))
s.mount('https://', HTTPAdapter(max_retries=3))
print(time.strftime('%Y-%m-%d %H:%M:%S'))
try:
r = s.get('http://www.google.com.hk', timeout=5)
return r.text
except requests.exceptions.RequestException as e:
print(e)
print(time.strftime('%Y-%m-%d %H:%M:%S'))
max_retries
为最大重试次数,重试3次,加上最初的一次请求,一共是4次,所以上述代码运行耗时是20秒而不是15秒
注意: 从 超时重试 直到这里, 参考了 https://www.cnblogs.com/gl1573/p/10129382.html, 在此表示感谢。
4. 使用urllib 将图片保存到本地以及会碰到的一些问题
1). 使用 urllib
urllib2 在 python3.x 中被改为urllib.request
import urllib
#i表示下标,从1开始; v表示数组的值(图片的地址)
for i,v in enumerate(li,start=1):
urllib.request.urlretrieve(v, path+str(x)+'.jpg')
2). 超时处理
有些图片可能网址打不开,所以要添加超时处理,但超时处理如下设置:
import urllib
import socket
#设置超时时间为30s
socket.setdefaulttimeout(30)
#i表示下标,从1开始; v表示数组的值(图片的地址)
for i,v in enumerate(li,start=1):
urllib.request.urlretrieve(v, path+str(x)+'.jpg')
2). 再次下载
同时,还可以超时后 使用递归 再次下载:
tips:新下载的文件会覆盖原来下载不完全的文件。
import urllib
import socket
#设置超时时间为30s
socket.setdefaulttimeout(30)
def auto_down(url,filename):
try:
urllib.urlretrieve(url,filename)
except urllib.ContentTooShortError:
print 'Network conditions is not good.Reloading.'
auto_down(url,filename)
#i表示下标,从1开始; v表示数组的值(图片的地址)
for i,v in enumerate(li,start=1):
auto_down(v, path+str(x)+'.jpg')
但下载 会尝试好几次,甚至十几次,偶尔会陷入死循环,这种情况是非常不理想的。需要避免陷入死循环,提高运行效率。
import urllib
import socket
#设置超时时间为30s
socket.setdefaulttimeout(30)
#i表示下标,从1开始; v表示数组的值(图片的地址)
for i,url in enumerate(li,start=1):
urllib.request.urlretrieve(v, path+str(x)+'.jpg')
try:
urllib.request.urlretrieve(url,path+str(x)+'.jpg')
except socket.timeout:
count = 1
while count <= 5:
try:
urllib.request.urlretrieve(url,path+str(x)+'.jpg')
break
except socket.timeout:
err_info = 'Reloading for %d time'%count if count == 1 else 'Reloading for %d times'%count
print(err_info)
count += 1
if count > 5:
print("downloading picture fialed!")
注意: 从 使用递归再次下载直到这里, 参考了 https://www.jianshu.com/p/a31745fef1d8 , 在此表示感谢。
3). 显示下载进度
同时使用 urllib.request.urlertriever() 的其他参数还可以显示出下载进度
import urllib
from urllib.request import urlretrieve
#解决urlretrieve下载文件不完全的问题且避免下载时长过长陷入死循环
def auto_down(url,filename):
try:
urlretrieve(url,filename,jindu)
except socket.timeout:
count = 1
while count <= 15:
try:
urlretrieve(url, filename,jindu)
break
except socket.timeout:
err_info = 'Reloading for %d time' % count if count == 1 else 'Reloading for %d times' % count
print(err_info)
count += 1
if count > 15:
print("下载失败")
"""
urlretrieve()的回调函数,显示当前的下载进度
a为已经下载的数据块
b为数据块大小
c为远程文件的大小
"""
global myper
def jindu(a,b,c):
if not a:
print("连接打开")
if c<0:
print("要下载的文件大小为0")
else:
global myper
per=100*a*b/c
if per>100:
per=100
myper=per
print("当前下载进度为:" + '%.2f%%' % per)
if per==100:
return True
注意: 上面这段参考: https://blog.csdn.net/HW140701/article/details/78254826 在此表示感谢。
5. urllib和requests设置超时后添加headers的问题
1). requests设置
request设置超时是用 requests.session() 返回的对象设置的,添加headers如下设置:
本人未亲自验证,不知道对错。
import requests
cookies.clear
headers = {
"User-Agent" : "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.1.6) ",
}
conn = requests.session()#设置一个回话
resp = conn.post('https://www.baidu.com/s?wd=findspace',headers=headers)
# 打印请求的头
print(resp.request.headers)
print resp.cookies
# 再访问一次:
resp = conn.get('https://www.baidu.com/s?wd=findspace',headers=headers)
print(resp.request.headers)
print resp.cookies
2). urllib设置
urllib如下设置:
本人未亲自验证,不知道对错。
注意:这个headers是不是字典。
opener = urllib.request.build_opener()
opener.addheaders =
[
('User-agent',
'Opera/9.80 (Android 2.3.4; Linux; Opera Mobi/build-1107180945; U; en-GB) Presto/2.8.149 Version/11.10')
]
urllib.request.install_opener(opener)
urllib.request.urlretrieve(URL, path) #path为本地保存路径
6. 总结
其实还是挺简单的(因为我没碰到难的。。。),最难的就是分析网页结构,都是套路。