python爬虫爬取图片的简单步骤和实现代码

目录

1. 如何获取网页信息

1). 直接从网络读取

2). 先将网页源码保存到本地,再读取

2. 分析获取到的网页信息,提取需要的信息(图片地址)

3. 使用request将图片保存到本地以及会碰到的一些问题

1)获取图片信息,再保存到本地文件中

2). 超时处理

3). 读写超时

4). 超时重试

4. 使用urllib 将图片保存到本地以及会碰到的一些问题

1). 使用 urllib 

2). 超时处理

2). 再次下载

3). 显示下载进度

5. urllib和requests设置超时后添加headers的问题

 1). requests设置

2). urllib设置

6. 总结


本人略微学了一点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. 总结

    其实还是挺简单的(因为我没碰到难的。。。),最难的就是分析网页结构,都是套路。

发布了48 篇原创文章 · 获赞 36 · 访问量 13万+

猜你喜欢

转载自blog.csdn.net/weixin_42845682/article/details/102756027