精华内容
下载资源
问答
  • scrapy爬虫完整实例

    2020-09-20 20:37:45
    主要介绍了scrapy爬虫完整实例,小编觉得还是挺不错的,具有一定借鉴价值,需要的朋友可以参考下
  • scrapy分布式爬虫案例

    千次阅读 2019-09-06 20:47:39
    关于 Redis Redis 是目前公认的速度最快的基于内存的键值对...scrapy-redis 是为了更方便地实现 Scrapy 分布式爬取,而提供的一些以 Redis 为基础的组件。 scrapy 使用 python 自带的 collection.deque 来存放待爬...

    在这里插入图片描述
    关于 Redis
    Redis 是目前公认的速度最快的基于内存的键值对数据库
    Redis 作为临时数据的缓存区,可以充分利用内存的高速读写能力大大提高爬虫爬取效率。

    关于 scrapy-redis
    scrapy-redis 是为了更方便地实现 Scrapy 分布式爬取,而提供的一些以 Redis 为基础的组件。
    scrapy 使用 python 自带的 collection.deque 来存放待爬取的 request。scrapy-redis 提供了一个解决方案,把 deque 换成 redis 数据库,能让多个 spider 读取同一个 redis 数据库里,解决了分布式的主要问题。

    为什么要使用scrapy-redis分布式爬虫
    scrapy是python界出名的一个爬虫框架。Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架。可以应用在包括数据挖掘,信息处理或存储历史数据等一系列的程序中。
    虽然scrapy能做的事情很多,但是要做到大规模的分布式应用则捉襟见肘。
    一个调度器,容器被封,慢。
    有能人改变了scrapy的队列调度,将起始的网址从start_urls里分离出来,改为从redis读取,多个客户端可以同时读取同一个redis,从而实现了分布式的爬虫。

    一、redis环境部署

    首先需要下载redis数据库和Redis数据可的可视化工具并解压安装,将redis数据库设置为远程连接
    在这里插入图片描述
    1、解压Redis-x64-3.2.100.zip包,修改文件配置
    在这里插入图片描述
    2、master主机修改ip为0.0.0.0,运行slaver从机访问
    在这里插入图片描述
    3、关闭保护模式
    在这里插入图片描述
    4、不开启守护进程模式
    在这里插入图片描述
    5、启动redis服务

    redis-server.exe redis.windows.conf
    关闭redis命令:
    redis-cli
    shutdown
    

    在这里插入图片描述

    二、添加爬取的数据url至redis数据库,相当于放入redis队列

    由于我们这里爬取的为淘车项目,涉及到全国的城市及城市下的汽车品牌
    列:https://beijing.taoche.com/audi/
    beijing 为城市缩写,audi 为汽车品牌
    我们基于这种url去请求,爬取数据,实现爬取接口,将全国城市和所有汽车品牌爬取下来,分别存储到一个py文件的列表中

    city.py

    # 城市编码
    CITY_CODE = ['shijiazhuang', 'tangshan', 'qinhuangdao', 'handan', 'xingtai', 'baoding', 'zhangjiakou',
                 'chengde', 'cangzhou', 'langfang', 'hengshui', 'taiyuan', 'datong', 'yangquan', 'changzhi', 'jincheng',
                 'shuozhou', 'jinzhong', 'yuncheng', 'xinzhou', 'linfen', 'lvliang', 'huhehaote', 'baotou', 'wuhai',
                 'chifeng', 'tongliao', 'eerduosi', 'hulunbeier', 'bayannaoer', 'wulanchabu', 'xinganmeng',
                 'xilinguolemeng', 'alashanmeng', 'changchun', 'jilin', 'hangzhou', 'ningbo', 'wenzhou', 'jiaxing',
                 'huzhou', 'shaoxing', 'jinhua', 'quzhou', 'zhoushan', 'tz', 'lishui', 'bozhou', 'chizhou', 'xuancheng',
                 'nanchang', 'jingdezhen', 'pingxiang', 'jiujiang', 'xinyu', 'yingtan', 'ganzhou', 'jian', 'yichun', 'jxfz',
                 'shangrao', 'xian', 'tongchuan', 'baoji', 'xianyang', 'weinan', 'yanan', 'hanzhong', 'yl', 'ankang',
                 'shangluo', 'lanzhou', 'jiayuguan', 'jinchang', 'baiyin', 'tianshui', 'wuwei', 'zhangye', 'pingliang',
                 'jiuquan', 'qingyang', 'dingxi', 'longnan', 'linxia', 'gannan', 'xining', 'haidongdiqu', 'haibei',
                 'huangnan', 'hainanzangzuzizhizho', 'guoluo', 'yushu', 'haixi', 'yinchuan', 'shizuishan', 'wuzhong',
                 'guyuan', 'zhongwei', 'wulumuqi', 'kelamayi', 'shihezi', 'tulufandiqu', 'hamidiqu', 'changji', 'boertala',
                 'bazhou', 'akesudiqu', 'xinjiangkezhou', 'kashidiqu', 'hetiandiqu', 'yili', 'tachengdiqu', 'aletaidiqu',
                 'xinjiangzhixiaxian', 'changsha', 'zhuzhou', 'xiangtan', 'hengyang', 'shaoyang', 'yueyang', 'changde',
                 'zhangjiajie', 'yiyang', 'chenzhou', 'yongzhou', 'huaihua', 'loudi', 'xiangxi', 'guangzhou', 'shaoguan',
                 'shenzhen', 'zhuhai', 'shantou', 'foshan', 'jiangmen', 'zhanjiang', 'maoming', 'zhaoqing', 'huizhou',
                 'meizhou', 'shanwei', 'heyuan', 'yangjiang', 'qingyuan', 'dongguan', 'zhongshan', 'chaozhou', 'jieyang',
                 'yunfu', 'nanning', 'liuzhou', 'guilin', 'wuzhou', 'beihai', 'fangchenggang', 'qinzhou', 'guigang',
                 'yulin', 'baise', 'hezhou', 'hechi', 'laibin', 'chongzuo', 'haikou', 'sanya', 'sanshashi', 'qiongbeidiqu',
                 'qiongnandiqu', 'hainanzhixiaxian', 'chengdu', 'zigong', 'panzhihua', 'luzhou', 'deyang', 'mianyang',
                 'guangyuan', 'suining', 'neijiang', 'leshan', 'nanchong', 'meishan', 'yibin', 'guangan', 'dazhou', 'yaan',
                 'bazhong', 'ziyang', 'aba', 'ganzi', 'liangshan', 'guiyang', 'liupanshui', 'zunyi', 'anshun',
                 'tongrendiqu', 'qianxinan', 'bijiediqu', 'qiandongnan', 'qiannan', 'kunming', 'qujing', 'yuxi', 'baoshan',
                 'zhaotong', 'lijiang', 'puer', 'lincang', 'chuxiong', 'honghe', 'wenshan', 'xishuangbanna', 'dali',
                 'dehong', 'nujiang', 'diqing', 'siping', 'liaoyuan', 'tonghua', 'baishan', 'songyuan', 'baicheng',
                 'yanbian', 'haerbin', 'qiqihaer', 'jixi', 'hegang', 'shuangyashan', 'daqing', 'yc', 'jiamusi', 'qitaihe',
                 'mudanjiang', 'heihe', 'suihua', 'daxinganlingdiqu', 'shanghai', 'tianjin', 'chongqing', 'nanjing', 'wuxi',
                 'xuzhou', 'changzhou', 'suzhou', 'nantong', 'lianyungang', 'huaian', 'yancheng', 'yangzhou', 'zhenjiang',
                 'taizhou', 'suqian', 'lasa', 'changdudiqu', 'shannan', 'rikazediqu', 'naqudiqu', 'alidiqu', 'linzhidiqu',
                 'hefei', 'wuhu', 'bengbu', 'huainan', 'maanshan', 'huaibei', 'tongling', 'anqing', 'huangshan', 'chuzhou',
                 'fuyang', 'sz', 'chaohu', 'luan', 'fuzhou', 'xiamen', 'putian', 'sanming', 'quanzhou', 'zhangzhou',
                 'nanping', 'longyan', 'ningde', 'jinan', 'qingdao', 'zibo', 'zaozhuang', 'dongying', 'yantai', 'weifang',
                 'jining', 'taian', 'weihai', 'rizhao', 'laiwu', 'linyi', 'dezhou', 'liaocheng', 'binzhou', 'heze',
                 'zhengzhou', 'kaifeng', 'luoyang', 'pingdingshan', 'jiyuan', 'anyang', 'hebi', 'xinxiang', 'jiaozuo',
                 'puyang', 'xuchang', 'luohe', 'sanmenxia', 'nanyang', 'shangqiu', 'xinyang', 'zhoukou', 'zhumadian',
                 'henanzhixiaxian', 'wuhan', 'huangshi', 'shiyan', 'yichang', 'xiangfan', 'ezhou', 'jingmen', 'xiaogan',
                 'jingzhou', 'huanggang', 'xianning', 'qianjiang', 'suizhou', 'xiantao', 'tianmen', 'enshi',
                 'hubeizhixiaxian', 'beijing', 'shenyang', 'dalian', 'anshan', 'fushun', 'benxi', 'dandong', 'jinzhou',
                 'yingkou', 'fuxin', 'liaoyang', 'panjin', 'tieling', 'chaoyang', 'huludao', 'anhui', 'fujian', 'gansu',
                 'guangdong', 'guangxi', 'guizhou', 'hainan', 'hebei', 'henan', 'heilongjiang', 'hubei', 'hunan', 'jl',
                 'jiangsu', 'jiangxi', 'liaoning', 'neimenggu', 'ningxia', 'qinghai', 'shandong', 'shanxi', 'shaanxi',
                 'sichuan', 'xizang', 'xinjiang', 'yunnan', 'zhejiang', 'jjj', 'jzh', 'zsj', 'csj', 'ygc']
    
    # 品牌类型列表
    CAR_CODE_LIST = ['southeastautomobile', 'sma', 'audi', 'hummer', 'tianqimeiya', 'seat', 'lamborghini', 'weltmeister',
                     'changanqingxingche-281', 'chevrolet', 'fiat', 'foday', 'eurise', 'dongfengfengdu', 'lotus-146', 'jac',
                     'enranger', 'bjqc', 'luxgen', 'jinbei', 'sgautomotive', 'jonwayautomobile', 'beijingjeep', 'linktour',
                     'landrover', 'denza', 'jeep', 'rely', 'gacne', 'porsche', 'wey', 'shenbao', 'bisuqiche-263',
                     'beiqihuansu', 'sinogold', 'roewe', 'maybach', 'greatwall', 'chenggongqiche', 'zotyeauto', 'kaersen',
                     'gonow', 'dodge', 'siwei', 'ora', 'lifanmotors', 'cajc', 'hafeiautomobile', 'sol', 'beiqixinnengyuan',
                     'dorcen', 'lexus', 'mercedesbenz', 'ford', 'huataiautomobile', 'jmc', 'peugeot', 'kinglongmotor',
                     'oushang', 'dongfengxiaokang-205', 'chautotechnology', 'faw-hongqi', 'mclaren', 'dearcc',
                     'fengxingauto', 'singulato', 'nissan', 'saleen', 'ruichixinnengyuan', 'yulu', 'isuzu', 'zhinuo',
                     'alpina', 'renult', 'kawei', 'cadillac', 'hanteng', 'defu', 'subaru', 'huasong', 'casyc', 'geely',
                     'xpeng', 'jlkc', 'sj', 'nanqixinyatu1', 'horki', 'venucia', 'xinkaiauto', 'traum',
                     'shanghaihuizhong-45', 'zhidou', 'ww', 'riich', 'brillianceauto', 'galue', 'bugatti',
                     'guagnzhouyunbao', 'borgward', 'qzbd1', 'bj', 'changheauto', 'faw', 'saab', 'fuqiautomobile', 'skoda',
                     'citroen', 'mitsubishi', 'opel', 'qorosauto', 'zxauto', 'infiniti', 'mazda', 'arcfox-289',
                     'jinchengautomobile', 'kia', 'mini', 'tesla', 'gmc-109', 'chery', 'daoda-282', 'joylongautomobile',
                     'hafu-196', 'sgmw', 'wiesmann', 'acura', 'yunqueqiche', 'volvo', 'lynkco', 'karry', 'chtc', 'gq',
                     'redstar', 'everus', 'kangdi', 'chrysler', 'cf', 'maxus', 'smart', 'maserati', 'dayu', 'besturn',
                     'dadiqiche', 'ym', 'huakai', 'buick', 'faradayfuture', 'leapmotor', 'koenigsegg', 'bentley',
                     'rolls-royce', 'iveco', 'dongfeng-27', 'haige1', 'ds', 'landwind', 'volkswagen', 'sitech', 'toyota',
                     'polarsunautomobile', 'zhejiangkaersen', 'ladaa', 'lincoln', 'weilaiqiche', 'li', 'ferrari', 'jetour',
                     'honda', 'barbus', 'morgancars', 'ol', 'sceo', 'hama', 'dongfengfengguang', 'mg-79', 'ktm',
                     'changankuayue-283', 'suzuki', 'yudo', 'yusheng-258', 'fs', 'bydauto', 'jauger', 'foton', 'pagani',
                     'shangqisaibao', 'guangqihinomotors', 'polestar', 'fujianxinlongmaqichegufenyouxiangongsi',
                     'alfaromeo', 'shanqitongjia1', 'xingchi', 'lotus', 'hyundai', 'kaiyi', 'isuzu-132', 'bmw', 'ssangyong',
                     'astonmartin']
    

    新建一个py文件,将其url拼接的所有结果存储到redis数据库中,用于后面爬取队列提取
    redis_url.py

    from taoche.spiders.city import CITY_CODE,CAR_CODE_LIST
    
    from redis import Redis
    # import sys
    # sys.path.append("..")
    class Redis_url():
    
        def __init__(self):
            #1.连接客户端,
            self.re = Redis("localhost", 6379)
    
        def add(self,url):
            #讲url,利用lpush方法,添加到"taoche:start_urls"键中,url为值
            self.re.lpush("taoche:start_urls",url)
    
    rd = Redis_url()
    
    # 先将redis中的requests全部清空
    # flushdbRes = rd.flushdb()
    for city in CITY_CODE:
        for car_code in CAR_CODE_LIST:
            rd.add( "https://{}.taoche.com/{}/".format(city, car_code))
    

    脚本运行之后,城市列表和汽车品牌交叉拼接之后的结果就保存到redis数据库中
    在这里插入图片描述

    修改爬虫文件:
    由于分布式爬虫需要部署在多台电脑上,所以起始的url就不能写在某一台电脑的start_urls中,需要指定一个redis_key这个字段用来在redis中写入爬虫文件所需要的第一个url
    redis_key这个字段命名规则

    这个名字就是存入redis数据库的键

    redis_key = “任意的字符串:start_urls”
    在这里插入图片描述
    修改setting配置(redis):

    #调度器
    SCHEDULER = "scrapy_redis.scheduler.Scheduler"
    #去重
    DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
    #redis服务器地址
    REDIS_HOST = 'localhost'(master主机写自己的地址,其他从机slaver写master远程主机地址)
    #redis端口号
    REDIS_PORT = 6379
    ##开启队列
    SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.PriorityQueue'
    

    三、MongoDB数据存储设置

    1、更改MongoDB数据库配置文件,主要用于其他从机slaver可以存储到远程的master的MongoDB数据库,添加如下配置

    logappend=true
    journal=true
    quiet=true
    port=27017
    

    在这里插入图片描述

    2、在pipelines.py文件中配置数据存储任务,连接远程的数据库

    import pymongo
    
    class TaochePipeline(object):
        def process_item(self, item, spider):
            return item
    
    class TaocheMongoPipeline(object):
        def __init__(self):
            # 创建客户端(master)
            self.client = pymongo.MongoClient(host='10.10.21.184',port=27017)
            # 创建数据库
            self.db = self.client['taoche']
            # 创建集合
            self.collection = self.db['car']
        def process_item(self, item, spider):
            print("="*30)
            self.collection.insert(dict(item))
            return item
    

    3、settings配置中使用此pipeline配置,解开注释,更新配置
    在这里插入图片描述

    到此,所有准备工作已完成,只要爬虫写好,就可以完整的运行整个项目进行淘车数据爬取

    四、编写爬虫程序

    1、爬取定义数据的字段数据,分析淘车页面,列表页,详情页结构,进行相应的数据爬取
    item.py

    import scrapy
    
    
    class TaocheItem(scrapy.Item):
        # define the fields for your item here like:
        # name = scrapy.Field()
        # 列表页
        title = scrapy.Field()  # 标题
        reg_date = scrapy.Field()  # 上牌日期
        mile = scrapy.Field()  # 公里数
        price = scrapy.Field()  # 优惠价格
        all_price = scrapy.Field()  # 全款价格
        detail_url = scrapy.Field()  # 详情url
        # 详情页
        city_name = scrapy.Field()  # 城市名称
        pic = scrapy.Field()  # 图片
        displace = scrapy.Field()  # 排量
        source_id = scrapy.Field()  # 车源号
        name = scrapy.Field() # 用于标识master数据库中存储的是哪个从机的数据
    

    2、编写爬虫程序
    spider:
    s_taoche.py

    # -*- coding: utf-8 -*-
    import scrapy
    import re
    from taoche.spiders.city import CITY_CODE,CAR_CODE_LIST
    from lxml import etree
    from taoche.items import TaocheItem
    from scrapy_redis.spiders import RedisSpider # 导入RedisSpider
    # 城市编码
    city_code = CITY_CODE
    # 车 品牌列表
    car_code_list = CAR_CODE_LIST
    # for city in city_code:
    #     for car in car_code_list:
    #         url = f'https://{city}.taoche.com/{car}/?page='
    #         print(url)
    # print(len(city_code)*len(car_code_list))
    
    # class STaocheSpider(scrapy.Spider):
    class STaocheSpider(RedisSpider):
        name = 's_taoche'
        redis_key = 'taoche:start_urls'
        # allowed_domains = ['taoche.com']
        # start_urls = ['https://beijing.taoche.com/all/?page=1']
        # https://beijing.taoche.com/all/?page=2
        # https://beijing.taoche.com/audi/?page=2#pagetag
        # 暂时没有找到符合条件的二手车
        # 'class="pages-next"'
        # 'class="pages-next pages-disabled"'
        def parse(self, response):
            # print("----",response.url)
            # html = response.body.decode('utf-8')
            page = response.xpath('//div[@class="paging-box the-pages"]/div/a[last()-1]/text()').extract()
            if len(page) > 0:
                page = page[0]
            else:
                page = 1
            # tree = etree.HTML(response.body.decode('utf-8'))
            # page = tree.xpath('//div[@class="paging-box the-pages"]/div/a[last()-1]/text()')
            # print("========",page)
            for p in range(1,int(page)+1):
                url = response.url + '?page=%s'%p
                # print("+++++++++",url)
                yield scrapy.Request(url=url,callback=self.parse_list)
    
    
    
    
    
        def parse_list(self, response):
            # with open('taoche.html','w',encoding='utf-8')as fp:
            #     fp.write(response.body.decode('utf-8'))
            # li_list = response.xpath('//div[@id="container_base"]/ul/li')
            # html = response.body.decode('utf-8')
            tree = etree.HTML(response.body.decode('utf-8'))
            # 判断列表页是否有数据
            data = tree.xpath('//div[@id="container_base"]')
            if data:
                # tree = etree.HTML(response.body.decode('utf-8'))
                # 汽车信息盒子列表
                li_list = tree.xpath('//div[@id="container_base"]/ul/li')
                print("-------",len(li_list))
                for li in li_list:
                    # 实例化item
                    item = TaocheItem()
                    # -----列表页
                    # 标题
                    title = li.xpath('./div[@class="gongge_main"]/a/span/text()')[0]
                    print("-------",title)
                    # 上牌日期
                    reg_date = li.xpath('./div[@class="gongge_main"]/p/i[1]/text()')[0]
                    print("-------", reg_date)
                    # 公里数
                    mile = li.xpath('./div[@class="gongge_main"]/p/i[2]/text()')[0]
                    print("-------", mile)
                    # 优惠价格
                    price = li.xpath('.//div[@class="price"]/i[@class="Total brand_col"]//text()')
                    price = ''.join(price)
                    print("-------", price)
                    # 全款价格
                    all_price = li.xpath('.//div[@class="price"]/i[@class="onepaynor"]/text()')[0]
                    all_price = re.findall(r'原价(.*)',all_price)[0]
                    print("-------", all_price)
                    # 详情页url
                    detail_url = li.xpath('./div[@class="gongge_main"]/a/@href')[0]
                    detail_url = 'https:' + detail_url
                    print("-------", detail_url)
    
                    # item数据
                    item['title'] = title
                    item['reg_date'] = reg_date
                    item['mile'] = mile
                    item['price'] = price
                    item['all_price'] = all_price
                    item['detail_url'] = detail_url
    
                    yield scrapy.Request(
                        url=detail_url,
                        callback=self.parse_detail,
                        meta={'data':item},
                        dont_filter=False
                    )
                else:
                    pass
    
        def parse_detail(self, response):
            # 继承上面的item
            item = response.meta['data']
            # with open('car_detail.html','w',encoding='utf-8')as fp:
            #     fp.write(response.body.decode('utf-8'))
            tree = etree.HTML(response.body.decode('utf-8'))
            # 城市名称
            city_name = tree.xpath('//div[@class="summary-attrs"]/dl[last()]/dd/text()')[0]
            print("-------", city_name)
            # 图片
            pic = tree.xpath('//ul[@id="taoche-details-piclist"]/li/img/@data-src')
            # print("-------", pic)
            # 排量
            displace = tree.xpath('//div[@class="summary-attrs"]/dl[3]/dd/text()')[0]
            displace = re.findall(r'(.*?)/.*?',displace)[0]
            print("-------", displace)
            # 车源号
            source_id = tree.xpath('//span[@class="car-number"]/text()')[0]
            source_id = re.findall(r'车源号:(\d+)',source_id)[0]
            print("-------", source_id)
    
            item['city_name'] = city_name
            item['pic'] = pic
            item['displace'] = displace
            item['source_id'] = source_id
            item['name'] = 'zz'
            yield item
    

    3、启动爬虫:(尽量多个从机slaver同时运行爬虫,一般从机使用的程序脚本都是同样的代码,都是有一个人进行操作多台电脑进行的)

    scrapy crawl s_taoche
    

    日志消息截取:

    2019-09-05 20:22:07 [scrapy.core.scraper] DEBUG: Scraped from <200 https://www.taoche.com/buycar/b-dealerydg229090677t
    .html?source=2808>
    {'all_price': '6.52万',
     'city_name': '信阳',
     'detail_url': 'https://www.taoche.com/buycar/b-dealerydg229090677t.html?source=2808',
     'displace': '1.8L',
     'mile': '6.27万公里',
     'name': 'zz',
     'pic': ['//img8.taoche.cn/1j/b555595e-10190dr111.jpg',
             '//img8.taoche.cn/1j/66e9da68-10190dr1es.jpg',
             '//img8.taoche.cn/1j/1d5e781f-10190dr12f.jpg',
             '//img8.taoche.cn/1j/d81fb097-10190dr11f.jpg',
             '//img8.taoche.cn/1j/f4cc9363-10190dr1d2.jpg',
             '//img8.taoche.cn/1j/b8f1350a-10190dr133.jpg',
             '//img8.taoche.cn/1j/57c478c4-10190dr1lg.jpg',
             '//img8.taoche.cn/1j/4899f89a-10190dr1ii.jpg',
             '//img8.taoche.cn/1j/e79e46cc-10190dr1j2.jpg',
             '//img8.taoche.cn/1j/00ea278a-10190dr1jt.jpg',
             '//img8.taoche.cn/1j/ab187361-10190dr1jy.jpg',
             '//img8.taoche.cn/1j/4a66e0a9-10190dr0im.jpg',
             '//img8.taoche.cn/1j/031ebe07-10190dr0j4.jpg',
             '//img8.taoche.cn/1j/70c8758f-10190dr0k9.jpg',
             '//img8.taoche.cn/1j/31af4c14-10190dr0ju.jpg',
             '//img8.taoche.cn/1j/6e98df85-10190dr0u7.jpg',
             '//img8.taoche.cn/1j/de482adf-10190dr0un.jpg',
             '//img8.taoche.cn/1j/e2316c5b-10190dr15u.jpg',
             '//img8.taoche.cn/1j/186afbbf-10190dr0hl.jpg',
             '//img8.taoche.cn/1j/6a4e5dcb-10190dr1h7.jpg',
             '//img8.taoche.cn/1j/241d2ead-10190dr12o.jpg',
             '//img8.taoche.cn/1j/4504536a-10190dr1fu.jpg',
             '//img8.taoche.cn/1j/158fd0d7-10190dr1cf.jpg',
             '//img8.taoche.cn/1j/cc3a79c3-10190dr1hw.jpg',
             '//img8.taoche.cn/1j/9e5127ad-10190dr0gj.jpg'],
     'price': '6.29万',
     'reg_date': '2017年',
     'source_id': '020688064',
     'title': '东风风光风光580 2016款 1.8L 手动 舒适版'}
    ------- 重庆
    ------- 1.8L
    ------- 020683412
    ==============================
    2019-09-05 20:22:07 [scrapy.core.scraper] DEBUG: Scraped from <200 https://www.taoche.com/buycar/b-dealerydg228940722t
    .html?source=2808>
    {'all_price': '6.42万',
     'city_name': '重庆',
     'detail_url': 'https://www.taoche.com/buycar/b-dealerydg228940722t.html?source=2808',
     'displace': '1.8L',
     'mile': '3.61万公里',
     'name': 'zz',
     'pic': ['//img5.taoche.cn/1j/387d1be4-10190dpssy.jpg',
             '//img5.taoche.cn/1j/ebb12a45-10190dpszz.jpg',
             '//img5.taoche.cn/1j/a7c54403-10190dpst7.jpg',
             '//img5.taoche.cn/1j/190b53c0-10190dpst3.jpg',
             '//img5.taoche.cn/1j/e3cabc3d-10190dpsxt.jpg',
             '//img5.taoche.cn/1j/21859af8-10190dpstb.jpg',
             '//img5.taoche.cn/1j/7fa07cb6-10190dptdl.jpg',
             '//img5.taoche.cn/1j/c59d0791-10190dptc9.jpg',
             '//img5.taoche.cn/1j/7a83f37d-10190dptc3.jpg',
             '//img5.taoche.cn/1j/15d3483a-10190dptcc.jpg',
             '//img5.taoche.cn/1j/f8624dd2-10190dptc5.jpg',
             '//img5.taoche.cn/1j/51109bd7-10190dpta8.jpg',
             '//img5.taoche.cn/1j/fe31cbbb-10190dpsl5.jpg',
             '//img5.taoche.cn/1j/d73c76dd-10190dptaq.jpg',
             '//img5.taoche.cn/1j/4993dcdb-10190dptau.jpg',
             '//img5.taoche.cn/1j/28654a8e-10190dpskz.jpg',
             '//img5.taoche.cn/1j/ca614179-10190dpsl3.jpg',
             '//img5.taoche.cn/1j/0c395dd8-10190dpstw.jpg',
             '//img5.taoche.cn/1j/7f4c0e6b-10190dpt89.jpg',
             '//img5.taoche.cn/1j/77f69a05-10190dpt75.jpg',
             '//img5.taoche.cn/1j/7b5fa6e4-10190dpstj.jpg',
             '//img5.taoche.cn/1j/2814eb54-10190dpszu.jpg',
             '//img5.taoche.cn/1j/2483fcab-10190dpsxk.jpg',
             '//img5.taoche.cn/1j/9036e828-10190dpt8d.jpg',
             '//img5.taoche.cn/1j/18f55630-10190dpsly.jpg'],
     'price': '6.18万',
     'reg_date': '2017年',
     'source_id': '020683412',
     'title': '东风风光风光580 2016款 1.8L 手动 舒适版'}
    2019-09-05 20:22:07 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://www.taoche.com/buycar/b-dealerydg229090961t
    .html?source=2808> (referer: https://zsj.taoche.com/dongfengfengguang/?page=34)
    ------- 黔西南
    ------- 1.8L
    ------- 020687925
    ==============================
    2019-09-05 20:22:07 [scrapy.core.scraper] DEBUG: Scraped from <200 https://www.taoche.com/buycar/b-dealerydg229090961t
    .html?source=2808>
    {'all_price': '8.48万',
     'city_name': '黔西南',
     'detail_url': 'https://www.taoche.com/buycar/b-dealerydg229090961t.html?source=2808',
     'displace': '1.8L',
     'mile': '1.29万公里',
     'name': 'zz',
     'pic': ['//img8.taoche.cn/1j/a0f3191c-10190dr6kc.jpg',
             '//img8.taoche.cn/1j/51b59cbd-10190dr6pq.jpg',
             '//img8.taoche.cn/1j/a15aa5f9-10190dr6mp.jpg',
             '//img8.taoche.cn/1j/6270abb1-10190dr6nn.jpg',
             '//img8.taoche.cn/1j/699d154b-10190dr6oe.jpg',
             '//img8.taoche.cn/1j/e21eeaa4-10190dr6m1.jpg',
             '//img8.taoche.cn/1j/ae4c5772-10190dr6mu.jpg',
             '//img8.taoche.cn/1j/1cb63081-10190dr6lb.jpg',
             '//img8.taoche.cn/1j/bb19e020-10190dr6n1.jpg',
             '//img8.taoche.cn/1j/6a0ab008-10190dr6lu.jpg',
             '//img8.taoche.cn/1j/da4637c8-10190dr6ly.jpg',
             '//img8.taoche.cn/1j/dc00aac0-10190dr6nv.jpg',
             '//img8.taoche.cn/1j/9cd79b92-10190dr6mz.jpg',
             '//img8.taoche.cn/1j/71a8ebfe-10190dr6na.jpg',
             '//img8.taoche.cn/1j/cc2d107b-10190dr6ln.jpg',
             '//img8.taoche.cn/1j/90189e12-10190dr6of.jpg',
             '//img8.taoche.cn/1j/fe7b4739-10190dr6o4.jpg',
             '//img8.taoche.cn/1j/65fee368-10190dr6nb.jpg',
             '//img8.taoche.cn/1j/f96b7470-10190dr6nr.jpg',
             '//img8.taoche.cn/1j/5c622e3a-10190dr6mx.jpg',
             '//img8.taoche.cn/1j/9d009de2-10190dr6ml.jpg',
             '//img8.taoche.cn/1j/909c2a5d-10190dr6k5.jpg',
             '//img8.taoche.cn/1j/3bcc7984-10190dr6o8.jpg',
             '//img8.taoche.cn/1j/13a49dc6-10190dr6ma.jpg',
             '//img8.taoche.cn/1j/bc7f3ba0-10190dr6mf.jpg'],
     'price': '8.17万',
     'reg_date': '2018年',
     'source_id': '020687925',
     'title': '东风风光风光580 2016款 1.8L 手动 舒适版'}
    2019-09-05 20:22:07 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://www.taoche.com/buycar/b-dealerydg229090967t
    .html?source=2808> (referer: https://zsj.taoche.com/dongfengfengguang/?page=34)
    ------- 西安
    ------- 1.8L
    ------- 020687925
    ==============================
    2019-09-05 20:22:07 [scrapy.core.scraper] DEBUG: Scraped from <200 https://www.taoche.com/buycar/b-dealerydg229090967t
    .html?source=2808>
    {'all_price': '8.50万',
     'city_name': '西安',
     'detail_url': 'https://www.taoche.com/buycar/b-dealerydg229090967t.html?source=2808',
     'displace': '1.8L',
     'mile': '1.29万公里',
     'name': 'zz',
     'pic': ['//img6.taoche.cn/1j/a0f3191c-10190dr6kc.jpg',
             '//img6.taoche.cn/1j/51b59cbd-10190dr6pq.jpg',
             '//img6.taoche.cn/1j/a15aa5f9-10190dr6mp.jpg',
             '//img6.taoche.cn/1j/6270abb1-10190dr6nn.jpg',
             '//img6.taoche.cn/1j/699d154b-10190dr6oe.jpg',
             '//img6.taoche.cn/1j/e21eeaa4-10190dr6m1.jpg',
             '//img6.taoche.cn/1j/ae4c5772-10190dr6mu.jpg',
             '//img6.taoche.cn/1j/1cb63081-10190dr6lb.jpg',
             '//img6.taoche.cn/1j/bb19e020-10190dr6n1.jpg',
             '//img6.taoche.cn/1j/6a0ab008-10190dr6lu.jpg',
             '//img6.taoche.cn/1j/da4637c8-10190dr6ly.jpg',
             '//img6.taoche.cn/1j/dc00aac0-10190dr6nv.jpg',
             '//img6.taoche.cn/1j/9cd79b92-10190dr6mz.jpg',
             '//img6.taoche.cn/1j/71a8ebfe-10190dr6na.jpg',
             '//img6.taoche.cn/1j/cc2d107b-10190dr6ln.jpg',
             '//img6.taoche.cn/1j/90189e12-10190dr6of.jpg',
             '//img6.taoche.cn/1j/fe7b4739-10190dr6o4.jpg',
             '//img6.taoche.cn/1j/65fee368-10190dr6nb.jpg',
             '//img6.taoche.cn/1j/f96b7470-10190dr6nr.jpg',
             '//img6.taoche.cn/1j/5c622e3a-10190dr6mx.jpg',
             '//img6.taoche.cn/1j/9d009de2-10190dr6ml.jpg',
             '//img6.taoche.cn/1j/909c2a5d-10190dr6k5.jpg',
             '//img6.taoche.cn/1j/3bcc7984-10190dr6o8.jpg',
             '//img6.taoche.cn/1j/13a49dc6-10190dr6ma.jpg',
             '//img6.taoche.cn/1j/bc7f3ba0-10190dr6mf.jpg'],
     'price': '8.19万',
     'reg_date': '2018年',
     'source_id': '020687925',
     'title': '东风风光风光580 2016款 1.8L 手动 舒适版'}
    2019-09-05 20:22:07 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://www.taoche.com/buycar/b-dealerydg229090974t
    .html?source=2808> (referer: https://zsj.taoche.com/dongfengfengguang/?page=34)
    ------- 平顶山
    ------- 1.8L
    ------- 020687925
    ==============================
    2019-09-05 20:22:07 [scrapy.core.scraper] DEBUG: Scraped from <200 https://www.taoche.com/buycar/b-dealerydg229090974t
    .html?source=2808>
    {'all_price': '8.56万',
     'city_name': '平顶山',
     'detail_url': 'https://www.taoche.com/buycar/b-dealerydg229090974t.html?source=2808',
     'displace': '1.8L',
     'mile': '1.29万公里',
     'name': 'zz',
     'pic': ['//img5.taoche.cn/1j/a0f3191c-10190dr6kc.jpg',
             '//img5.taoche.cn/1j/51b59cbd-10190dr6pq.jpg',
             '//img5.taoche.cn/1j/a15aa5f9-10190dr6mp.jpg',
             '//img5.taoche.cn/1j/6270abb1-10190dr6nn.jpg',
             '//img5.taoche.cn/1j/699d154b-10190dr6oe.jpg',
             '//img5.taoche.cn/1j/e21eeaa4-10190dr6m1.jpg',
             '//img5.taoche.cn/1j/ae4c5772-10190dr6mu.jpg',
             '//img5.taoche.cn/1j/1cb63081-10190dr6lb.jpg',
             '//img5.taoche.cn/1j/bb19e020-10190dr6n1.jpg',
             '//img5.taoche.cn/1j/6a0ab008-10190dr6lu.jpg',
             '//img5.taoche.cn/1j/da4637c8-10190dr6ly.jpg',
             '//img5.taoche.cn/1j/dc00aac0-10190dr6nv.jpg',
             '//img5.taoche.cn/1j/9cd79b92-10190dr6mz.jpg',
             '//img5.taoche.cn/1j/71a8ebfe-10190dr6na.jpg',
             '//img5.taoche.cn/1j/cc2d107b-10190dr6ln.jpg',
             '//img5.taoche.cn/1j/90189e12-10190dr6of.jpg',
             '//img5.taoche.cn/1j/fe7b4739-10190dr6o4.jpg',
             '//img5.taoche.cn/1j/65fee368-10190dr6nb.jpg',
             '//img5.taoche.cn/1j/f96b7470-10190dr6nr.jpg',
             '//img5.taoche.cn/1j/5c622e3a-10190dr6mx.jpg',
             '//img5.taoche.cn/1j/9d009de2-10190dr6ml.jpg',
             '//img5.taoche.cn/1j/909c2a5d-10190dr6k5.jpg',
             '//img5.taoche.cn/1j/3bcc7984-10190dr6o8.jpg',
             '//img5.taoche.cn/1j/13a49dc6-10190dr6ma.jpg',
             '//img5.taoche.cn/1j/bc7f3ba0-10190dr6mf.jpg'],
     'price': '8.25万',
     'reg_date': '2018年',
     'source_id': '020687925',
     'title': '东风风光风光580 2016款 1.8L 手动 舒适版'}
    

    在这里插入图片描述

    展开全文
  • Python爬虫实例教程讲师日月光华Scrapy分布式爬虫答疑群945189407分布式爬虫分布式爬虫也就是集群爬虫当遇到大型的爬虫任务单台计算机是难以满足抓取需求的毕竟单机有CPUIO带宽等多重限制可以尝试分布式爬虫扩展性能...
  • 分布式爬虫:爬虫共用同一个爬虫程序,即把同一个爬虫程序同时部署到多台电脑上运行,这样可以提高爬虫速度。 在默认情况下,scrapy爬虫是单机爬虫,只能在一台电脑上运行,因为爬虫调度器当中的队列queue去重和set...

    分布式爬虫:爬虫共用同一个爬虫程序,即把同一个爬虫程序同时部署到多台电脑上运行,这样可以提高爬虫速度。

    在默认情况下,scrapy爬虫是单机爬虫,只能在一台电脑上运行,因为爬虫调度器当中的队列queue去重和set集合都是在本机上创建的,其他的电脑无法访问另外一台电脑上的内存的内容;想要让多台机器共用一个queue队列和set集合,可以让scrapy结合scrapy_redis来完成。

    要实现分布式爬虫,首先要配置服务器主从:

    配置主从的目的:

    1、达到一个备份的功能,一旦master出现崩溃,而数据库中还有数据的话

    可以将其中的一个slave重新设置为主服务器,从而恢复Redis的正常运行

    2、一个Redis服务器负责读写,性能较低,通过主从来减轻一个Redis的压力

    下面通过爬伯乐在线来介绍具体怎么实现:

    第一步:

    在爬虫文件中引入RedisCrawSpider和Rule

    from scrapy_redis.spiders import RedisCrawlSpider
    from scrapy.spiders import Rule
    
    #让类继承RedisCrawSpider
    class BlogSpider(RedisCrawlSpider):
        name = 'blog'
        #允许爬虫的范围
        allowed_domains = ['blog.jobbole.com']
        #用于查找所有符合给定模式的key
        redis_key = 'blogspider:start_urls'
    

    第二步:

    完成需求,我的需求:获取所有文章的标题 图片地址 时间 详情页地址 收藏 点赞 评论

    引入scrapy

    import scrapy
    

    获取数据:

        def parse(self, response):
            div_list = response.xpath('//div[@class="post floated-thumb"]')
            for div in div_list:
                img = div.xpath('.//div[@class="post-thumb"]/a/img/@src').extract_first('')
                title_url = div.xpath('.//div[@class="post-meta"]/p/a/@href').extract_first('')
                yield scrapy.Request(url=title_url,meta={'img':img},callback=self.get_detail_with_url)
     
        def get_detail_with_url(self,response):
            img = response.meta['img']
            detail_url = response.url
            title = response.xpath('//div[@class="entry-header"]/h1/text()').extract_first('')
            dian_zan = response.xpath('//div[@class="post-adds"]/span/h10/text()').extract_first('')
            book_mark = response.xpath('//div[@class="post-adds"]/span[@class=" btn-bluet-bigger href-style bookmark-btn  register-user-only "]/text()').extract_first('')
            book_mark = book_mark.split(' ')[1]
            book_num = 0
            if len(book_mark) != 0:
                book_num = int(book_mark)
            comment = response.xpath('//div[@class="post-adds"]/a/span/text()').extract_first('')
            comment = comment.split(' ')[1]
            comment_num = 0
            if len(comment) != 0:
                comment_num = int(comment)
            time = response.xpath('//div[@class="entry-meta"]/p/text()').extract_first('')
    

     第三步:

    将值传入items.py(存放数据模型)中

    在items.py中写入要获得的数据

    import scrapy
     
     
    class JobboleItem(scrapy.Item):
        # define the fields for your item here like:
        # name = scrapy.Field()
        img = scrapy.Field()
        title = scrapy.Field()
        time = scrapy.Field()
        detail_url = scrapy.Field()
        dian_zan = scrapy.Field()
        book_mark = scrapy.Field()
        comment = scrapy.Field()
    

    在爬虫文件中引入items.py中的类名

    from ..items import JobboleItem
    

     在第二步代码的最后实例化item,并传值

            item = JobboleItem()
            item['img'] = [img]
            item['title'] = title
            item['time'] = time
            item['detail_url'] = detail_url
            item['dian_zan'] = dian_zan
            item['book_mark'] = book_num
            item['comment'] = comment_num
            yield item
    

    第四步:

    设置settings.py

    # 使用scrapy_redis的去重类 不使用scrapy默认的去重类
    DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
     
    # 使用scrapy_redis的调度器 不使用scrapy默认的调度器
    SCHEDULER = "scrapy_redis.scheduler.Scheduler"
     
    # 控制爬虫是否允许暂停
    SCHEDULER_PERSIST = True
     
    ITEM_PIPELINES = {
       'jobbole.pipelines.JobbolePipeline': 300,
        # 'jobbole.pipelines.jobboleDownImage':1,
        'scrapy_redis.pipelines.RedisPipeline': 400,
    }
     
    # 域名为字符串  如果不写  默认为本机  数据库的ip
    # 注意:若为master这里的域名应该写本机的域名,我这里写127.0.0.1来代替
           若为slave这里写master的域名
    REDIS_HOST = '127.0.0.1'
    # 端口为数字
    REDIS_PORT = 6379
    

     

    第五步:

    master打开服务器(注意:服务器不能关)

     master和slave一起运行爬虫程序

    重新打开一个终端

    运行之后,master和slave会一起停留,等待命令

     这里再打开一个终端(红色部分为master的ip地址)

    master和slave运行爬虫程序的终端将会继续执行

     这样就实现了分布式爬虫

     

    展开全文
  • scrapy分布式爬虫Scrapy-Redis分布式爬虫组件分布式爬虫的优点分布式爬虫必须要解决的问题Scrapy-Redis架构图redis安装 Scrapy-Redis分布式爬虫组件 Scrapy是一个框架,他本身是不支持分布式的。如果我们想要做...

    一:Scrapy-Redis分布式爬虫组件

    Scrapy是一个框架,他本身是不支持分布式的。如果我们想要做分布式的爬虫,就需要借助一个组件叫做Scrapy-Redis,这个组件正是利用了Redis可以分布式的功能,集成到Scrapy框架中,使得爬虫可以进行分布式。可以充分的利用资源(多个ip、更多带宽、同步爬取)来提高爬虫的爬行效率。

    为什么原生的scrapy不可以实现分布式?

    1. 调度器不可以被分布式集群共享
    2. 管道不可以被分布式集群共享

    scrapy-redis组件作用:

    1. 可以给原生的scrapy框架提供可以被共享的管道和调度器

    二:分布式爬虫的优点

    可以充分利用多台机器的带宽
    可以充分利用多台机器的ip地址
    多台机器做,爬取效率更高

    三:分布式爬虫必须要解决的问题

    分布式爬虫是好几台机器在同时运行,如何保证不同的机器爬取页面的时候不会出现重复爬取的问题
    分布式爬虫在不同的机器上运行,在把数据爬完后如何保证保存在同一个地方

    四:Scrapy-Redis架构图

    Scrapy-Redis架构图

    五:redis安装

    https://blog.csdn.net/pcn01/article/details/104516047
    

    六:分布式爬虫改造

    6.1 scrapy基础爬虫

    (crawler) F:\WWWROOT\crawler>scrapy startproject fang
    (crawler) F:\WWWROOT\crawler>cd fang
    (crawler) F:\WWWROOT\crawler\fang>scrapy genspider sofang "fang.com"
    

    settings配置文件:

    ROBOTSTXT_OBEY = False
    DOWNLOAD_DELAY = 3
    DOWNLOADER_MIDDLEWARES = {
       # 'fang.middlewares.FangDownloaderMiddleware': 543,
       'fang.middlewares.UserAgentDownloadMiddleware': 543,  # 使用自定义的随机请求头中间件
    }
    ITEM_PIPELINES = {
       'fang.pipelines.FangPipeline': 300,
    }
    LOG_LEVEL = "WARNING"
    

    随机请求头middlewares.py文件:

    import random
    class UserAgentDownloadMiddleware(object):
        # user-agent随机请求头中间件
        USER_AGENT = [
            'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36',
            'Mozilla/5.0 (Macintosh; U; PPC Mac OS X; pl-PL; rv:1.0.1) Gecko/20021111 Chimera/0.6',
            'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1',
            'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/17.17134',
            'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.41 Safari/535.1 QQBrowser/6.9.11079.201',
            'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17 SE 2.X MetaSr 1.0',
        ]
        def process_request(self, request, spider):
            user_agent = random.choice(self.USER_AGENT)
            request.headers['User-Agent'] = user_agent
    

    设置字段items.py文件

    import scrapy
    # 此次爬取每个城市的新房与二手房信息
    class NewHouseItem(scrapy.Item):
        province = scrapy.Field()       # 省份
        city = scrapy.Field()           # 城市
        name = scrapy.Field()           # 小区的名字
        price = scrapy.Field()          # 价格
        rooms = scrapy.Field()          # 几居室
        area = scrapy.Field()           # 面积
        address = scrapy.Field()        # 地址
        district = scrapy.Field()       # 行政区
        sale = scrapy.Field()           # 是否在售
        origin_url = scrapy.Field()     # 详情页面url
    
    class ESFHouseItem(scrapy.Item):
        province = scrapy.Field()       # 省份
        city = scrapy.Field()           # 城市
        name = scrapy.Field()           # 小区的名字
        price = scrapy.Field()          # 价格
        rooms = scrapy.Field()          # 几居室
        area = scrapy.Field()           # 面积
        address = scrapy.Field()        # 地址
        origin_url = scrapy.Field()     # 详情页面url
        floor = scrapy.Field()
        toward = scrapy.Field()
        year = scrapy.Field()
        unit = scrapy.Field()
    

    sofang.py文件内容如下:

    import scrapy
    import re
    from fang.items import NewHouseItem, ESFHouseItem
    
    class SofangSpider(scrapy.Spider):
        name = 'sofang'
        allowed_domains = ['fang.com']
        start_urls = ['https://www.fang.com/SoufunFamily.htm']
    
        def parse(self, response):
            trs = response.xpath('//div[@class="outCont"]//tr')
            province = None
            for tr in trs:
                tds = tr.xpath('.//td[not(@class)]')
                province_td = tds[0]
                province_text = province_td.xpath('.//text()').get()
                province_text = re.sub(r'\s', '', province_text)
                if province_text:
                    province = province_text
    
                if province == '其它': continue
                city_td = tds[1]
                city_links = city_td.xpath('.//a')
                for city_link in city_links:
                    city = city_link.xpath('.//text()').get()
                    city_url = city_link.xpath('.//@href').get()
    
                    url_module = city_url.split('//')
                    scheme = url_module[0]
                    domain = url_module[1]
    
                    if 'bj.' in domain:
                        newhouse_url = 'http://newhouse.fang.com/house/s/'
                        esf_url = 'http://esf.fang.com/'
                    else:
                        newhouse_url = scheme + '//' + 'newhouse.' + domain + 'house/s/'
                        esf_url = scheme + '//' + 'esf.' + domain
    
                    yield scrapy.Request(url = newhouse_url, callback = self.parse_newhouse, meta = {"info": (province, city)})
                    yield scrapy.Request(url = esf_url, callback = self.parse_esf, meta = {"info": (province, city)})
    
        def parse_newhouse(self, response):
            province, city = response.meta.get('info')
            lis = response.xpath('//div[contains(@class, "nl_con")]/ul/li[position() != 6]')
            for li in lis:
                name = li.xpath('.//div[@class="nlcd_name"]/a/text()').get().strip()
                house_type_list = li.xpath('.//div[contains(@class, "house_type")]/a/text()').getall()
                house_type_list = list(map(lambda x: re.sub(r'\s', '', x), house_type_list))
                rooms = list(filter(lambda x: x.endswith('居'), house_type_list))
                area = ''.join(li.xpath('.//div[contains(@class, "house_type")]/text()').getall())
                area = re.sub(r'\s|-|-|/', '', area)
                address = li.xpath('.//div[@class="address"]/a/@title').get()
                district_text = ''.join(li.xpath('.//div[@class="address"]/a//text()').getall())
                district = re.search(r'.*\[(.+)\].*', district_text).group(1)
                sale = li.xpath('.//div[contains(@class, "fangyuan")]/span/text()').get()
                price = ''.join(li.xpath('.//div[@class="nhouse_price"]//text()').getall())
                price = re.sub(r'\s|广告', '', price)
                origin_url = response.urljoin(li.xpath('.//div[@class="nlcd_name"]/a/@href').get())
                item = NewHouseItem(
                    name = name,
                    rooms = rooms,
                    area = area,
                    address = address,
                    district = district,
                    sale = sale,
                    price = price,
                    origin_url = origin_url,
                    province = province,
                    city = city
                )
                yield item
            next_url = response.xpath('//div[@class="page"]//a[@class="next"]/@href').getall()
            yield scrapy.Request(url = response.urljoin(next_url), callback = self.parse_newhouse, meta = {'info': (province, city)})
    
        def parse_esf(self, response):
            province, city = response.meta.get('info')
            dls = response.xpath('//div[contains(@class, "shop_list")]//dl[contains(@dataflag, "bg")]')
            for dl in dls:
                item = ESFHouseItem(province = province, city = city)
    
                item['name'] = dl.xpath('.//p[@class="add_shop"]/a/text()').get().strip()
                item['address'] = dl.xpath('.//p[@class="add_shop"]//span/text()').get().strip()
    
                infos = dl.xpath('.//p[@class="tel_shop"]//text()').getall()
                infos = list(map(lambda x:re.sub(r'\s|\|', '', x), infos))
                for info in infos:
                    if '厅' in info:
                        item['rooms'] = info
                    elif '层' in info:
                        item['floor'] = info
                    elif '向' in info:
                        item['toward'] = info
                    elif '建' in info:
                        item['year'] = info
                    elif '㎡' in info:
                        item['area'] = info
                item['price'] = ''.join(dl.xpath('.//span[@class="red"]//text()').getall())
                item['unit'] = dl.xpath('.//dd[@class="price_right"]//span[2]/text()').get()
                item['origin_url'] = response.urljoin(dl.xpath('.//h4[@class="clearfix"]/a/@href').get())
                yield item
    
            for i in range(32,42):
                next_url = response.url + 'house/i' + str(i)
                yield scrapy.Request(next_url, callback = self.parse_esf, meta = {'info': (province, city)})
    

    pipelines.py文件内容如下:

    from scrapy.exporters import JsonLinesItemExporter
    
    class FangPipeline(object):
    
        def __init__(self):
            self.newhouse_fp = open('newhouse.json', 'wb')
            self.esfhouse_fp = open('esfhouse.json', 'wb')
            self.newhouse_exporter = JsonLinesItemExporter(
                self.newhouse_fp, ensure_ascii = False
            )
            self.esfhouse_exporter = JsonLinesItemExporter(
                self.esfhouse_fp, ensure_ascii = False
            )
    
        def process_item(self, item, spider):
            self.newhouse_exporter.export_item(item)
            self.esfhouse_exporter.export_item(item)
            return item
    
        def close_spider(self, spider):
            self.newhouse_fp.close()
            self.esfhouse_fp.close()
    

    这里首先将数据保存到两个json文件中。运行没问题后将上面的爬虫代码改成分布式

    6.2 分布式爬虫

    pipenv install scrapy-redis  # 安装scrapy-redis
    
    1. 将爬虫的类从scrapy.Spider变成scrapy_redis.spiders.RedisSpider;或者是从scrapy.CrawlSpider变成scrapy_redis.spiders.RedisCrawlSpider
    2. 将爬虫中的start_urls删掉。增加一个redis_key="xxx"。这个redis_key是为了以后在redis中控制爬虫启动的。爬虫的第一个url,就是在redis中通过这个发送出去的。
    3. 在配置文件中增加如下配置
        # Scrapy-Redis相关配置
        # 确保request存储到redis中
        SCHEDULER = "scrapy_redis.scheduler.Scheduler"
        # 确保所有爬虫共享相同的去重指纹
        DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
        # 设置redis为item pipeline
        ITEM_PIPELINES = {
            'scrapy_redis.pipelines.RedisPipeline': 300
        }
        # 在redis中保持scrapy-redis用到的队列,不会清理redis中的队列,从而可以实现暂停和恢复的功能。
        SCHEDULER_PERSIST = True
        # 设置连接redis信息
        REDIS_HOST = '172.17.2.231'
        REDIS_PORT = 6379
        REDIS_PASSWORD = 123456
    

    需要修改的文件只有一个sofang.py文件:

    import scrapy
    import re
    from fang.items import NewHouseItem, ESFHouseItem
    from scrapy_redis.spiders import RedisSpider		# 导入RedisSpider模块
    
    class SofangSpider(RedisSpider):
        name = 'sofang'
        allowed_domains = ['fang.com']
        # start_urls = ['https://www.fang.com/SoufunFamily.htm']
        redis_key = 'fang:start_url'			# 添加 redis_key变量
    

    运行爬虫:

    1. Redis服务器上,推入一个开始的url链接
    127.0.0.1:6379> lpush fang:start_urls https://www.fang.com/SoufunFamily.htm
    
    1. 在爬虫服务器上。进入爬虫文件所在的路径,然后输入命令:scrapy runspider [爬虫文件名],(进入到sofang.py文件所在目录运行)

    上面的代码在win服务器在可以正常跑来,redis服务器与爬虫代码不在同一台服务器上;但在linux内核下还有问题,先记着。

    七:分布式爬虫案例

    下面以爬取wz.sun0769.com为例,完整案例源码见:

    https://blog.csdn.net/pcn01/article/details/106219763
    

    scrapy分布式爬虫实现流程:
    1:创建一个工程

    scrapy startproject cluster
    

    2:创建一个基于CrawlSpider的爬虫文件

    scrapy genspider -t crawl fds www.xxx.com
    

    3:修改爬虫文件

    1. 导包:from scrapy_redis.spiders import RedisCrawlSpider
    2. 将start_urls和allowed_domains注释掉
    3. 添加一个新属性:redis_key = ‘sun’,可以被共享的调度器队列的名称,名称可随意取
    4. 编写数据解析相关操作
    5. 将当前爬虫的父类修改为RedisCrawlSpider
    # -*- coding: utf-8 -*-
    import scrapy
    from scrapy.linkextractors import LinkExtractor
    from scrapy.spiders import CrawlSpider, Rule
    from scrapy_redis.spiders import RedisCrawlSpider
    from cluster.items import ClusterItem
    from cluster.items import DetailItem
    
    class FdsSpider(RedisCrawlSpider):
        name = 'fds'
        # allowed_domains = ['www.xxx.com']
        # start_urls = ['http://www.xxx.com/']
        redis_key = 'sun'
        rules = (
            # 获取分页
            Rule(LinkExtractor(allow=r'id=1&page=\d+'), callback='parse_item', follow=True),
            # follow = True 可以将链接提取器继续作用到连接提取器提取到的链接所对应的页面中
            Rule(LinkExtractor(allow=r'id=\d+'), callback='parse_detail', follow=False)
        )
        # 如下两个解析方法中不可以实现请求传参,如传递meta;可以将数据保存到两个item中
        def parse_item(self, response):
            # xpath中不能出现tbody
            li_list = response.xpath('//ul[@class="title-state-ul"]/li')
            for li in li_list:
                detail_url = li.xpath('./span[@class="state3"]/a/@href').extract_first()
                # detail_url = urljoin(response.url, detail_url)
                title = li.xpath('./span[@class="state3"]/a/text()').extract_first()
                item = ClusterItem(title=title)
                yield item
        def parse_detail(self, response):
            new_id = response.xpath('//div[contains(@class, "focus-date-list")]/span[4]//text()').extract_first()
            item = DetailItem(new_id=new_id)
            yield item
    

    4:修改settings配置文件:指定使用可以被共享的管道,指定调度器,配置redis

    BOT_NAME = 'cluster'
    SPIDER_MODULES = ['cluster.spiders']
    NEWSPIDER_MODULE = 'cluster.spiders'
    USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_0) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11'
    ROBOTSTXT_OBEY = False
    # Scrapy-Redis相关配置
    # 确保request存储到redis中
    SCHEDULER = "scrapy_redis.scheduler.Scheduler"
    # 确保所有爬虫共享相同的去重指纹
    DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
    # 在redis中保持scrapy-redis用到的队列,不会清理redis中的队列,从而可以实现暂停和恢复的功能。
    SCHEDULER_PERSIST = True
    # 设置redis为item pipeline
    ITEM_PIPELINES = {
        'scrapy_redis.pipelines.RedisPipeline': 300
    }
    # 设置连接redis信息
    REDIS_HOST = '172.17.2.36'
    REDIS_PORT = 6379
    REDIS_PARAMS = {
        'password': 'xxxxx'
    }
    

    5:执行工程

    # 进入到fds.py文件所在目录,或写上全路径
    scrapy runspider fds.py
    

    6:向调度器的队列中放入一个起始的url

    127.0.0.1:10009> lpush sun http://wz.sun0769.com/political/index/politicsNewest?id=1&page=1
    

    八:增量爬虫

    将上面的案例改造成增量爬虫。主要是对详情页面的URL做了一个检测,利用了redis的set数据类型。

    1:爬虫文件内容

    import scrapy
    from scrapy.linkextractors import LinkExtractor
    from scrapy.spiders import CrawlSpider, Rule
    from SunPro.items import SunproItem
    import redis
    
    class SunSpider(CrawlSpider):
        name = 'sun'
        # allowed_domains = ['www.xxx.com']
        start_urls = ['http://wz.sun0769.com/political/index/politicsNewest?id=1&page=1']
        conn = redis.Redis(host='172.17.2.36', port=10009, db=0, password='BruceLee', charset='utf8', decode_responses=True)
        rules = (
            # 获取分页
            Rule(LinkExtractor(allow=r'id=1&page=\d+'), callback='parse_item', follow=True),
            # follow = True 可以将链接提取器继续作用到连接提取器提取到的链接所对应的页面中
            # Rule(LinkExtractor(allow=r'id=\d+'), callback='parse_detail', follow=False)
        )
        # 如下两个解析方法中不可以实现请求传参,如传递meta;可以将数据保存到两个item中
        def parse_item(self, response):
            # xpath中不能出现tbody
            li_list = response.xpath('//ul[@class="title-state-ul"]/li')
            for li in li_list:
                detail_url = 'http://wz.sun0769.com/' + li.xpath('./span[@class="state3"]/a/@href').extract_first()
                is_exists = self.conn.sadd('urls', detail_url)
                if is_exists == 1:
                    print('该url没有被爬取过,可以进行数据的爬取')
                    yield scrapy.Request(url=detail_url, callback=self.parse_detail)
                else:
                    print('数据还没有更新,暂无新数据可爬取')
    
        def parse_detail(self, response):
            title = response.xpath('//p[@class="focus-details"]//text()').extract_first()
            new_id = response.xpath('//div[contains(@class, "focus-date-list")]/span[4]//text()').extract_first()
            item = SunproItem(title=title, new_id=new_id)
            yield item
    

    2:items文件内容

    import scrapy
    class SunproItem(scrapy.Item):
        title = scrapy.Field()
        new_id = scrapy.Field()
    

    3:pipelines文件内容

    class SunproPipeline:
        conn = None
        def open_spider(self, spider):
            self.conn = spider.conn		# 获取爬虫文件中的redis实例
        def process_item(self, item, spider):
            dic = {
                'title': item['title'],
                'new_id': item['new_id']
            }
            # 这里注意使用的python redis模块的版本为redis==2.10.6
            # Python的第三方库redis升级到3.0后仅接受用户数据为字节、字符串或数字(整数,长整数和浮点数)。尝试将键或值指定为任何其他类型将引发DataError异常。
            print(dic)
            self.conn.lpush('sun_data', dic)
            return item
    
    展开全文
  • scrapy分布式爬虫框架搭建超级详细版,若需要源码,请到[源站联系站长](http://www.zhaoqiansunli.com.cn//a/meinv/20190912/192.html) 本篇针对的是python爬虫框架scrapy的分布式爬虫框架搭建问题,下面看具体步骤...

    注:本文系作者从其个人网站转载过来,源网址http://www.zhaoqiansunli.com.cn//a/meinv/20190912/192.html
    scrapy分布式爬虫框架搭建超级详细版,若需要源码,请到[源站联系站长](http://www.zhaoqiansunli.com.cn//a/meinv/20190912/192.html)

    本篇针对的是python爬虫框架scrapy的分布式爬虫框架搭建问题,下面看具体步骤。
    

    安装python。现在有2个版本的python,python2.7和python3.x,本文使用python2的版本,下载和安装地址https://www.python.org/downloads
    安装scrapy。直接使用pip工具进行安装,输入pip install scrapy
    安装scrapy-redis。也是直接使用pip工具安装,输入pip install scrapy-redis
    安装redis。也是直接pip工具安装,输入pip install redis
    安装scrapyd,也是直接pip安装,输入pip install scrapyd
    最好同时把mongo数据库组件和mysql组件也装上,mongo数据组件安装 pip install pymongo,mysql组件安装sudo apt-get install libmysqlclient-dev和pip install MySQL-Python
    安装redis服务,redis下载地址http://xiazai.jinyihulian.cn/xiazai/redis.tar.gz
    安装mongodb服务,mongodb下载地址http://xiazai.jinyihulian.cn/xiazai/mongodb.rar
    配置。1、spider部分
    name = ‘hdhdspider’
    redis_key = “hdhdspider:start_urls”
    rules=(
    Rule(LinkExtractor(restrict_xpaths=’.//[@class=“title”]’),callback=“parse_page”,follow=True),
    )#allow=r’/htm/movie\d+/[^\s]+.htm’),restrict_xpaths=".//
    [@class=“title”]"
    num=0
    def init(self, *args, **kwargs):
    # Dynamically define the allowed domains list.
    domain = kwargs.pop(‘domain’, ‘’)
    self.allowed_domains = filter(None, domain.split(’,’))
    super(HdhdspiderSpider, self).init(*args, **kwargs)

    2、setting.py部分
    DUPEFILTER_CLASS = “scrapy_redis.dupefilter.RFPDupeFilter”
    SCHEDULER = “scrapy_redis.scheduler.Scheduler”
    SCHEDULER_PERSIST = True
    #SCHEDULER_QUEUE_CLASS = “scrapy_redis.queue.SpiderPriorityQueue”
    #SCHEDULER_QUEUE_CLASS = “scrapy_redis.queue.SpiderQueue”
    #SCHEDULER_QUEUE_CLASS = “scrapy_redis.queue.SpiderStack”

    ITEM_PIPELINES = {
    #‘example.pipelines.ExamplePipeline’: 300,
    ‘scrapy_redis.pipelines.RedisPipeline’: 400,
    }
    REDIS_HOST = “192.168.13.26”
    REDIS_PORT = 6379
    LOG_LEVEL = ‘DEBUG’
    实例爬取百度新闻

    -- coding: utf-8 --

    import sys
    reload(sys)
    sys.setdefaultencoding(‘utf-8’)
    import scrapy
    import time,re
    from scrapy_redis.spiders import RedisSpider
    from mingan.items import bdtoprankitem
    from scrapy import Selector
    from spidertools import spiderTool
    class bdnewsSpider(RedisSpider):
    name = ‘bdnews’
    #start_urls = [‘http://tieba.baidu.com/f?ie=utf-8&kw=李晨吧&fr=search&red_tag=n3540397284’]#https://www.cnblogs.com/qiyeboy/default.html?page=1 http://ycddz.cn/
    redis_key = “bdnews:start_urls”#http://s.weibo.com/weibo/%25E7%25A7%2591%25E5%2588%259B%25E4%25BF%25A1%25E6%2581%25AF?topnav=1&wvr=6&b=1

    domain="http://jian.news.baidu.com/"#https://www.amazon.com/s/ref=sr_pg_1?me=ABB9OQDQJ01FR&rh=i%3Amerchant-items&ie=UTF8&qid=1522631980
    #http://jian.news.baidu.com/ 新版百度抓取地址
    
    def parse(self, response):#parse
        urls=["http://jian.news.baidu.com/"]
        for url in urls:
            yield scrapy.Request(url=url,meta={"flag":"bdnews"},callback=self.parse_content1,dont_filter=True)
            #yield scrapy.Request(url=nodeqaurl,meta={"id":node,},callback=self.parse_qafenye,dont_filter=False)
    def parse_content1(self,response):
        selector=Selector(response)
        nodes=selector.xpath("//div[@id='feeds']/div[@class='feed long']")
        for node in nodes:
            keyword=node.xpath("string(.)").extract_first()#
            if len(keyword)>10:
                keyword=keyword[0:20]
            #print keyword
    
            if keyword!=None:
                keyword=keyword.replace("评论","").replace("不感兴趣","");
                item=bdtoprankitem(index="bdtoprank",keyword=keyword)
                yield item
    11.启动方法,新建begin.py,输入  from scrapy import cmdline  cmdline.execute("scrapy crawl bdad".split()),运行该文件就行了。
    12、在redis客户端输入 lpush  bdnews:start_urls http://www.baidu.com即可开始运行。
    

    在这里插入图片描述
    在这里插入图片描述

    展开全文
  • 基于Scrapy分布式爬虫的开发与设计

    万次阅读 多人点赞 2018-04-27 17:11:07
    这个项目也是初窥python爬虫的一个项目,也是我的毕业设计,当时选题的时候,发现大多数人选择的都是网站类,实在是普通不过了,都是一些简单的增删查改,业务类的给人感觉一种很普通的系统设计,当时也刚好在知乎上...
  • [234]scrapy分布式爬虫scrapy-redis(一)

    千次阅读 2018-07-04 09:01:10
    分布式爬虫原理 首先我们来看一下scrapy的单机架构: 可以看到,scrapy单机模式,通过一个scrapy引擎通过一个调度器,将Requests队列中的request请求发给下载器,进行页面的爬取。 那么多台主机协作的关键是...
  • 作 者: lizhonglin ... ... 学了这么久的Scrapy框架,自己动手写了一个分布式爬虫.检验一下自己的学习成果.仅做学习技术参考。 主要功能介绍: (人人车二手车)renrenchesipder[项目源码] ...
  • 分布式爬虫搭建一个分布式的集群,让其对一组资源进行分布联合爬取,提升爬取效率如何实现分布式1.scrapy框架是否可以自己实现分布式?不可以!!!其一:因为多台机器上部署的scrapy会各自拥有各自的调度器,这样就...
  • 第一步:配置setting.py【这里非常重要,决定分布式爬虫的成败关键】 配置官方文档是最完善的:https://pypi.org/project/scrapy-redis/ 我的爬虫项目【scrapy_distributed】配置【一定要开启你的redis数据库-否则...
  • scrapy进行分布式爬虫

    2021-04-16 15:17:44
    一、分布式爬虫 1.概念: 我们需要搭建一个分布式的机群,让其对一组资源进行分布联合爬取。 2.作用: 提升爬取数据的效率 3.依赖: pip install scrapy-redis scrapy-redis作用:可以给原生的scrapy框架提供可以...
  • Python3网络爬虫教程18——分布式爬虫Scrapy实例(爬取一个页面) https://blog.csdn.net/u011318077/article/details/86692598 先补充几个常用命令; 7. Scrapy项目常用命令 先打开CMD命令,CD切换到要创建的项目的...
  • 4. xpath抓取实例 为什么要使用xpath? xpath使用路径表达式在xml和html中进行导航 xpath包含有一个标准函数库 xpath是一个w3c的标准 xpath速度要远远超beautifulsoup。 xpath节点关系 父节点 *上一...
  • scrapy简单分布式爬虫

    千次阅读 2019-08-14 15:53:41
    有能人改变了scrapy的队列调度,将起始的网址从start_urls里分离出来,改为从redis读取,多个客户端可以同时读取同一个redis,从而实现了分布式爬虫。就算在同一台电脑上,也可以多进程的运行爬虫,在大规模抓取的...
  • 1、master主机 city.py#文件 redis_url文件 from taoche.taoche.spiders.city import CITY_CODE, CAR_CODE_LIST from redis import Redis class Redis_url(): def __init__(self): #连接客户端 ...
  • 创建分布式爬虫环境 链接:https://pan.baidu.com/s/16aXPHtePiarEIFATwcILbw 提取码:hhhh 复制example_project文件,可自行更改名称 保留自己需要用的爬虫文件(这里留mycrawler_redis.py) 项目目录结构如下 ...
  • 1 Scrapy简介 1.1 概念 Scrapy是由Python语言开发的一个快速、高层次的屏幕抓取和web信息抓取...它也提供了多种类型爬虫的基类,如BaseSpider、sitemap爬虫等,最新版本又提供了web2.0爬虫的支持。 通过scrapy
  • Python3网络爬虫教程17——分布式爬虫Scrapy基础 https://blog.csdn.net/u011318077/article/details/86692451 4. 简单实例(爬取一条名言) 创建一个简单的爬虫实例 第一步:创建一个scrapy项目 创建...
  • Python分布式爬虫打造搜索引擎 基于Scrapy、Redis、elasticsearch和django打造一个完整的搜索引擎网站 本教程一共八章:从零开始,直到搭建一个搜索引擎。 推荐前往我的个人博客进行阅读:http://blog.mtianyan.cn/ ...
  • 这几天完成了分布式爬虫的学习,发现了解scrapy-redis源代码对于分布式爬虫的学习真的很重要,废话少说,直接上干货: 文章目录1.创建项目2.源代码解析2.1 Connection.py2.2 defaults.py2.3 dupefilter.py2.4 ...
  • 搭建scrapy的开发环境,本文介绍scrapy的常用命令以及工程目录结构分析,本文中也会详细的讲解xpath和css选择器的使用。然后通过scrapy提供的spider完成所有文章的爬取。然后详细讲解item以及item loader方式完成...
  • 这个实例主要是爬取腾讯滚动新闻模块的数据,做了去重,自增量,可以分布爬取,定时关闭爬虫;一键启动,每半个小时自动获取一次数据。(若这些还不满足,可以继续在run方法中新构一个redis-key,保存在redis中,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,297
精华内容 918
关键字:

scrapy分布式爬虫实例

爬虫 订阅