Python编写蜘蛛/爬虫

我目前住在女友家的花场(种花木的花木基地,空气清新并夹杂着混合了泥土味的阵阵猪屎气息,绝对的有益身心),只是美中不足的地方是,由于地方较为偏僻没办法上网。虽然可以通过无线3G上网,但是上网价格明显和工资水平不是一个级别,暂时不考虑。
再说,在花场大部分时间我都不需要用到网络,因为我会把需要用到网络的工作留在公司做,而不需要用到网络的工作才带回花场(比如:设计类、动画类、制作类等)。但有时设计也需要用到素材或是参考一下别的设计的时候,素材倒好办,买DVD或是到电驴上下载回来就行。可是参考设计这个就没有了,于是我就有了一个念头,就是写个爬虫把常用的一些界面设计全部下载到本机来。

非线程版本:

#!/usr/bin/python
# -*- coding: utf-8 -*-

import os, sys
import urllib
import urllib2
import re

from BeautifulSoup import BeautifulSoup
import chardet

import datetime

get_charset = lambda html: chardet.detect(html)

abspath = os.path.dirname(__file__)

class FC(object):

    _base_url_ = None
    _list_params_ = None

    _urls_ = None
    _html_ = None

    _start_page_ = 1
    _end_page_ = 1
    _current_page_ = 1

    _soup = None

    _begin_time_ = None

    _download_dir_ = 'downloads/'
    _download_list_ = {}

    def __init__(self, base_url, list_param, set_end_page = None):
        """
        构造函数
        """
        self._begin_time_ = datetime.datetime.now()

        self.logging(u'开始运行...')

        (self._base_url_, self._list_params_) = (base_url, list_param)

        if set_end_page == None and set_end_page > 0:
            self.setEndPage()
        else:
            self._end_page_ = int(set_end_page)
            self.logging(u'手动设置列表分页总数为 %d 页...' % set_end_page)

        self.loadListPages()

    def __del__(self):
        """
        析构函数
        """
        self.logging(u'执行时间:%d 秒...' % (datetime.datetime.now() - self._begin_time_).seconds)
        self.logging(u'结束运行...')

    def nextPage(self):
        """
        当前页面递加 1
        """
        self._current_page_ = self._current_page_ + 1

    def setEndPage(self):
        """
        设置列表分页总数
        """
        self.logging(u'获得并设置列表分页总数...')

        html = self.fetchHtml(self.getFirstPageLink())

        pager = self._soup.find('td', attrs={'align': 'center', 'bgcolor': '#FFFFFF', 'class': 't14'}).renderContents()

        if pager:
            href = BeautifulSoup(pager).findAll('a')[-1]['href']

            try:
                count = re.search('d{2}', href).group()
            except:
                self.logging('Can not fetch pages count!', 'ERROR')

            self._end_page_ = int(count)
        self.logging(u'总共 %d 页...' % self._end_page_)

    def loadListPages(self):
        """
        载入页面列表
        """
        self._urls_ = [(self._base_url_ + (self._list_params_ % i)) for i in range(self._start_page_, (self._end_page_ + 1))]

        self.logging(u'获得所有列表页面URL...')

    def getFirstPageLink(self):
        """
        获得第一页的链接
        """
        return self._base_url_ + (self._list_params_ % self._start_page_)

    def getPageLink(self, page):
        """
        获得页面链接
        """
        return self._base_url_ + page

    def fetchAll(self):
        """
        分析列表
        """
        self.logging(u'开始分析列表...')

        if self._urls_:
            for url in self._urls_:
                self.fetchListHtml(url)
                self.getImgList()
                self.logging(u'分析第 %d 页完成,执行时间:%d 秒...' % (self._current_page_, (datetime.datetime.now() - self._begin_time_).seconds))

                self.nextPage()

                self.logging(u'-----------------------------------------------', 'LINE')
        else:
            self.logging(u'列表堆栈为空,无法分析...', 'WAITTING')

        # 开始下载
        # if self._download_list_:
            # self.logging(u'开始下载图片...')

            # for title in self._download_list_.keys():
                # self.download(self._download_list_[title], title)

        # self.logging(u'下载完成...')

    def fetchListHtml(self, url):
        """
        获得列表页HTML
        """
        self._soup = None

        self.logging(u'开始采集第 %d 页的数据...' % self._current_page_)

        try:
            html = urllib.urlopen(url).read()
            self._soup = BeautifulSoup(html, fromEncoding='utf-8')
            self._html_ = self._soup.find('table',  attrs={'width': '96%', 'bgcolor': '#efefef', 'cellspacing': '0', 'cellpadding': '0', 'border': '0'}).prettify()
            self._html_ = self._encoding(self._html_)
        except:
            self.logging(u'采集 %s 的数据失败...' % url, 'WAITTING')

        html = None
        return self._html_

    def fetchHtml(self, url, is_prettify = True):
        """
        获得页面HTML
        """
        self._soup = None

        self.logging(u'抓取 %s 的数据...' % url)

        try:
            html = self.fetch(url)
            self._soup = BeautifulSoup(html, fromEncoding='utf-8')
            if is_prettify:
                self._html_ = self._soup.prettify()
                self._html_ = self._encoding(self._html_)
            else:
                self._html_ = self._soup
        except:
            self.logging(u'采集 %s 的数据失败...' % url, 'WAITTING')

        html = None
        return self._html_

    def fetch(self, url):
        """
        获取内容
        """
        try:
            opener = urllib2.build_opener(urllib2.HTTPHandler)
            opener.addheaders = [('User-agent', 'Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3')]
            opener.addheaders = [('Referer', 'http://www.68design.net/')]
            urllib2.install_opener(opener)
            response = urllib2.urlopen(url)

            return response.read()
        except:
            self.logging(u'采集 %s 的数据失败...' % url, 'WAITTING')

    def getImgList(self):
        """
        获得图片列表
        """
        self.logging(u'开始获取第 %d 页的图片URL...' % self._current_page_)

        try:
            tables = self._soup.findAll('table', attrs={'width': '116', 'align': 'center', 'cellspacing': '0', 'cellpadding': '0', 'border': '0'})
        except:
            self.logging(u'获取失败...', 'WAITTING')

        if tables:
            self.logging(u'总共获得 %s 条地址' % len(tables))

            for table in tables:
                link = table.find('a', attrs={'class': 'a04'})['href']

                """
                获得列表中的图片
                """
                page = self.fetchHtml(self.getPageLink(link), False)

                try:
                    # 标题
                    title = page.find('td', attrs={'height': '65', 'align': 'center', 'valign': 'bottom', 'class': 't25'}).contents[0]
                    self.logging(u'获取图片标题为 [%s]...' % title)
                    # 分页
                    pagerSoup = page.find('td', attrs={'align': 'center', 'class': 't14', 'width': None, 'height': None})
                except:
                    self.logging(u'获取图片标题失败...', 'WAITTING')

                links = self.getImgsPager(pagerSoup)

                if links:
                   imgList = [self.getPageLink(link)] + [self._base_url_ + link for link in links]
                else:
                   imgList = [self.getPageLink(link)]

                self._download_list_[title] = self.getImgs(imgList)

    def getImgs(self, links):
        """
        获得页面中的图片
        """
        imgs = []

        if links:
            for link in links:
                page = self.fetchHtml(link, False)

                try:
                    imgs.append(page.find('img', attrs={'onload': 'resizepic(this)'})['src'])
                except:
                    self.logging(u'获取图片地址失败...', 'WAITTING')

        return imgs

    def getImgsPager(self, pager = None):
        """
        获得列表中图片的内容分页列表
        """
        if pager:
            # 清除重复链接(下一页,上一页,尾页)
            return [str(link['href']) for link in pager.findAll('a') if re.search('[d{1|2}]', link.string)]

        return None

    def download(self, lists, title):
        """
        下载图片文件
        """
        path = os.path.join(abspath, self._download_dir_, title)

        if title:
            if os.path.exists(path) == False:
                os.mkdir(path)

        if lists:
            for url in lists:
                try:
                    data = self.fetch(url)
                except:
                    self.logging(u'图片 %s 无法下载...' % url, 'WAITTING')

                suffix = url[-3:]
                filename = 'image-' + str(lists.index(url) + 1) + '.' + suffix

                try:
                    f = file(os.path.join(path, filename), 'wb')
                    f.write(data)
                    f.close()

                    self.logging(u'写入 [%s] 文件完成...' % filename)
                except:
                    self.logging(u'写入 [%s] 文件失败...' % filename, 'WAITTING')

    def _encoding(self, html):
        """
        转换编码
        """
        charset = get_charset(html)
        if charset['encoding'] == 'utf-8':
            html = unicode(html, 'utf-8', 'ignore').encode('gbk', 'ignore')

        return html

    def logging(self, msg, type = 'INFO'):
        """
        输出格式化日志
        """
        print '[%s] - %s (%s)' % (type, msg, datetime.datetime.now())

