使用Python批量下载网站图片

    在网上冲浪的时候,总有些“小浪花”令人喜悦。没错,小浪花就是美图啦。边浏览边下载,自然是不错的;不过,好花不常开,好景不常在,想要便捷地保存下来,一个个地另存为还是很麻烦的。能不能批量下载呢? 只要获得图片地址,还是不难的。

目标 

太平洋摄影网, 一个不错的摄影网站。 如果你喜欢自然风光的话,不妨在上面好好饱览一顿吧。饱览一顿,或许你还想打包带走呢。这并不是难事,让我们顺藤摸瓜地来尝试一番吧(懒得截图,自己打开网站观赏吧)。

首先,我们打开网址 http://dp.pconline.com.cn/list/all_t145.html ; 那么,马上有N多美妙的缩略图呈现在你面前。任意点击其中一个链接,就到了一个系列的第一张图片的页面: http://dp.pconline.com.cn/photo/3687487.html, 再点击下可以到第二张图片的页面: http://dp.pconline.com.cn/photo/3687487_2.html, 图片下方点击“查看原图”, 会跳转到 http://dp.pconline.com.cn/public/photo/source_photo.jsp?id=19706865&photoId=3687487 这个页面,呈现出一张美美的高清图。右键另存为,就可以保存到本地。也许你的心已经开始痒痒啦。

该如何下手呢? 只要你做过 web 开发,一定知道,在浏览器的控制台,会有页面的 html , 而 html 里会包含图片, 或者是包含图片的另一个 HTML。对于上面的情况而言, http://dp.pconline.com.cn/list/all_t145.html 是一个大主题系列的入口页面,比如自然是 t145, 建筑是 t292, 记作 EntryHtml ;这个入口页面包含很多链接指向子的HTML,这些子 HTML 是这个大主题下的不同个性风格的摄影师拍摄的不同系列的美图, 记作 SerialHtml ; 而这些 SerialHtml 又会包含一个子系列每一张图片的首 HTML,记作 picHtml , 这个 picHtml 包含一个“查看原图”链接,指向图片高清地址的链接 http://dp.pconline.com.cn/public/photo/source_photo.jsp?id=19706865&photoId=3687487 , 记作 picOriginLink ; 最后, 在 picOriginLink 里找到  img 元素,即高清图片的真真实地址 picOrigin。 (⊙v⊙)嗯,貌似有点绕晕了,我们来总结一下:

EntryHtml (主题入口页面) -> SerialHtml (子系列入口页面) -> picHtml (子系列图片浏览页面) -> picOriginLink (高清图片页面) -> picOrigin (高清图片的真实地址)

现在,我们要弄清楚这五级是怎么关联的。

经过查看 HTML 元素,可知:

(1)  SerialHtml 元素是 EntryHtml  页面里的 class="picLink" 的 a 元素;

(2)  picHtml 元素是 SerialHtml 的加序号的结果,比如  SerialHtml 是 http://dp.pconline.com.cn/photo/3687487.html, 总共有 8 张,那么 picHtml = http://dp.pconline.com.cn/photo/3687487_[1-8].html ,注意到 http://dp.pconline.com.cn/photo/3687487.html 与 http://dp.pconline.com.cn/photo/3687487_1.html 是等效的,这会给编程带来方便。

(3) “查看原图” 是指向高清图片地址的页面 xxx.jsp 的链接:它是 picHtml 页面里的 class="aView aViewHD" 的 a 元素;

(4)  最后,从 xxx.jsp 元素中找出 src 为图片后缀的 img 元素即可。

那么,我们的总体思路就是:

STEP1: 抓取  EntryHtml  的网页内容 entryContent ;

STEP2: 解析 entryContent ,找到class="picLink" 的 a 元素列表 SerialHtmlList ;

STEP3: 对于SerialHtmlList 的每一个网页 SerialHtml_i:

(1)  抓取其第一张图片的网页内容, 解析出其图片总数 total ;

(2)  根据图片总数 total 并生成 total 个图片链接 picHtmlList ;

a.  对于 picHtmlList 的每一个网页,找到 class="aView aViewHD" 的 a 元素 hdLink ;

b.  抓取 hdLink 对应的网页内容,找到img元素 获得最终的 图片 真实地址 picOrigin ;

c.  下载 picOrigin 。

注意到, 一个主题系列有多页,比如首页是 EntryHtml :http://dp.pconline.com.cn/list/all_t145.html , 第二页是 http://dp.pconline.com.cn/list/all_t145_p2.html ;首页等效于 http://dp.pconline.com.cn/list/all_t145_p1.html 这会给编程带来方便。要下载一个主题下多页的系列图片,只要在最外层再加一层循环。这就是串行版本的实现流程。

  串行版本实现:  

