Python爬虫(图片)编写过程中遇到的问题
2015-11-10 01:18
573 查看
最近我突然对网络爬虫开窍了,真正做起来的时候发现并不算太难,都怪我以前有点懒,不过近两年编写了一些程序,手感积累了一些肯定也是因素,总之,还是惭愧了。好了,说正题,我把这两天做爬虫的过程中遇到的问题总结一下:
需求:做一个爬虫,爬取一个网站上所有的图片(只爬大图,小图标就略过)
思路:1、获取网站入口,这个入口网页上有很多图片集合入口,进入这些图片集合就能看到图片链接了,所以爬取的深度为2,比较简单;2、各个子图片集合内所包含的图片链接有两种形式:一种是绝对图片路径(直接下载即可),另一种的相对图片路径(需要自行拼接当前网页路径)。总之这些子图集合的表现形式简单,没有考虑更复杂的情况。3、在爬取的过程中保存已成功爬取的路径,防止每次爬取都执行重复的任务,当然,当每次启动时要首先加载该历史数据库,剩下的就是细节了。
快速链接:
2.1 日志系统
[b]2.2 记录访问历史[/b]
[b][b]2.3 异常处理[/b][/b]
[b][b][b]2.4 网页自动编码判断[/b][/b][/b]
[b][b][b][b]2.5 远程主机重置链接(Errno 10054)[/b][/b][/b][/b]
一、全部代码
直接先来代码,再详细说优化的过程和内容吧:
查看myLogger.py
注意在爬虫的代码执行前调用一下该函数:
日志调用和打印的结果如下:
2.2 记录访问历史
这里记录URL的访问历史是为了防止执行重复任务,在内存中保持一个Set就能满足需求,在磁盘上可以简单的保存成一个TXT文件,每个URL保存成一行即可。所以这里是逻辑顺序应该是在初始化时创建一个Set用来保存访问历史,任务执行之前从数据库中加载访问历史,如果是首次运行尚未创建数据库还需要进行一次创建操作。然后就好办了,每次完成一个URL的访问就保存一下访问记录。相关代码如下:
有了上面的代码,在记录日志过程中就很方便了:
2.3 异常处理
访问网络资源不可避免的会有很多异常情况,要处理这些异常情况才能稳定运行,Python的异常处理很简单,请参考如下获取网页的代码段:
2.4 网页自动编码判断
昨天在爬取网页的过程中突然发现,有的页面居然说编码不能通过utf-8进行解析,我看了看有的网页确实不是utf-8编码的,那怎么办呢?怎么才能自动进行解码?从网上可以搜索到一个Python的开源库很好用,叫做chardet,默认Python2.7是不带的需要下载,比如我下载的是:chardet-2.3.0.tar.gz
有了这个压缩包解压出来cahrdet子文件夹拷贝到:C:\Python27\Lib\site-packages目录下即可。下面看看用法实例:
2.5 远程主机重置链接(Errno 10054)
在今天的爬取过程中我发现了一个问题,爬到后面的内容都出错了,错误信息参见如下:
可以看到都在报这个错误代码,网上一查,发现有很多同学已经遇到过了,请参考知乎上的讨论:http://www.zhihu.com/question/27248551
从网上讨论的结果来看,可以肯定的是直接原因在于“远程主机主动关闭了当前链接”,而根本原因则在于:网站启用了反爬虫的策略,也就是说我的爬虫爬的过快并且被发现不是真正的浏览器浏览了。怎么办了?针对这两个原因逐个处理,一是伪装成浏览器,二是不要访问的过快,三是每次访问完成都关闭链接。相关代码如下:
需求:做一个爬虫,爬取一个网站上所有的图片(只爬大图,小图标就略过)
思路:1、获取网站入口,这个入口网页上有很多图片集合入口,进入这些图片集合就能看到图片链接了,所以爬取的深度为2,比较简单;2、各个子图片集合内所包含的图片链接有两种形式:一种是绝对图片路径(直接下载即可),另一种的相对图片路径(需要自行拼接当前网页路径)。总之这些子图集合的表现形式简单,没有考虑更复杂的情况。3、在爬取的过程中保存已成功爬取的路径,防止每次爬取都执行重复的任务,当然,当每次启动时要首先加载该历史数据库,剩下的就是细节了。
快速链接:
2.1 日志系统
[b]2.2 记录访问历史[/b]
[b][b]2.3 异常处理[/b][/b]
[b][b][b]2.4 网页自动编码判断[/b][/b][/b]
[b][b][b][b]2.5 远程主机重置链接(Errno 10054)[/b][/b][/b][/b]
一、全部代码
直接先来代码,再详细说优化的过程和内容吧:
__author__ = 'KLH' # -*- coding:utf-8 -*- import logging import time def InitLogger(): logFileName = 'log_' + time.strftime("%Y%m%d%H%M%S", time.localtime(time.time())) + '.txt' logging.basicConfig(level=logging.DEBUG, format='[%(asctime)s][%(filename)s:%(lineno)d][%(levelname)s] - %(message)s', filename=logFileName, filemode='w') # 定义一个StreamHandler将INFO级别以上的信息打印到控制台 console = logging.StreamHandler() console.setLevel(logging.INFO) formatter = logging.Formatter('[%(asctime)s][%(filename)s:%(lineno)d][%(levelname)s] - %(message)s') console.setFormatter(formatter) logging.getLogger('').addHandler(console)
查看myLogger.py
注意在爬虫的代码执行前调用一下该函数:
from myLogger import * # 初始化系统日志存储 InitLogger()
日志调用和打印的结果如下:
logging.info(u"准备获取网页内容(超时设置:60秒):" + pageURL) #打印结果如下: [2015-11-09 22:35:02,976][spider2.py:182][INFO] - 准备获取网页内容(超时设置:60秒):http://365.XXXXX.com/xtu/
2.2 记录访问历史
这里记录URL的访问历史是为了防止执行重复任务,在内存中保持一个Set就能满足需求,在磁盘上可以简单的保存成一个TXT文件,每个URL保存成一行即可。所以这里是逻辑顺序应该是在初始化时创建一个Set用来保存访问历史,任务执行之前从数据库中加载访问历史,如果是首次运行尚未创建数据库还需要进行一次创建操作。然后就好办了,每次完成一个URL的访问就保存一下访问记录。相关代码如下:
# 类初始化 def __init__(self): self.contentFolder = u"抓取内容" self.dbName = "url.db" # 1、定义数据库文件名 self.createFolder(self.contentFolder) # 2、创建内容存储目录 self.urlDB = set() # 3、创建内存数据库 # 获取URL数据库以获取爬过的网页地址 def loadDatabase(self): isExists = os.path.exists(self.dbName) # 4、首先判断是否是首次运行,如果数据库文件不存在则创建一下 if not isExists: logging.info(u"创建URL数据库文件:'" + self.dbName + u"'") f = open(self.dbName, 'w+') f.write("#Create time: " + time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time())) + '\n') f.close() return db = open(self.dbName, 'r') # 5、从磁盘中加载数据库 for line in db.readlines(): if not line.startswith('#'): self.urlDB.add(line.strip('\n')) db.close() logging.info(u"URL数据库加载完成!") # 追加数据库文件 def writeToDatabase(self, url): # 6、在系统运行过程中,如需记录日志,追加日志内容即可 db = open(self.dbName, 'a') db.write(url + '\n') db.close()
有了上面的代码,在记录日志过程中就很方便了:
succeed = self.saveImgs(images, folderName) if succeed: self.urlDB.add(folderURL) self.writeToDatabase(folderURL)
2.3 异常处理
访问网络资源不可避免的会有很多异常情况,要处理这些异常情况才能稳定运行,Python的异常处理很简单,请参考如下获取网页的代码段:
# 获取索引页面的内容 def getPage(self, pageURL, second): try: request = urllib2.Request(pageURL) response = urllib2.urlopen(request, timeout = second) data = response.read() return data.decode('gbk'), True except urllib2.HTTPError,e: # HTTPError必须排在URLError的前面 logging.error("HTTPError code:" + str(e.code) + " - Content:" + e.read()) return "", False except urllib2.URLError, e: logging.error("URLError reason:" + str(e.reason) + " - " + str(e)) return "", False except Exception, e: # 其他所有类型的异常 logging.error(u"获取网页失败:" + str(e)) return "", False # 这里返回两个值方便判断
2.4 网页自动编码判断
昨天在爬取网页的过程中突然发现,有的页面居然说编码不能通过utf-8进行解析,我看了看有的网页确实不是utf-8编码的,那怎么办呢?怎么才能自动进行解码?从网上可以搜索到一个Python的开源库很好用,叫做chardet,默认Python2.7是不带的需要下载,比如我下载的是:chardet-2.3.0.tar.gz
有了这个压缩包解压出来cahrdet子文件夹拷贝到:C:\Python27\Lib\site-packages目录下即可。下面看看用法实例:
import chardet # 获取页面所有图片 def getAllImgURL(self, infoURL): images = [] succeed = True try: data = urllib2.urlopen(infoURL).read() # 1、先获取网页内容 chardet1 = chardet.detect(data) # 2、再调用该模块的方法自动判断网页编码 page = data.decode(str(chardet1['encoding'])) # 3、注意,得到的chardet1是一个字典类似于:{'confidence': 0.98999999999999999, 'encoding': 'GB2312'} # 第一种解码格式: pattern = re.compile('<option value="(.*?)">(.*?)</option>') items = re.findall(pattern, page) # item[0]为图片URL尾部,item[1]为图片名称 for item in items: imageURL = infoURL + item[0] if imageURL in self.urlDB: logging.info(u"获得图片URL(曾被访问,跳过):" + imageURL) else: logging.info(u"获得图片URL:" + imageURL) images.append(imageURL) # 第二种解码格式 pattern = re.compile('<IMG src="(.*?)".*?>') items = re.findall(pattern, page) # item为图片URL for item in items: if item.startswith('http://'): # 4、这里也注意一下,在这种网页中相对路径的图片都是插图之类的小图片不需要下载,所以过滤掉了。 if item in self.urlDB: logging.info(u"获得图片URL(曾被访问,跳过):" + item) else: logging.info(u"获得图片URL:" + item) images.append(item) except Exception, e: logging.warning(u"在获取子路径图片列表时出现异常:" + str(e)) succeed = False return images, succeed
2.5 远程主机重置链接(Errno 10054)
在今天的爬取过程中我发现了一个问题,爬到后面的内容都出错了,错误信息参见如下:
[2015-11-09 23:48:51,082][spider2.py:130][INFO] - 开始准备保存图片(超时设置:300秒):http://xz1.XXXX.com/st/st-06/images/009.jpg [2015-11-09 23:48:55,095][spider2.py:160][ERROR] - 保存图片失败:[Errno 10054] [2015-11-09 23:48:55,096][spider2.py:140][WARNING] - 保存图片失败(耗时:4.01399993896秒):http://xz1.XXXX.com/st/st-06/images/009.jpg [2015-11-09 23:48:55,098][spider2.py:130][INFO] - 开始准备保存图片(超时设置:300秒):http://xz1.XXXX.com/st/st-06/images/010.jpg [2015-11-09 23:48:56,576][spider2.py:160][ERROR] - 保存图片失败:[Errno 10054] [2015-11-09 23:48:56,578][spider2.py:140][WARNING] - 保存图片失败(耗时:1.48000001907秒):http://xz1.XXXX.com/st/st-06/images/010.jpg
可以看到都在报这个错误代码,网上一查,发现有很多同学已经遇到过了,请参考知乎上的讨论:http://www.zhihu.com/question/27248551
从网上讨论的结果来看,可以肯定的是直接原因在于“远程主机主动关闭了当前链接”,而根本原因则在于:网站启用了反爬虫的策略,也就是说我的爬虫爬的过快并且被发现不是真正的浏览器浏览了。怎么办了?针对这两个原因逐个处理,一是伪装成浏览器,二是不要访问的过快,三是每次访问完成都关闭链接。相关代码如下:
# 传入图片地址,文件名,超时时间,保存单张图片 def saveImg(self, imageURL, fileName, second): try: headers = {'User-agent' : 'Mozilla/5.0 (Windows NT 6.2; WOW64; rv:22.0) Gecko/20100101 Firefox/22.0'} # 1、这里构造一个浏览器的头部 request = urllib2.Request(imageURL, headers = headers) u = urllib2.urlopen(request, timeout = second) # 2、这里的超时设置timeout不要太长 data = u.read() f = open(fileName, 'wb') f.write(data) f.close() u.close() # 3、注意这里的链接要主动调用一下关闭 return True except urllib2.HTTPError,e: #HTTPError必须排在URLError的前面 logging.error("HTTPError code:" + str(e.code) + " - Content:" + e.read()) return False except urllib2.URLError, e: logging.error("URLError reason:" + str(e.reason) + " - " + str(e)) return False except Exception, e: logging.error(u"保存图片失败:" + str(e)) return False # 4、注意调用完这个函数之后再time.sleep(1)一下,防止过快访问被发现了,呵呵
相关文章推荐
- python控制台中实现进度条功能
- Python 文件管理实例详解
- Python中对元组和列表按条件进行排序的方法示例
- Python复制文件操作实例详解
- python 操作haproxy配置文件
- python机器学习《回归 一》
- python yield
- python2.7 matplotlib安装
- Python3安装BeautifulSoup4模块
- leetcode Reverse Integer python
- ubuntu14.04 安装 sklearn
- python学习笔记-day03 第四部分(函数和文件操作)
- Python处理json字符串转化为字典
- python 中文字符问题
- python机器学习《回归 一》
- Python标准库 多线程与同步 (threading包)
- python爬取网页信息
- 【好文收藏】理解python多线程
- Python核心编程读笔 4
- 笔记一:Python的PyDev插件在eclipse上面安装(新的插件地址 location)