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 相关文章推荐
linux系统使用python监测网络接口获取网络的输入输出
Jan 15 Python
Python单链表的简单实现方法
Sep 23 Python
python中偏函数partial用法实例分析
Jul 08 Python
django解决跨域请求的问题
Nov 11 Python
python实现三维拟合的方法
Dec 29 Python
python twilio模块实现发送手机短信功能
Aug 02 Python
Python散点图与折线图绘制过程解析
Nov 30 Python
python paramiko远程服务器终端操作过程解析
Dec 14 Python
详解Python中的分支和循环结构
Feb 11 Python
Django后端分离 使用element-ui文件上传方式
Jul 12 Python
django跳转页面传参的实现
Sep 17 Python
python脚本框架webpy模板赋值实现
Nov 20 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
解析zend Framework如何自动加载类
2013/06/28 PHP
PHP关于IE下的iframe跨域导致session丢失问题解决方法
2013/10/10 PHP
laravel5.2实现区分前后台用户登录的方法
2017/01/11 PHP
laravel利用中间件做防非法登录和权限控制示例
2019/10/21 PHP
用js判断浏览器是否是IE的比较好的办法
2007/05/08 Javascript
JavaScript 对Cookie 操作的封装小结
2009/12/31 Javascript
JavaScript学习笔记(十七)js 优化
2010/02/04 Javascript
js 小贴士一星期合集
2010/04/07 Javascript
javascript 面向对象的JavaScript类
2010/05/04 Javascript
对javascript的一点点认识总结《javascript高级程序设计》读书笔记
2011/11/30 Javascript
jQuery不兼容input的change事件问题解决过程
2014/12/05 Javascript
jQuery语法小结(超实用)
2015/12/31 Javascript
Javascript OOP之面向对象
2016/07/31 Javascript
jquery实现放大镜简洁代码(推荐)
2017/06/08 jQuery
JavaScript多种页面刷新方法小结
2019/04/04 Javascript
layui加载表格,绑定新增,编辑删除,查看按钮事件的例子
2019/09/06 Javascript
微信小程序中weui用法解析
2019/10/21 Javascript
ant-design-vue 实现表格内部字段验证功能
2019/12/16 Javascript
详解在Vue.js编写更好的v-for循环的6种技巧
2020/04/14 Javascript
Vue父子之间值传递的实例教程
2020/07/02 Javascript
[06:07]辉夜杯现场观众互动 “比谁远送显示器”
2015/12/26 DOTA
Python编程实现两个文件夹里文件的对比功能示例【包含内容的对比】
2017/06/20 Python
python图像常规操作
2017/11/11 Python
Python无损音乐搜索引擎实现代码
2018/02/02 Python
详解python--模拟轮盘抽奖游戏
2019/04/12 Python
python格式化输出保留2位小数的实现方法
2019/07/02 Python
详解python程序中的多任务
2020/09/16 Python
基于Python中Remove函数的用法讨论
2020/12/11 Python
python多线程和多进程关系详解
2020/12/14 Python
Sperry官网:帆船鞋创始品牌
2016/09/07 全球购物
serialVersionUID具有什么样的特征
2014/02/20 面试题
优秀党员转正的自我评价
2013/10/06 职场文书
《一本男孩子必读的书》教学反思
2014/02/19 职场文书
基层党支部承诺书
2015/04/30 职场文书
经济纠纷起诉状
2015/05/20 职场文书
Pytorch GPU内存占用很高,但是利用率很低如何解决
2021/06/01 Python