爬虫实战:一键爬光指定网站所有图片(一)

前言:

​ 最近自己在做图片处理工具,最开始的初衷只是为了做一个图片深度学习项目,做的时候缺少大量的图片素材,手动去下载自己又是比较懒,并且操作起来非常的麻烦,于是自己写了一个单页面全图片的爬虫,等自己实现完功能之后,发现又有很多功能是可以优化的,于是在这个基础上我又做了一下功能升级,最终出了一个爬取指定网站所有图片的版本,当然,这个版本还有很多可以优化的点,我会在下面的实际过程中进行说明。本篇着重说明指定页面的图片抓取。

项目目标:

​ 指定某一页面进行图片资源进行爬取,保存到本地硬盘。

项目分析:

​ 1、本项目我们要实现某一个指定网站的页面URL,也就是提取href的链接。并将所有的内链创建到下一个任务当中去。

​ 2、除了页面中的href链接,我们还要读取页面中所有图片元素,通过get方式进行访问,读取后保存。

简单分析了一下,我们开始代码的实现

首先完成第2项的功能,我们要将页面图片元素提取出来,并写入到一个指定的文件目录当中,根据url中的文件名进行保存处理,考虑到我们未来功能复用性,所以我单独为单页面文件下载实现了一个类,(当然最终实现之后,发现Python中存在一些问题,这里我们在尾部再做解释)

我们先定义一个类,这里命名叫“DownloadImage.py”,因为是通过curl方式进行抓取采集图片列表,我们要定义一个header头属性,以及一个保存图片的本地的地址,定义代码如下:

headers = {
# 用户代理
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36'
}
_downloadDir = './img/'

因为这个类我们要复用,所以单独放到一个py文件中,方便后面进行引用。

在类的构造方法中,我们需要进行一个参数的初始化。为了下载指定页面的图片,那么我们需要指定一个页面(必要参数),为了路径可定义化,我们考虑增加了一个可选的本地存放的路径参数。 另外,因为每个页面其实有些图片我们是不需要的,比如一些页面的logo.gif,style.css中的样式图片我们并不需要,那我们这里就定义了一个图片过滤参数。

定义完之后我们需要对对象中的参数进行赋值,并初始化相关的参数。

def __init__(self,url,download_path=None,filter=[]):
    self.url = url
    self.initUrl()
    self.filter =filter

    # 定义图片下载图径
    if download_path:
        self.downloadPath=self._downloadDir + download_path
    else:
        self.downloadPath=self._downloadDir + self.urlParse.netloc
    self.makeDir()
    self.getImages()

首页我们在传入Url之后,将这个url赋给个类,方便对象中直接调用,然后我们要将url进行一个格式化,解析一次。

这个方法名就是initUrl(),方法的主用要途对过 urlparse方法,将url的域名和参数进行分离。整理成我们需要的格式。

原因是因为在http的页面当中,我们定义图片会有几种格式:

1.绝对路径,大部分网站的图片url都是这样,单独配置了域名资源进行显示

2.相对路径,有很多网站只有一台服务器,会把静态资源和html文件放在一起

3.某些站点的域名证书绑定是兼容性的,所以也会有//前缀进行http和https的兼容处理。

处理完url之后,我们将图片的过滤增加进去, 方法我不再细说,处理方式是通过正则进入搜索匹配来过滤的,比如传入[‘png’,‘gif’],那么所有的png和gif都不再被下载。

然后我们再说一下makeDir,初始化时会判断文件下载目录是否存在,如果不存在,则新建。

def makeDir(self):
    if not os.path.exists(self.downloadPath):
        os.makedirs(self.downloadPath)

最后,我们通过curl获取传参的url页面中所有的图片地址!

def getImages(self):
    response = requests.get(self.url, headers=self.headers)
    if response.status_code == 200:
        html = et.HTML(response.text)
        images = html.xpath('//img/@src')
        if self.filter:
            match = '|'.join(self.filter)
            self.Imageurls = []
            for value in images:
                if not re.search(match,value):
                    self.Imageurls.append(value)

        else:
            self.Imageurls=images
    else:
        return None

最终类代码如下:

# 抓取指定网页所有图片保存到本地
import requests
import os
from urllib.parse import *
from lxml import etree as et
import re
import sys
# 请求头
class DownloadImage(object):
    headers = {
    # 用户代理
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36'
    }
    _downloadDir = './img/'

    def __init__(self,url,download_path=None,filter=[]):
        self.url = url
        self.initUrl()
        self.filter =filter

        # 定义图片下载图径
        if download_path:
            self.downloadPath=self._downloadDir + download_path
        else:
            self.downloadPath=self._downloadDir + self.urlParse.netloc
        self.makeDir()
        self.getImages()

    #通用图片路径方法格式化
    def initUrl(self):
        self.urlParse=urlparse(self.url)

    def getImages(self):
        response = requests.get(self.url, headers=self.headers)
        if response.status_code == 200:
            html = et.HTML(response.text)
            images = html.xpath('//img/@src')
            if self.filter:
                match = '|'.join(self.filter)
                self.Imageurls = []
                for value in images:
                    if not re.search(match,value):
                        self.Imageurls.append(value)

            else:
                self.Imageurls=images
        else:
            return None

    #格式化图片URL
    def formatImageUrls(self,url):
        imgParase = urlparse(url)
        if not imgParase.netloc:
            imgpath = "%s://%s/%s" %(self.urlParse.scheme,self.urlParse.netloc,imgParase.path)
        else:
            imgpath = urljoin(self.url,url)
        return imgpath
    # 保存图片
    def downloadImage(self,url):
        print("download :" + url)
        arr = url.split('/')
        file_name = self.downloadPath +'/' + arr[-1]
        # file_name = self.downloadPath +'/' + arr[-2] +'/' + arr[-1]
        try:
            response = requests.get(url, headers=self.headers)
            with open(file_name, 'wb') as fp:
                for data in response.iter_content(128):
                    fp.write(data)
            self.start = self.start+1
            return file_name
        except:
            print("download error")

    def makeDir(self):
        if not os.path.exists(self.downloadPath):
            os.makedirs(self.downloadPath)

    def run(self):
        for img in self.Imageurls:
            self.downloadImage(self.formatImageUrls(img))

相关的头文件引用,大家可以参考python手册,这里不再细说。

新建一个单页的download_image_page.py文件。

import argparse
from DownloadImage import DownloadImage
def getArgv():
    parser = argparse.ArgumentParser()
    parser.add_argument('-i', '--uri', dest='Url', type=str, default='root', help='target Url')
    args= parser.parse_args()
    return args.Url

if __name__ == '__main__':
    url = getArgv()
    obj=DownloadImage(url,None)
    obj.run()

在控制台中运行:python3 download_image_page.py -i https://www.baidu.com

可以看到执行结果。

这里大家注意了,因为我最开始要做的是单页面采集,最开始设计的时候并未考虑图片的采集控制,这里算是一个优化点。

第一阶段结束,因为篇幅原因,整站部分的说明我将在下一篇中进行讲解说明。当然,代码已经上传,感兴趣的朋友可以先行clone。

代码地址:https://gitee.com/python_play/download_image

本文是“明哥陪你学Python”系列章节之一,如果你对Python有更多兴趣,或有问题,可以私信与明哥联系,我会陪你一起解决,其它相关章节可以从首页中的“明哥陪你学Python”列表进行查看。

本系列教程及源码地址:点击访问

最后:如果你正在学习Python的路上,或者准备打算学习Python、明哥会陪着你陪你一起共同进步!

手打不易,有用的话,请记得关注转发。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章