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

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

作者: 4ffde5305e8f | 来源:发表于2017-01-13 15:10 被阅读0次

    爬取北京赶集网——二手市场——所有商品类目的个人类别的商品信息

    最近工作上一直比较忙,拖到现在才写完作业,实在是太不好意思了。
    统计了一下,总共爬取了 大概三万条数据,每一页有很多的重复的数据。
    流程:
    1. 获得所有栏目的url
    2.获得每个栏目的所有商品链接
    2.1:建立栏目对应的抓取股则
    3.获得每个商品的详细信息
    具体分析:
    1. 获得所有栏目的url
    a. 栏目的url在 'dl.fenlei > dt > a' 路径下
    b. 剔除 手机号码栏目,里面什么也没有
    2. 获得每个栏目的第一页商品链接
    a. 建立商品链接抓取规则:
    经过测试一共有三种抓取规则:
    i. 规则1
    1) 'div.infocon tr.zzinfo td.t a.t'大部分栏目都是这个规则
    ii. 规则2
    1) 'a.ft-tit'
    2) 栏目:设备办公用品,农用品,闲置礼品,虚拟物品
    iii. 规则3:
    1) 'div.layoutlist div.infor01 > a'
    2) 栏目:杂七杂八,免费赠送,物品交易
    其余均为规则1
    b. 将抓取到的商品url存储到数据库中:
    3. 获得每个栏目的每一页的商品链接:
    a. 经过观察发现:
    i. 每个栏目的第一页均为: ‘http://bj.ganji.com/栏目url/’
    ii. 除了第一页的每一页均为:‘http://bj.ganji.com/栏目/o页码/'
    iii. 页码均已 2 开始,表示第二页
    b. 边界检查:
    i. 爬取到最后一页,则停止爬取
    ii. 经过观察发现,每个栏目的每一商品列表页,有三种边界形式
    1) 最后一页的下一页,后有一个 空商品列表的页面。
    2) 最后一页的下一页,会有一些过时数据(商品)列表页面
    第一条形式,我们可以检测,页面商品页面列表是否为空,来进行边界判断。
    第二条形式,就不好判断了。因为最后一页的下一页仍然会有商品列表,只是过时数据,但并不是我们想要的。
    iii. so,需要另外一种边界判断形式:
    1) 我们发现,正常页面每一页都有一个 下一页 的标签,最后一页的下一页,则没有 下一页标签
    2) 有一些另外的:
    a) 最有一页没有下一页标签,最后一页的下一页,要么是空商品列表页,要么是过时,商品列表页。
    b) so,为了简单处理。我们决定,把这个类型的最后一页pass掉,
    4. 获得每页的商品信息:
    商品信息页面共两种页面:
    1. 转转网
    1) 获得:标题,价格,类型
    2. 赶集网
    1) 获得:标题,价格,类型,地域。
    实现:
    具体分三个实现,一个MongoDB配置文件:
    1. 页面链接爬取器:爬取所有页面中的所有商品链接
    2. 页面详情爬取器:爬取所有商品链接页面的商品信息。
    3. 主程序:入口程序,调用链接爬取器,详情爬取器。
    4.MongoDB配置文件
    以下为具体代码:
    MongoDB配置文件:

    #coding=utf-8
    """mongoDB的相关配置"""
    
    from pymongo import MongoClient
    client = MongoClient('localhost',27017)
    db = client['ganji']
    #商品url
    bj_item_urls_collenction = db['bj_item_urls_collenction']
    #每一页的页码url
    bj_page_urls_collenction = db['bj_page_urls_collenction']
    #每个商品页码信息
    bj_page_info_collenction = db['bj_page_info_collenction']
    

    链接爬取器:

    #coding=utf-8
    import time
    import requests
    import mongoConfig
    from bs4 import BeautifulSoup
    
    bj_item_urls_collenction = mongoConfig.bj_item_urls_collenction
    bj_page_urls_collenction = mongoConfig.bj_page_urls_collenction
    
    def get_html(url):
        """获得html源码"""
        html = None
        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')
        }
        #request并不会主动抛出http status code 异常,也就是说 404 之类的
        #所有我们只需要捕捉超时异常即可
        try:
            response = requests.get(url,headers=headers)
            if response.status_code == 200:
                html = response.text
                print '下载--',url
            else:
                print 'HTTP状态码不对',response.status_code,url
        except requests.Timeout as e:
            print '访问超时 --- ',url
        time.sleep(3)
        return html
    
    def get_all_category_links(url):
        """获得所有栏目的url"""
        html = get_html(url)
        soup = BeautifulSoup(html, 'lxml')
        base_url = 'http://bj.ganji.com{}'
        dl = soup.select('dl.fenlei > dt > a')
        links = None
        if dl:
            links = [base_url.format(link.get('href')) for link in dl]
        else:
            print '栏目信息不存在',url
        if 'http://bj.ganji.com/shoujihaoma/' in links:
            links.remove('http://bj.ganji.com/shoujihaoma/')
            print '删除 http://bj.ganji.com/shoujihaoma/'
        return links
    
    def get_item_links(url):
        """获得当前页商品列表的url"""
        html = get_html(url)
        soup = BeautifulSoup(html, 'lxml')
        #三种抓取规则
        item_rules = ['div.infocon tr.zzinfo td.t a.t','a.ft-tit','div.layoutlist div.infor01 > a']
        #a.next 下一页 标签
        if soup.find('a', 'next'):
            for item_rule in item_rules:
                items = soup.select(item_rule)
                if items:
                    return [link.get('href').split('?')[0] for link in items]
        return None
    
    def save_to_db(infos):
        """保存到数据库"""
        start = bj_item_urls_collenction.find().count()
        for url in infos:
            if not bj_item_urls_collenction.find_one({'url': url}):
                bj_item_urls_collenction.insert_one({'url': url})
                #print '插入数据库成功 {}'.format(url)
            else:
                pass
                #print '当前商品已经抓取过 {} '.format(url)
        end = bj_item_urls_collenction.find().count()
        print '当前页有 {} 条数据'.format(len(infos))
        print '保存 {} 条数据到数据库'.format(end-start)
    
    def process_main(url):
        """抓取当前栏目的每一页的所有连接"""
        def page_zhuaqu(url):
            is_continue = True
            if not bj_page_urls_collenction.find_one({'url': url}):
                links = get_item_links(url)
                if links:
                    save_to_db(links) #保存商品列表到url
                    bj_page_urls_collenction.insert_one({'url': url})
                    is_continue = True
                    print '抓取 {} 页成功'.format(url)
                else:
                    print '页面不存在,到达边界', url
                    is_continue = False
            else:
                print '当前 {} 页已经抓取过'.format(url)
            return is_continue
    
        #抓取第一页
        page_zhuaqu(url)
        #抓取其余的页
        page = 2
        is_continue = True
        base_url = url + 'o{}/'
        while is_continue:
            page_url = base_url.format(page)
            is_continue = page_zhuaqu(page_url)
            page += 1
    

    页面详情爬取器:

    #coding=utf-8
    import time
    import requests
    import mongoConfig
    from bs4 import BeautifulSoup
    
    bj_item_urls_collenction = mongoConfig.bj_item_urls_collenction
    bj_page_info_collenction = mongoConfig.bj_page_info_collenction
    
    def get_html(url):
        """获得html源码"""
        html = None
        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')
        }
        #request并不会主动抛出http status code 异常,也就是说 404 之类的
        #所有我们只需要捕捉超时异常即可
        try:
            response = requests.get(url,headers=headers)
            if response.status_code == 200:
                html = response.text
                print '下载--',url
            else:
                print 'HTTP状态码不对',response.status_code,url
        except requests.Timeout as e:
            print '访问超时 --- ',url
        time.sleep(3)
        return html
    
    def price_to_float(price_tag):
        """价格转换为float格式
            价格 有三种格式:数字型:200     直接转换为数字
                          数字+字符型:1.2万 将万转换为元 12000
                          字符型:面议    不做修改
        """
        price = None
        if price_tag:
            price_str = price_tag[0].get_text()
            if price_str == u'面议':
                price = price_str
            elif price_str.endswith(u'万'):
                price = float(price_str[:-2]) * 10000
            else:
                price = float(price_str)
        return price
    
    def zhuanzhuan_page_parse(soup,url):
        """转转二手交易页面,解析
            获得:标题,价格,地域"""
        area = soup.select('div.palce_li > span > i')
        title = soup.select('h1.info_titile')
        price = soup.select('span.price_now > i')
    
        data = {'url':url,'title': title[0].get_text() if title else None,
                'price': price_to_float(price),
                'area':area[0].get_text() if area else  None
                }
        return data
    
    def ganji_page_parse(soup,url):
        """赶集网二手交易页面解析
            获得:标题,类型,价格,地域"""
        title= soup.select('h1.title-name')[0].get_text()
    
        item_soup = soup.select('ul.det-infor > li')
        item_type = item_soup[0].find('a').get_text()
        price = price_to_float(item_soup[1].select('i'))
        area_soup = item_soup[2].find_all('a')
        # BeautifulSoup返回的都是unicode格式的字符串,所以使用unicode格式的字符串来格式化,避免编码问题。
        area = u'{}-{}'.format(area_soup[0].string.strip(), area_soup[1].string.strip())
    
        data = {
            'url':url,
            'title':title,
            'tiem_type':item_type,
            'price':price,
            'area':area
        }
        return data
    
    def process_main(url):
        """页面解析,并存储"""
        if not bj_page_info_collenction.find_one({'url':url}):
            html = get_html(url)
            soup = BeautifulSoup(html, 'lxml')
            data = None
            if url.startswith('http://bj.ganji.com'):
                data = ganji_page_parse(soup,url)
            elif url.startswith('http://zhuanzhuan.ganji.com'):
                data = zhuanzhuan_page_parse(soup,url)
            bj_page_info_collenction.insert_one(data)
            print '保存数据库成功 {}'.format(url)
        else:
            print '页面已经抓取过 {}'.format(url)
    

    主程序:

    #coding=utf-8
    import multiprocessing
    import mongoConfig
    import links_spider
    import pages_spider
    
    if __name__ == '__main__':
        #先抓取所有商品连接,保存数据库
        url = 'http://bj.ganji.com/wu/'
        links_urls = links_spider.get_all_category_links(url)
        #未设置进程数,则默认调用cpu个数个进程
        pool = multiprocessing.Pool()
        pool.map_async(links_spider.process_main,links_urls)
        pool.close()
        pool.join()
    
        #再从数据库中抓取所有商品连接,并解析页面信息
        items_urls =[items['url'] for items in mongoConfig.bj_item_urls_collenction.find({})]
    
        pool = multiprocessing.Pool()
        pool.map_async(pages_spider.process_main,items_urls)
        pool.close()
        pool.join()
    
        print '总共保存 {} 条商品url'.format(mongoConfig.bj_item_urls_collenction.find().count())
        print '总共保存 {} 条商品信息'.format(mongoConfig.bj_page_info_collenction.find().count())
    

    相关文章

      网友评论

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

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