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

要求:
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文件内。如下:

Python 爬虫实战计划:第一周实战作业_第1张图片
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] 表示选择 
xxxx
的标签 #
xxxx
则不会被选择 # 如果需要选择 class='infocom jzcon' # 可以使用 'div.infocom.jzcon' # 切记:'div.infocom' 是只要 class属性里包含 infocm 就会被查询到 # 例如:
xxxx
xxxx
# 'div.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 爬虫实战计划:第一周实战作业)