美文网首页Python
Python 爬虫实战计划:第一周实战作业

Python 爬虫实战计划:第一周实战作业

作者: 4ffde5305e8f | 来源:发表于2016-12-31 23:45 被阅读138次

    要求:
    1. 爬取58同城转转二手交易平台商品信息 http://bj.58.com/pbdn/0/
    2. 爬取每一页商品的所有商品链接,爬取前十页
    3. 爬取每一件商品的详细信息:类目,标题,价格,区域
    分析:
    1. 每一页商品的链接格式都相同均为 :http://bj.58.com/pbdn/0/pn{}/,
    由此在{}内填充1-10,即可得到前10页的商品html页面地址。
    2.每一页中每一条商品的详情页面均在
    “div#infolist > div[class=infocon] > table.tbimg tbody > tr > td.t > a” 路径下
    3. 商品详情页:
    类目:"div#nav > div.breadCrumb.f12 span a"
    标题:"h1.info_titile"
    价格:"span.price_now i"
    区域:"div.palce_li span > i"
    4. 避免程序的中断,再次重启后数据的重复下载,构造了一个缓存模块:
    1.设置缓存保质期为1天,过期则删除缓存
    2. 对未在缓存内的页面进行下载。
    3.将页面url作为唯一标示,保存在缓存内
    4.将url转换为MD5码保存在磁盘中,
    因为windows磁盘文件命名系统对特殊的字符有限制,而且最长不能超过255个字符,
    相对于url来说,可能包含一些非法的文件命名字符,所以转换为MD5来存储,简单粗暴
    5. 将爬取的商品信息保存到CSV文件内。如下:

    image.png

    具体代码如下:

    #coding=utf-8
    """爬取五八同城 转转二手交易平台商品信息"""
    import os
    import csv
    import time
    import shutil
    import datetime
    import hashlib
    import requests
    from bs4 import BeautifulSoup
    
    class Cache(object):
        """缓存类,用于处理缓存文件
            缓存保质期为1天,失效则删除缓存文件"""
        def __init__(self,cache_name='cache'):
            """如果缓存文件夹不存在,则新建缓存文件夹"""
            if not os.path.exists(cache_name):
                os.mkdir(cache_name)
                #建立一个time文件,用来储存上次启动爬虫的时间
                #本次启动时间与上次启动时间的间隔大于1天,则清除所有缓存
                with open(cache_name+'/time','w') as f:
                    #获得当前系统时间,并按照时间格式转换为字符串格式
                    time_str = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                    f.write(time_str)
    
            with open(cache_name + '/time', 'r') as f:
                time_str = f.read()
                time_date = datetime.datetime.strptime(time_str,'%Y-%m-%d %H:%M:%S')
                if datetime.datetime.now() - time_date > datetime.timedelta(days=1):
                    #shutil是一个高级的文件操作模块
                    #rmtree 删除目录下的所有文件及文件夹,包括子文件夹内的所有文件及文件夹,递归删除
                    #os模块
                    #os.remove(name)只能删除文件,不能删除文件夹(目录)
                    #os.rmdir(name)只能删除空文件夹(目录),非空则报错
                    #os.removedirs()删除空文件夹(目录)及空的子文件夹(目录),非空则报错。递归删除
                    shutil.rmtree(cache_name)
                    print '缓存保存日期大于1天,已过期,从新下载数据'
                else:
                    print '缓存正常 非过期'
            self.cache_name = cache_name
    
        def __url_md5(self,url):
            """获得url对应的md5码"""
            # 获得url的MD5码
            # 因为windows磁盘文件命名系统对特殊的字符有限制,而且最长不能超过255个字符
            # 相对于url来说,可能包含一些非法的文件命名字符,所以转换为MD5来存储,简单粗暴
            md5 = hashlib.md5()
            md5.update(url)
            return md5.hexdigest()
    
        def is_cache(self,url):
            """判断缓存是否存在
                返回 True 表示存在,False表示不存在"""
            cache_list = os.listdir(self.cache_name)  # 获得缓存文件名列表
            # 获得url的MD5码
            md5 = self.__url_md5(url)
            if md5 in cache_list:
                print '已经缓存该 {} 网页'.format(url)
                return True
            return False
    
        def save_cache(self,url):
            """保存url到缓存"""
            old_path = os.getcwd()  # 获得当前工作目录路径
            os.chdir(self.cache_name)  # 改变工作目录为缓存文件
            #print '切换后--',os.getcwd()
            with open(self.__url_md5(url), 'w'):
                pass
            print '缓存不存在,缓存 {} 网页'.format(url)
            os.chdir(old_path)  # 切换到原始目录
    
    def get_html(url):
        """获得网页源码"""
        time.sleep(1)  # 延迟1秒
        headers = {
            'User-Agent': ('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)'
                           'Chrome/54.0.2840.87 Safari/537.36')
        }
        html = requests.get(url, headers=headers).text
        return html
    
    def get_product_list(url):
        """获得当前页所有商品的链接
           返回一个list"""
        html = get_html(url)
        soup = BeautifulSoup(html, 'lxml')
        # div[class=infocom] 表示选择 <div class = 'infocom'>xxxx</div>的标签
        #    <div class = 'infocom jzcon'>xxxx</div> 则不会被选择
        # 如果需要选择 class='infocom jzcon'
        #   可以使用 'div.infocom.jzcon'
        # 切记:'div.infocom' 是只要 class属性里包含 infocm 就会被查询到
        #   例如:<div class = 'infocom'>xxxx</div> <div class = 'infocom jzcon'>xxxx</div>
        #    'div.infocom' 会返回查询到的 两个标签信息 而不是一个
        #    想要得到唯一的<div class = 'infocom'>,
        #    则需要 'div[class=infocom']
        products = soup.select('div#infolist > div[class=infocon] > table.tbimg tbody > tr > td.t > a')
        product_list = [product.get('href') for product in products]
        print '当前页 {} 有 {} 个商品'.format(url,len(product_list))
        return product_list
    
    def product_detailed_info(url):
        """获得商品的详细信息
           商品类目,商品标题,商品价格,商品区域
           """
        html = get_html(url)
        soup = BeautifulSoup(html, 'lxml')
        product_type = soup.select('div#nav > div.breadCrumb.f12 span a')[-1].get_text().strip()
        product_title = soup.select('h1.info_titile')[0].get_text().strip()
        product_price = soup.select('span.price_now i')[0].get_text().strip()
        product_area = soup.select('div.palce_li span > i')[0].get_text()
    
        product_dict = {}
        product_dict['type'] = product_type.encode('utf-8')
        product_dict['title'] = product_title.encode('utf-8')
        product_dict['price'] = product_price
        product_dict['area'] = product_area.encode('utf-8')
    
        #爬取一个商品详细信息,保存一个,避免程序的以外中断导致数据的丢失
        save_product_info(product_dict,'product.csv')
        print '保存商品信息到文件 {}'.format(url)
    
    def save_product_info(info,file_name):
        """保存商品信息到指定文件内"""
        fieles = ['price','area','type','title']
        if not os.path.exists(file_name):
            with open(file_name, 'wb') as f:
                writer = csv.DictWriter(f, fieldnames=fieles)
                writer.writerow(dict(zip(fieles, fieles)))
        with open(file_name,'ab') as f:
            writer = csv.DictWriter(f, fieldnames=fieles)
            writer.writerow(info)
    
    def start():
        """启动爬虫"""
        # 获得前十页的url
        base_url = 'http://bj.58.com/pbdn/0/pn{}/'
        urls = [base_url.format(page) for page in range(1, 11)]
    
        # 获得每一页中的商品链接
        product_url_set = set()
        for url in urls:
            product_url_set.update(get_product_list(url))
        print '总共 {} 商品'.format(len(product_url_set))
    
        #获得每件商品的详细信息
        #并将已下载过的url保存到缓存中
        count = 0
        cache = Cache() #缓存
        for product_url in product_url_set:
            if not cache.is_cache(product_url):
                product_detailed_info(product_url)
                cache.save_cache(product_url)
                count += 1
        print '本次保存 {} 件商品'.format(count)
    
    if __name__ == '__main__':
        #启动爬虫
        start()
    
    

    相关文章

      网友评论

        本文标题:Python 爬虫实战计划:第一周实战作业

        本文链接:https://www.haomeiwen.com/subject/egsmvttx.html