Python之多线程爬虫抓取网页图片的示例代码


Posted in Python onJanuary 10, 2018

目标

嗯,我们知道搜索或浏览网站时会有很多精美、漂亮的图片。

我们下载的时候,得鼠标一个个下载,而且还翻页。

那么,有没有一种方法,可以使用非人工方式自动识别并下载图片。美美哒。

那么请使用python语言,构建一个抓取和下载网页图片的爬虫。

当然为了提高效率,我们同时采用多线程并行方式。

思路分析

Python有很多的第三方库,可以帮助我们实现各种各样的功能。问题在于,我们弄清楚我们需要什么:

1)http请求库,根据网站地址可以获取网页源代码。甚至可以下载图片写入磁盘。

2)解析网页源代码,识别图片连接地址。比如正则表达式,或者简易的第三方库。

3)支持构建多线程或线程池。

4)如果可能,需要伪造成浏览器,或绕过网站校验。(嗯,网站有可能会防着爬虫 ;-))

5)如果可能,也需要自动创建目录,随机数、日期时间等相关内容。

如此,我们开始搞事情。O(∩_∩)O~

环境配置

操作系统:windows 或 linux 皆可

Python版本:Python3.6 ( not Python 2.x 哦)

第三方库

urllib.request

threading 或者 concurrent.futures 多线程或线程池(python3.2+)

re 正则表达式内置模块

os 操作系统内置模块

编码过程

我们分解一下过程。完整源代码在博文最终提供。

伪装为浏览器

import urllib.request

# ------ 伪装为浏览器 ---
def makeOpener(head={
  'Connection': 'Keep-Alive',
  'Accept': 'text/html, application/xhtml+xml, */*',
  'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
  'Connection': 'keep-alive',
  'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:57.0) Gecko/20100101 Firefox/57.0'
  }):
  cj = http.cookiejar.CookieJar()
  opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(cj))
  header = []
  for key, value in head.items():
    elem = (key, value)
    header.append(elem)
  opener.addheaders = header
  return opener

获取网页源代码

# ------ 获取网页源代码 ---
# url 网页链接地址
def getHtml(url):
  print('url='+url)
  oper = makeOpener()
  if oper is not None:
    page = oper.open(url)
    #print ('-----oper----')
  else:
    req=urllib.request.Request(url)
    # 爬虫伪装浏览器
    req.add_header('User-Agent','Mozilla/5.0 (Windows NT 6.1; WOW64; rv:57.0) Gecko/20100101 Firefox/57.0')
    page = urllib.request.urlopen(req)
  html = page.read()
  if collectHtmlEnabled: #是否采集html
    with open('html.txt', 'wb') as f:
      f.write(html) # 采集到本地文件,来分析
  # ------ 修改html对象内的字符编码为UTF-8 ------
  if chardetSupport:
    cdt = chardet.detect(html)
    charset = cdt['encoding'] #用chardet进行内容分析
  else:
    charset = 'utf8'
  try:
    result = html.decode(charset)
  except:
    result = html.decode('gbk')
  return result

下载单个图片

# ------ 根据图片url下载图片 ------
# folderPath 定义图片存放的目录 imgUrl 一个图片的链接地址 index 索引,表示第几个图片
def downloadImg(folderPath, imgUrl, index):
  # ------ 异常处理 ------
  try:
    imgContent = (urllib.request.urlopen(imgUrl)).read()
  except urllib.error.URLError as e:
    if printLogEnabled : print ('【错误】当前图片无法下载')
    return False
  except urllib.error.HTTPError as e:
    if printLogEnabled : print ('【错误】当前图片下载异常')
    return False
  else:
    imgeNameFromUrl = os.path.basename(imgUrl)
    if printLogEnabled : print ('正在下载第'+str(index+1)+'张图片,图片地址:'+str(imgUrl))
    # ------ IO处理 ------
    isExists=os.path.exists(folderPath)
    if not isExists: # 目录不存在,则创建
       os.makedirs( folderPath )
       #print ('创建目录')
    # 图片名命名规则,随机字符串
    imgName = imgeNameFromUrl
    if len(imgeNameFromUrl) < 8:
      imgName = random_str(4) + random_str(1,'123456789') + random_str(2,'0123456789')+"_" + imgeNameFromUrl
    filename= folderPath + "\\"+str(imgName)+".jpg"
    try:
       with open(filename, 'wb') as f:
         f.write(imgContent) # 写入本地磁盘
       # if printLogEnabled : print ('下载完成第'+str(index+1)+'张图片')
    except :
      return False
    return True