def main():
    fc = FC('http://www.68design.net/Appreciate/Interface/', 'list-%d.html', 1)
    fc.fetchAll()

    #lists = fc.getImgs(['http://www.68design.net/Appreciate/Interface/55303-1.html'])
    #fc.download(lists, 'hello')

if __name__ == '__main__':
    main()

说明:

此程序依赖的包有

结语:

我测试爬完第一页用了260秒+(WLAN), 56秒+(LAN),看来无线网卡掉包的厉害呀~~~

写得不好,请别见笑~~~

{PS}

其实我还有一个多线程版本,比这个要快一点,但还是觉得不够好,想做成GUI的。
最主要的原因是,设计图片共94页,每页30组, 每组按平均5张图片计算,每张图片按我爬第一页的平均SIZE: 500Kb计算,就这样也超过4G的数据,以我无线网卡的能力要下载4G估计是很痛苦的事情。
所以,必须要减少分析页面的次数,并可以哪停下哪,以配合我的上班时间。

{OVER}

版权所有,转载请注明出处。
转载自 <a href="http://www.movoin.com/python-devel-spider.html" title="Python编写蜘蛛/爬虫" rel="bookmark">Python编写蜘蛛/爬虫 | Movoin Studio</a>
Tags: Python , 爬虫

Comments are closed.

无觅相关文章插件,快速提升流量