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

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()

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,569评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,499评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,271评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,087评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,474评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,670评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,911评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,636评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,397评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,607评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,093评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,418评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,074评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,092评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,865评论 0 196
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,726评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,627评论 2 270

推荐阅读更多精彩内容