下载一批图片(多线程/线程池模式皆支持)

# ------ 批量下载图片 ------
# folderPath 定义图片存放的目录 imgList 多个图片的链接地址
def downloadImgList(folderPath, imgList):
  index = 0
  # print ('poolSupport='+str(poolSupport))
  if not poolSupport:
   #print ('多线程模式')
   # ------ 多线程编程 ------
   threads = []
   for imgUrl in imgList:
     # if printLogEnabled : print ('准备下载第'+str(index+1)+'张图片')
     threads.append(threading.Thread(target=downloadImg,args=(folderPath,imgUrl,index,)))
     index += 1
   for t in threads:
     t.setDaemon(True)
     t.start()
   t.join() #父线程,等待所有线程结束
   if len(imgList) >0 : print ('下载结束,存放图片目录:' + str(folderPath))
  else:
   #print ('线程池模式')
    # ------ 线程池编程 ------
   futures = []
   # 创建一个最大可容纳N个task的线程池 thePoolSize 为 全局变量
   with concurrent.futures.ThreadPoolExecutor(max_workers=thePoolSize) as pool: 
    for imgUrl in imgList:
     # if printLogEnabled : print ('准备下载第'+str(index+1)+'张图片')
     futures.append(pool.submit(downloadImg, folderPath, imgUrl, index))
     index += 1
    result = concurrent.futures.wait(futures, timeout=None, return_when='ALL_COMPLETED')
    suc = 0
    for f in result.done:
      if f.result(): suc +=1
    print('下载结束,总数:'+str(len(imgList))+',成功数:'+str(suc)+',存放图片目录:' + str(folderPath))

调用例子

如百度贴吧为例

# ------ 下载百度帖子内所有图片 ------
# folderPath 定义图片存放的目录 url 百度贴吧链接
def downloadImgFromBaidutieba(folderPath='tieba', url='https://tieba.baidu.com/p/5256331871'):
  html = getHtml(url)
  # ------ 利用正则表达式匹配网页内容找到图片地址 ------
  #reg = r'src="(.*?\.jpg)"'
  reg = r'src="(.*?/sign=.*?\.jpg)"'
  imgre = re.compile(reg);
  imgList = re.findall(imgre, html)
  print ('找到图片个数:' + str(len(imgList)))
  # 下载图片
  if len(imgList) >0 : downloadImgList(folderPath, imgList) 

# 程序入口
if __name__ == '__main__':
  now = datetime.datetime.now().strftime('%Y-%m-%d %H-%M-%S')
  # 下载百度帖子内所有图片
  downloadImgFromBaidutieba('tieba\\'+now, 'https://tieba.baidu.com/p/5256331871')

效果

Python之多线程爬虫抓取网页图片的示例代码

Python之多线程爬虫抓取网页图片的示例代码

Python之多线程爬虫抓取网页图片的示例代码

完整源码请见

