单网页多图下载,难度中等

一、实验说明

1.1. 环境登录

无需密码自动登录,系统用户名shiyanlou

1.2. 环境介绍

本实验环境采用带桌面的Ubuntu Linux环境,实验中会用到桌面上的程序:

  1. Xfce终端: Linux命令行终端,打开后会进入shell环境,可以使用Linux命令
  2. Firefox:浏览器,可以用在需要前端界面的课程里,只需要打开环境里写的HTML/JS页面即可
  3. GVim:非常好用的编辑器,最简单的用法可以参考课程Vim编辑器

1.3. 环境使用

使用GVim编辑器输入实验所需的代码及文件,使用Xfce终端运行所需命令进行操作。

实验报告可以在个人主页中查看,其中含有每次实验的截图及笔记,以及每次实验的有效学习时间(指的是在实验桌面内操作的时间,如果没有操作,系统会记录为发呆时间)。这些都是您学习的真实性证明。

实验楼虚拟机,自带了python2.X和python3.X版本,无需安装,本实验基于 python2。

二、课程介绍

这一节呢,利用上一节提到的知识点,进行单网页的图片爬取和下载至本地

三、功能详解

  • 从网页的html中,获取我们需要的图片链接,打开网页,这个页面下,一共有10张图片是我们需要爬取的,查看网页源码,找到这十张图片的代码,都是的格式,但是除了这几张,该网页里面其他的图片也是用img标签,所有只用img匹配,是不太好控制正确率的,另外我在ubuntu下用spy
  • der查阅这个链接html时中文全为乱码,我也就放弃了中文匹配,我用的笨方法就是先找父标签,依次往里直至寻找到img标签,具体路径如下

    body标签 -> wrapper -> container -> pagecontent -> maincontent -> postContent -> picture -> p (查阅HTML源码,我只用了后面三个)

匹配用到的正则表达式(我用id和class匹配):
'<div.*?class="postContent.*?>.*?<p>(.*?)</p>' 获取图片链接那段html代码
'<div.*?id="picture.*?>.*?<p>(.*?)</p>' 查阅上级获得的html中,picture那块代码
'<img.*?src="(.*?)".*?>' 从上一个表达式中,获取图片链接
  • 从链接中获得图片名,也是正则表达式获取
'.*/(.*?.jpg)' 从图片链接中,提取图片名,这个是最简单的
  • 打开图片链接,在本地创建文件,保存,这里呢,有个小功能,保证以后更新不重复下载
判断文件时候存在,如果存在,不下载,节约时间
如果文件不存在,下一步创建新文件,读写模式打开
打开图片链接,将缓存写入到文件中
关闭图片文件,完成

以上呢就是本节内容要实现的全部功能,接下来贴上代码

四、功能代码

这里呢,按功能的先后顺序,逆序贴代码,不过代码有点长,我会在'def'后面附上注释

#文件名:meizi_page_download
import urllib2
import os
import re
#loadurl()这个函数呢,是防打开链接超时,如果超时返回空字符,则主调函数会再次调用(while语句就可以实现),正常的话返回html代码,一个网页不算大,如果你的网络超级好,timeout可以缩短
def loadurl(url):
    try:
        conn = urllib2.urlopen(url,timeout=5)
        html = conn.read()
        return html
    except urllib2.URLError:
        return ''
    except Exception:
        print("unkown exception in conn.read()")
        return ''
#这里是图片保存的代码被调函数,timeout=5设置超时时间,一个500k不到的图片,5秒时间算长的了,超时的话,返回失败

def download(url,filename):
    try:
        conn = urllib2.urlopen(url,timeout=5)
        f = open(filename,'wb')
        f.write(conn.read())
        f.close()
        return True
    except urllib2.URLError:
        print 'load',url,'error'
        return False
    except Exception:
        print("unkown exception in conn.read()")
        return ''

#保存图片的逻辑代码块
def save_pic(url,path):
    searchname = '.*/(.*?.jpg)'
    name = re.findall(searchname,url)
    filename = path +'/'+ name[0]

    print filename + ':start' #控制台显示信息
    #下面的代码,当下载成功,break跳出就好了,如果存在,直接结束这个函数

    #定义了在下载图片时遇到错误的重试次数
    tryTimes = 3

    #当重试次数没有用完时,则尝试下载
    while tryTimes != 0:
        tryTimes -= 1
        if os.path.exists(filename):
            print filename,' exists, skip'
            return True
        elif os.path.exists(filename):
            os.mknod(filename)
        if download(url,filename):
            break

    if tryTimes != 0:
        print(filename + ": over")
    else:
        print(url + " :Failed to download")
    #控制台显示信息

#这个函数,相当于一个中介,我只是把for循环代码提出就得到了这个函数    
def pic_list(picList,path):
    picurl = ''
    for picurl in picList:
        save_pic(picurl,path)

#图片下载的主逻辑函数,获取图片链接,然后传给pic_list(),等结果(其实也没结果,就是等退出)
def picurl(url,path):
    if os.path.exists(path):
        print path, 'exist'
    else:
        os.makedirs(path)
    html = ''
    while True:#这里和下载图片是一个道理,细看即可
        html = loadurl(url)
        if html == '':
            print 'load', url,'error'
            continue
        else:
            break
    #其实这里呢,也是后期发现的一个小bug,这个网站的前后代码有不同(目前而言发现的一处),在rePicContent1运行到后面,是匹配不到的,导致rePicList返回的结果也是空,也就造成了这个符号[0]报错,因为没有任何值,越界错误,单线程会在这里报错并停止运行。rePicContent2其实是我解决bug的另一个匹配正则式,被我发现的页面是这个--http://www.meizitu.com/a/454.html,有兴趣的去对比看看
    rePicContent1 = '<div.*?id="picture.*?>.*?<p>(.*?)</p>'
    rePicContent2 = '<div.*?class="postContent.*?>.*?<p>(.*?)</p>'
    rePicList = '<img.*?src="(.*?)".*?>'
    #这里对re.S做个介绍,re.S是可以不添加的,加上之后,它的作用就是能忽略换行符,将两条作为一条来匹配。html代码碰上换行的概率是很高的,所以我一致采用re.S(下文有配图)
    picContent = re.findall(rePicContent1, html,re.S)
    if len(picContent) <=0:
        picContent = re.findall(rePicContent2, html,re.S)
    if len(picContent) <=0:
        print 'load false, over download this page and return'
        return False
    else:
        picList = re.findall(rePicList,picContent[0],re.S)
        pic_list(picList,path)
#url = 'http://www.meizitu.com/a/454.html'这两行是我函数测试所用
#picurl(url,'/home/shiyanlou/Desktop/demo454')     

声明,在运行代码的时候,保险起见别插入中文,特别是linux环境,容易报错,另外在这里附上re.S的截图(没有3吧)res 代码到这里就结束了,底层的图片获取链接并保存,完成,附上实验楼的截图一张(一开始报错,第二次就好了,运行之前,请确保你有外网权限,用下Firefox看下网站就行,如果没用,请本机测试)shiyanlou

请继续下一个实验

版权声明:允许转载,转载请注明出处 —— 《基于Python的网络爬虫》: 单网页多图下载,难度中等

Copyright @2016-2021 | 赣ICP备16003025号-1 | 公安备案号:36062202000048 |