#!/usr/bin/python
#_*_encoding:utf-8_*_

import os
import re
import sys
import requestsfrom bs4 import BeautifulSoup

saveDir = os.environ[‘HOME‘] + ‘/joy/pic/pconline/nature‘

def catchExc(func):
    def _deco(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except Exception as e:
            print "error catch exception for %s (%s, %s)." % (func.__name__, str(*args), str(**kwargs))
            print e
            return None
    return _deco

@catchExc
def getSoup(url):
    ‘‘‘
       get the html content of url and transform into soup object
           in order to parse what i want later
    ‘‘‘
    result = requests.get(url)
    status = result.status_code
    if status != 200:
        return None
    resp = result.text
    soup = BeautifulSoup(resp, "lxml")
    return soup

@catchExc
def parseTotal(soup):
    ‘‘‘
      parse total number of pics in html tag <span class="totPic"> (1/total)</span>
    ‘‘‘
    totalNode = soup.find(‘span‘, class_=‘totPics‘)
    total = int(totalNode.text.split(‘/‘)[1].replace(‘)‘,‘‘))
    return total

@catchExc
def buildSubUrl(href, ind):
    ‘‘‘
    if href is http://dp.pconline.com.cn/photo/3687736.html, total is 10
    then suburl is
        http://dp.pconline.com.cn/photo/3687736_[1-10].html
    which contain the origin href of picture
    ‘‘‘
    return href.rsplit(‘.‘, 1)[0] + "_" + str(ind) + ‘.html‘ 

@catchExc
def download(piclink):
    ‘‘‘
       download pic from pic href such as
            http://img.pconline.com.cn/images/upload/upc/tx/photoblog/1610/21/c9/28691979_1477032141707.jpg
    ‘‘‘

    picsrc = piclink.attrs[‘src‘]
    picname = picsrc.rsplit(‘/‘,1)[1]
    saveFile = saveDir + ‘/‘ + picname

    picr = requests.get(piclink.attrs[‘src‘], stream=True)
    with open(saveFile, ‘wb‘) as f:
        for chunk in picr.iter_content(chunk_size=1024):
            if chunk:
                f.write(chunk)
                f.flush()
    f.close()

@catchExc
def downloadForASerial(serialHref):
    ‘‘‘
       download a serial of pics
    ‘‘‘

    href = serialHref
    subsoup = getSoup(href)
    total = parseTotal(subsoup)
    print ‘href: %s *** total: %s‘ % (href, total)

    for ind in range(1, total+1):
        suburl = buildSubUrl(href, ind)
        print "suburl: ", suburl
        subsoup = getSoup(suburl)

        hdlink = subsoup.find(‘a‘, class_=‘aView aViewHD‘)
        picurl = hdlink.attrs[‘href‘]

        picsoup = getSoup(picurl)
        piclink = picsoup.find(‘img‘, src=re.compile(".jpg"))
        download(piclink)

@catchExc
def downloadAllForAPage(entryurl):
    ‘‘‘
       download serial pics in a page
    ‘‘‘

    soup = getSoup(entryurl)
    if soup is None:
        return
    #print soup.prettify()
    picLinks = soup.find_all(‘a‘, class_=‘picLink‘)
    if len(picLinks) == 0:
        return
    hrefs = map(lambda link: link.attrs[‘href‘], picLinks)
    print ‘serials in a page: ‘, len(hrefs)

    for serialHref in hrefs:
        downloadForASerial(serialHref)

def downloadEntryUrl(serial_num, index):
    entryUrl = ‘http://dp.pconline.com.cn/list/all_t%d_p%d.html‘ % (serial_num, index)
    print "entryUrl: ", entryUrl
    downloadAllForAPage(entryUrl)
    return 0

def downloadAll(serial_num):
    start = 1
    end = 2
    return [downloadEntryUrl(serial_num, index) for index in range(start, end+1)] 

serial_num = 145

if __name__ == ‘__main__‘:
    downloadAll(serial_num)

很显然,串行版本会比较慢,CPU 长时间等待网络连接和操作。要提高性能,通常是采用如下措施:

(1)   使用多线程将 io 密集型操作隔离开,避免CPU等待;

(2)   单个循环操作改为批量操作,更好地利用并发;

(3)   使用多进程进行 CPU 密集型操作,更充分利用多核的力量。

批量并发版本(线程池貌似有点多,有点不稳定,后优化):

#!/usr/bin/python
#_*_encoding:utf-8_*_

