置顶 如何使用Python批量爬取网页的图片资源(建议收藏)
发布于 4 个月前 作者 JavaBird 527 次浏览 来自 分享

欢迎关注微信公众号:老夫撸代码 微信小程序写多了,老夫也换换口味,写点儿其它的教程 10分钟教程:如何使用Python批量爬取网页的图片资源 敲黑板,知识点: 正则表达式、网络请求、读写文件、SSL认证、操作文件夹、Base64解析图片

为什么出这个教程?

最近公司业务需要开发一些基于微信小程序的营销类工具,比如:砸金蛋、大转盘、九宫格抽奖等等,所以老夫参考了一些网赚平台的H5营销工具,使用Taro来开发小程序端的营销工具。 老夫先将此网址贴出来,链接如下: https://activity.tuiayonghu.com/activity/index?id=9150&slotId=193506&login=normal&appKey=uuNX4GQ6Xoi9mNG2yZcz3eYBRf1&deviceId=212a63c1-0fb1-4f9b-8fcf-70bfb1a6df94&dsm=1.193506.0.0&tenter=SOW&subActivityWay=2&tck_rid_6c8=0ad021cejrhq8cb9-6019810&tck_loc_c5d=tactivity-9150&dcm=401.193506.0.0&&tenter=SOW 手机浏览器效果如下图: WechatIMG100.jpeg 老夫只是想借用一下对方的图片资源,一般获取一个网页的图片资源有以下几种方式:

  1. 在当前页面右键-另存为,就会将当前页面的样式文件、图片资源、字体资源等下载到本地
  2. 通过F12,打开开发者工具 - Sources - 右键 - Open in new tab ,在新页面中另存为即可

上述1方法一般是大家用的方式,但是仅限于当前页面请求过的图片资源,如果是通过js加载的资源是获取不到的。 上述2方法不推荐,费时间。 对于1和2的方法都有各自的缺陷,老夫都不想用,人生苦短,何必在这些事情上浪费时间呢? 没有什么事情是通过写程序办不到,如果办不到,请多写几个程序! 以下涉及到的代码使用的是python3的语法

人生苦短,我用Python

这里我用大概138行的python来实现爬取图片资源并保存到本地的过程。 为什么不用其它语言来实现而用 python 来实现? 因为python入门低,而且各种类库用的那个爽。

第一步:通过url获取目标文件源码

我们通过python自带的标准库 urllib.request 来构造请求,并获取请求到的内容,并把这些内容写入到index.html文件中,示例代码如下:

'''
author:老夫撸代码
wechat:cxyzxh1388
获取目前url的源文件
'''
def getIndex():

    #目标url
    url = "https://activity.tuiayonghu.com/activity/index?id=9150&slotId=193506&login=normal&appKey=uuNX4GQ6Xoi9mNG2yZcz3eYBRf1&deviceId=212a63c1-0fb1-4f9b-8fcf-70bfb1a6df94&dsm=1.193506.0.0&tenter=SOW&subActivityWay=2&tck_rid_6c8=0ad021cejrhq8cb9-6019810&tck_loc_c5d=tactivity-9150&dcm=401.193506.0.0&&tenter=SOW"

    request = urllib.request.Request(url)

    #去掉ssl认证

    ssl._create_default_https_context = ssl._create_unverified_context

    with urllib.request.urlopen(request) as f:

        html = f.read()
        '''因为html读出来的是二进制,因此写入index.html也必须以二进制的形式'''
        with open('index.html','wb') as fb:
            fb.write(html)

    currpath = os.getcwd()

    if not os.path.exists(currpath+'/css'):
        os.mkdir(currpath + '/css')
    if not os.path.exists(currpath+'/images'):
        os.mkdir(currpath + '/images')

    return os.getcwd()+'/index.html'

代码解析:

  1. urllib读取url的时候,返回的是二进制格式的内容,因此保存到文件种也必须是以wb的方式写入
  2. 在保存index.html的同时,新建css文件夹和images文件夹

有一点儿需要注意的是: 如果url是以https开头的,我们需要将ssl认证关闭,代码如下:

import ssl
ssl._create_default_https_context = ssl._create_unverified_context

第二步:处理index.html源码中的图片资源

通过分析生成的index.html的文件结构,我们发现并没有任何一个显式声明的img标签存在于html的标签当中,更多的是将图片资源的链接拼接到js代码中:

"value":"//yun.tuitiger.com/mami-media/img/1vxv3dun0w.png"
"image":"//yun.tuiayonghu.com/mami-media/img/el1bddtppl.png"

先上代码:

'''
获取直接页面的图片资源
'''
def getIndexImage(path):
    soup = BeautifulSoup(open(path), 'lxml')
    '''获取页面中img标签'''
    imgs = soup.find_all('img')
    if len(imgs) > 0:
        #todo 保存Url图片,因为此链接的源码页面中并没有img标签,所以这里不做处理
        print()
    '''获取js变量中的img路径'''
    with open(path) as p:
        content = p.read()
        imglist = re.findall('("image":[^,]*,)',content)
        print(imglist)
        for img in imglist:
            arr = img.split(':')
            url = 'http:'+arr[1][1:-2]
            saveImage(url)
        valuelist =re.findall('("value":"//yun[^,]*,)',content)
        print(valuelist)
        for img in valuelist:
            arr = img.split(':')
            url = 'http:'+arr[1][1:-2]
            saveImage(url)

'''将url图片路径保存为图片'''
def saveImage(url):
    name = url[url.rindex('/')+1:]
    with urllib.request.urlopen(url) as i:
        content = i.read()
        with open(os.getcwd()+'/images/'+name,'wb') as f:
            f.write(content)
            print('保存图片:'+name+'\n')

代码解析:

  1. 上述代码主要用到一个类库 BeautifulSoup,它是一个可以从HTMLXML文件中提取数据的Python库.它能够通过你喜欢的转换器实现惯用的文档导航,查找,修改文档的方式.Beautiful Soup会帮你节省数小时甚至数天的工作时间.
  2. imgs = soup.find_all('img') 此方法就是在当前文档结构中查找所有显式声明的img标签
  3. 上述代码 12 ~ 25 行代码是通过正则表达式将所有拼接到js中的图片路径找到,并且保存到images的文件夹中。

第三步:保存index.html中的css文件

在前两步,我们已经将index.html中涉及到的图片资源都已保存下来,但是那些通过js加载的图片,还未处理。 我们先处理隐藏到css中的图片资源。 css中的图片资源分为两种:

  1. 图片资源链接
  2. base64格式的图片资源

上述 1 中的图片资源很好处理,我们通过正则表达式先将这些图片链接筛选处理,然后再通过模拟请求,保存到本地,代码如下:

#保存css文件
def dealCss(path):
    soup = BeautifulSoup(open(path), 'lxml')
    links = soup.find_all('link', attrs={'href': re.compile('.css')})
    '''
     保存css文件到文件夹中
    '''
    list = []
    for link in links:
        href = 'http:' + link['href']
        name = href[href.rindex('/') + 1:]
        request = urllib.request.Request(href)
        with urllib.request.urlopen(request) as u:
            css = u.read()
            with open(os.getcwd()+'/css/'+name,'wb') as f:
                f.write(css)
    '''
    通过正则表达式提取base64数据和图片路径
    '''
    dirss = os.listdir(os.getcwd()+'/css')
    for dir in dirss:
        with open(os.getcwd()+'/css/'+dir) as f:
            content = f.read()
            reglist = re.findall('(url\([^\)]*\);)',content)
            for i in reglist:
                real = i[i.index('(')+1:i.rindex(')')]
                if real.startswith("//"):
                    saveImage('http:'+real)
                else:
                    saveBase64(real)

代码解析:

  1. 3~16行代码是获取index.html中显式声明的css文件,并保存。
  2. 20~28行代码是通过对已保存的css文件,进行正则表达式找到图片的路径。因为在css中只能通过background-image:url(*)来加载图片,url中即可以加载图片又可以加载base64编码的图片数据,所以我们需要分开处理。
  3. '(url([^)]);)’* 这个正则表达式获取了所有加载的图片
  4. i[i.index(’(’)+1:i.rindex(’)’)] 是获取了url里面包括的数据
  5. real.startswith("//") 判断是否为图片链接

第四步:解码base64格式的图片并保存

cssbase64图片的格式为如下: background-image: url(“data:image/png;base64,iVBORw0KGgo=…”); 其中iVBORw0KGgo=…才是base64编码的数据,我们需要把这部分数据解码,然后保持到图片文件中。

'''将base64的编码转换为图片格式'''
def saveBase64(code):
    rc = re.search('data:image\/[a-z]+;',code).group()
    ext = '.'+rc[rc.rindex('/')+1:-1]
    name = ''.join(random.sample(['z','y','x','w','v','u','t','s','r','q','p','o','n','m','l','k','j','i','h','g','f','e','d','c','b','a'], 8))
    seq = re.search('data:image\/[a-z]+\;base64,',code).span()
    print(seq)
    newcode = code.replace(code,code[seq[1]:-1])
    print(newcode+'\n')
    with open(os.getcwd()+'/images/'+name+ext,'wb') as f:
        if len(newcode) % 4 != 0:  # check if multiple of 4
            while len(newcode) % 4 != 0:
                newcode = newcode + "="
            req_str = base64.b64decode(newcode)
        else:
            req_str = base64.b64decode(newcode)
        f.write(req_str)

代码解析:

  1. data:image/png表示当前的图片格式是png
  2. 4~5 行代码表示获取图片的扩展名和生成随机图片名称
  3. 6~8 行代码获取base64的数据格式
  4. 10~17 行代码将base64的数据格式解码并且保存到图片文件中。 …

关注微信公众号:老夫撸代码 回复数字 1009 获取完整代码

老夫撸代码


回到顶部