我的github:https://github.com/SvenAugustus/PicDownloader-example

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python单链表的简单实现方法
Sep 23 Python
两个使用Python脚本操作文件的小示例分享
Aug 27 Python
Python爬虫爬取美剧网站的实现代码
Sep 03 Python
解决python2.7用pip安装包时出现错误的问题
Jan 23 Python
python 接口返回的json字符串实例
Mar 27 Python
PyQt5每天必学之切换按钮
Aug 20 Python
Sanic框架基于类的视图用法示例
Jul 18 Python
Python 在OpenCV里实现仿射变换—坐标变换效果
Aug 30 Python
在python3中实现查找数组中最接近与某值的元素操作
Feb 29 Python
浅谈Python 命令行参数argparse写入图片路径操作
Jul 12 Python
Python数据分析库pandas高级接口dt的使用详解
Dec 11 Python
Python基础之变量的相关知识总结
Jun 23 Python
Python设计模式之观察者模式简单示例
Jan 10 #Python
Python爬虫实例_城市公交网络站点数据的爬取方法
Jan 10 #Python
Python爬虫_城市公交、地铁站点和线路数据采集实例
Jan 10 #Python
Python tornado队列示例-一个并发web爬虫代码分享
Jan 09 #Python
Python中join函数简单代码示例
Jan 09 #Python
Python中顺序表的实现简单代码分享
Jan 09 #Python
python中set()函数简介及实例解析
Jan 09 #Python
You might like
遍历指定目录下的所有目录和文件的php代码
2011/11/27 PHP
解析php多线程下载远程多个文件
2013/06/25 PHP
php array_key_exists() 与 isset() 的区别
2016/10/24 PHP
Laravel下生成验证码的类
2017/11/15 PHP
laravel-admin 后台表格筛选设置默认的查询日期方法
2019/10/03 PHP
PHP Swoole异步Redis客户端实现方法示例
2019/10/24 PHP
jQuery 加上最后自己的验证
2009/11/04 Javascript
简单的前端js+ajax 购物车框架(入门篇)
2011/10/29 Javascript
Jquery EasyUI的添加,修改,删除,查询等基本操作介绍
2013/10/11 Javascript
javascript页面动态显示时间变化示例代码
2013/12/18 Javascript
8个超实用的jQuery功能代码分享
2015/01/08 Javascript
Js操作DOM元素及获取浏览器高宽的简单方法
2016/09/08 Javascript
js判断文件格式及大小的简单实例(必看)
2016/10/11 Javascript
AngularJS中如何使用echart插件示例详解
2016/10/26 Javascript
使用AngularJS 跨站请求如何解决jsonp请求问题
2017/01/16 Javascript
微信小程序实现拖拽 image 触摸事件监听的实例
2017/08/17 Javascript
vue项目中引入noVNC远程桌面的方法
2018/03/05 Javascript
Vue.js图片预览插件使用详解
2018/08/27 Javascript
Node+OCR实现图像文字识别功能
2018/11/26 Javascript
JS实现电话号码的字母组合算法示例
2019/02/26 Javascript
vue keep-alive列表页缓存 详情页返回上一页不刷新,定位到之前位置
2019/11/26 Javascript
JS中队列和双端队列实现及应用详解
2020/09/29 Javascript
haskell实现多线程服务器实例代码
2013/11/26 Python
使用python统计文件行数示例分享
2014/02/21 Python
Python中的面向对象编程详解(上)
2015/04/13 Python
Python3使用requests包抓取并保存网页源码的方法
2016/03/15 Python
Python查找两个有序列表中位数的方法【基于归并算法】
2018/04/20 Python
python web自制框架之接受url传递过来的参数实例
2018/12/17 Python
Python实现Event回调机制的方法
2019/02/13 Python
canvas画布实现手写签名效果的示例代码
2019/04/23 HTML / CSS
声明struct x1 { . . . }; 和typedef struct { . . . }x2;有什么不同
2012/06/02 面试题
《老王》教学反思
2014/02/23 职场文书
2015年财务个人工作总结范文
2015/05/22 职场文书
我对PyTorch dataloader里的shuffle=True的理解
2021/05/20 Python
用Python爬取英雄联盟的皮肤详细示例
2021/12/06 Python
vue实现移动端div拖动效果
2022/03/03 Vue.js