import os
import re
import sys
from multiprocessing.dummy import Pool as ThreadPool

import requests
from bs4 import BeautifulSoup

saveDir = os.environ[‘HOME‘] + ‘/joy/pic/pconline‘
dwpicPool = ThreadPool(20)

def catchExc(func):
    def _deco(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except Exception as e:
            print "error catch exception for %s (%s, %s): %s" % (func.__name__, str(*args), str(**kwargs), e)
            return None
    return _deco

@catchExc
def batchGetSoups(urls):
    ‘‘‘
       get the html content of url and transform into soup object
           in order to parse what i want later
    ‘‘‘

    urlnum = len(urls)
    if urlnum == 0:
        return []

    getUrlPool = ThreadPool(urlnum)
    results = []
    for i in range(urlnum):
        results.append(getUrlPool.apply_async(requests.get, (urls[i], )))
    getUrlPool.close()
    getUrlPool.join()

    soups = []
    for res in results:
        r = res.get(timeout=1)
        status = r.status_code

        if status != 200:
            continue
        resp = r.text
        soup = BeautifulSoup(resp, "lxml")
        soups.append(soup)
    return soups

@catchExc
def parseTotal(soup):
    ‘‘‘
      parse total number of pics in html tag <span class="totPic"> (1/total)</span>
    ‘‘‘
    totalNode = soup.find(‘span‘, class_=‘totPics‘)
    total = int(totalNode.text.split(‘/‘)[1].replace(‘)‘,‘‘))
    return total

@catchExc
def buildSubUrl(href, ind):
    ‘‘‘
    if href is http://dp.pconline.com.cn/photo/3687736.html, total is 10
    then suburl is
        http://dp.pconline.com.cn/photo/3687736_[1-10].html
    which contain the origin href of picture
    ‘‘‘
    return href.rsplit(‘.‘, 1)[0] + "_" + str(ind) + ‘.html‘ 

@catchExc
def downloadPic(piclink):
    ‘‘‘
       download pic from pic href such as
            http://img.pconline.com.cn/images/upload/upc/tx/photoblog/1610/21/c9/28691979_1477032141707.jpg
    ‘‘‘

    picsrc = piclink.attrs[‘src‘]
    picname = picsrc.rsplit(‘/‘,1)[1]
    saveFile = saveDir + ‘/‘ + picname

    picr = requests.get(piclink.attrs[‘src‘], stream=True)
    with open(saveFile, ‘wb‘) as f:
        for chunk in picr.iter_content(chunk_size=1024):
            if chunk:
                f.write(chunk)
                f.flush()
    f.close()

@catchExc
def getOriginPicLink(subsoup):
    hdlink = subsoup.find(‘a‘, class_=‘aView aViewHD‘)
    return hdlink.attrs[‘href‘]

@catchExc
def downloadForASerial(serialHref):
    ‘‘‘
       download a serial of pics
    ‘‘‘

    href = serialHref
    subsoups = batchGetSoups([href])
    total = parseTotal(subsoups[0])
    print ‘href: %s *** total: %s‘ % (href, total)

    suburls = [buildSubUrl(href, ind) for ind in range(1, total+1)]
    subsoups = batchGetSoups(suburls)
    picUrls = map(getOriginPicLink, subsoups)
    picSoups = batchGetSoups(picUrls)
    piclinks = map(lambda picsoup: picsoup.find(‘img‘, src=re.compile(".jpg")), picSoups)
    dwpicPool.map_async(downloadPic, piclinks) 

def downloadAllForAPage(entryurl):
    ‘‘‘
       download serial pics in a page
    ‘‘‘

    soups = batchGetSoups([entryurl])
    if len(soups) == 0:
        return

    soup = soups[0]
    #print soup.prettify()
    picLinks = soup.find_all(‘a‘, class_=‘picLink‘)
    if len(picLinks) == 0:
        return
    hrefs = map(lambda link: link.attrs[‘href‘], picLinks)

    for serialHref in hrefs:
        downloadForASerial(serialHref)

def downloadAll(serial_num, start, end):
    entryUrl = ‘http://dp.pconline.com.cn/list/all_t%d_p%d.html‘
    entryUrls = [ (entryUrl % (serial_num, ind)) for ind in range(start, end+1)]
    taskpool = ThreadPool(20)
    taskpool.map_async(downloadAllForAPage, entryUrls)
    taskpool.close()
    taskpool.join()

if __name__ == ‘__main__‘:
    serial_num = 145
    downloadAll(serial_num, 1, 2)
时间: 2024-10-13 23:49:34

使用Python批量下载网站图片的相关文章

C#获取网页的HTML码、下载网站图片、获取IP地址

1.根据URL请求获取页面HTML代码 /// <summary> /// 获取网页的HTML码 /// </summary> /// <param name="url">链接地址</param> /// <param name="encoding">编码类型</param> /// <returns></returns> public static string Get

用python批量下载wallheaven网站图片

这要从很早以前说起,那个时候是刚开始玩Ubuntu,但是ubuntu的壁纸不太好看,所以我就想方设法找到很漂亮的壁纸来替换原先的,但是我又想让壁纸像幻灯片一样播放,但是ubuntu不像windows,没有这样的功能,所以只能上网找办法来解决,最后终于在ubuntu论坛看到了variety这个东东,这个东西用起来确实很酷,可以自己编辑图片源,但是它本身默认带了几个源,而恰好其中就有wallheaven(那个时候还是wallpaper,之后网站改了叫做wallheaven),所以我就到wallhea

用python批量下载图片

一 写爬虫注意事项 网络上有不少有用的资源, 如果需要合理的用爬虫去爬取资源是合法的,但是注意不要越界,前一阶段有个公司因为一个程序员写了个爬虫,导致公司200多个人被抓,所以先进入正题之前了解下什么样的爬虫是违法的: 如果爬虫程序采集到公民的姓名.身份证件号码.通信通讯联系方式.住址.账号密码.财产状况.行踪轨迹等个人信息,并将之用于非法途径的,则肯定构成非法获取公民个人信息的违法行为.除此之外,根据相关规定,对于违反国家有关规定,向他人出售或者提供公民个人信息,情节严重的,窃取或者以其他方法

用python批量下载贴吧图片 附源代码

环境:windows 7 64位:python2.7:IDE pycharm2016.1 功能: 批量下载百度贴吧某吧某页的所有帖子中的所有图片 使用方法: 1.安装python2.7,安装re模块,安装urllib2模块 2.复制以下源代码保存为tbImgiDownloader.py文件 3.打开某个贴吧并复制其网址 4.打开文件tbImgiDownloader.py在第37行的单引号中输入网址,保存  5.双击tbImgiDownloader.py 说明: 1.本程序每次可以下载大概50个贴

Python批量下载百度贴吧贴子图片

批量下载贴吧里面某个贴子的所有图片,或者只下载某一页中的图片. #!/usr/bin/env python3 import re import urllib.request class DownTiebaImg: def __init__(self, url): self.url = url def getImgLinks(self): response = urllib.request.urlopen(self.url) pattern = re.compile(r'<img class=&quo

python批量下载图片的三种方法

一是用微软提供的扩展库win32com来操作IE: win32com可以获得类似js里面的document对象,但貌似是只读的(文档都没找到). 二是用selenium的webdriver: selenium则提供了Chrome,IE,FireFox等的支持,每种浏览器都有execute_script和find_element_by_xx方法,可以方便的执行js脚本(包括修改元素)和读取html里面的元素.不足是selenium只提供对python2.6和2.7的支持. 三是用python自带的

python爬虫,一段完整的python爬虫批量下载网站图片资源的代码

# 本程序为爬虫学习代码,成功爬取了漫微网站上的全部图片内容 import re import os import requests def getHTMLText(url): try: r=requests.get(url) r.raise_for_status() r.encoding=r.apparent_encoding return r.text except: print("request failed") url = 'http://marvel.mtime.com/'

自从会了Python在群里斗图就没输过,Python批量下载表情包!

导语 最近图慌,于是随便写了个表情包批量下载的脚本,没什么技术含量,纯娱乐性质. 让我们愉快地开始吧~ 开发工具 Python版本:3.6.4 相关模块: requests模块: fake_useragent模块: 以及一些Python自带的模块. 环境搭建 安装Python并添加到环境变量,pip安装需要的相关模块即可. 原理简介 爬的站长之家的表情包,链接: http://sc.chinaz.com/biaoqing/index.html 非常好爬,思路也很简单: ① 获得表情包所在地址:

python 批量下载美剧 from 人人影视 HR-HDTV

本人比较喜欢看美剧,尤其喜欢人人影视上HR-HDTV 的 1024 分辨率的高清双字美剧,这里写了一个脚本来批量获得指定美剧的所有 HR-HDTV 的 ed2k下载链接,并按照先后顺序写入到文本文件,供下载工具进行批量下载.源代码如下: # python3 实现,下面的实例 3 部美剧爬完大概要 20 s import urllib.request import re def get_links(url, name='yyets'): data = urllib.request.urlopen(