美文网首页虫虫
Scrapy 爬取京东笔记本电脑

Scrapy 爬取京东笔记本电脑

作者: 马本不想再等了 | 来源:发表于2019-03-11 12:35 被阅读47次

    一、项目介绍

    主要目标

    使用scrapy爬取京东上笔记本电脑的数据
    并使用MongoDB保存数据

    环境

    win 7、python 3.6、pycharm 2018.1、MongoDB 4.0

    技术

    数据采集:scrapy
    数据存储:MongoDB

    思路、难点分析

    1.京东上商品条目的显示分为两步,一开始只加载30条,下拉页面后,再次加载30条,一页数据共有60条。
    2.寻找JS加载的API接口,分析隐藏链接的构造,构建后30条数据的请求。
    3.分析后30条数据的请求条件,若不加referer请求会被拒绝,并且referer是前30条数据请求的url。
    4.分析url的参数,其中包括keyword,page,s等,还有后30天数据请求时需要加上show_items参数(其实就是前三十条数据的sku_number)。
    5.商品详情页中,价格是动态加载,寻找请求入口(https://p.3.cn/prices/mgets?skuIds=J_18250826063),分析请求参数(只有sku_number才是有用的),使用函数处理价格。

    二、项目搭建:

    打开cmd,进入project目录(我自己的项目目录),执行scrpay startproject JD,创建scrapy项目;
    执行cd JD进入项目;
    执行scrapy genspider JD_spider jd.com,创建爬虫。

    三、项目准备

    3.1在settings.py中进行配置

    设置请求头

    DEFAULT_REQUEST_HEADERS = {
        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
        'Accept-Language': 'en',
        'User-Agent':'Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML like Gecko) Chrome/44.0.2403.155 Safari/537.36'
    }
    

    设置robots协议

    ROBOTSTXT_OBEY = False
    

    在项目文件夹中,新建一个start.py文件,方便启动爬虫。

    from scrapy import cmdline
    
    
    cmdline.execute("scrapy crawl JD_spider".split())
    

    设置MongoDB参数

    # 主机地址
    MONGODB_HOST = '127.0.0.1'
    # 端口号,默认27017
    MONGODB_POST = 27017
    # 数据库名称
    MONGODB_DBNAME = 'JingDong'
    # 集合名称
    MONGODB_COLLECTION = 'JingDongPC'
    

    3.2在items.py中配置item

    import scrapy
    
    
    class JdItem(scrapy.Item):
        # 品牌
        brand = scrapy.Field()
        # 产品名称
        name = scrapy.Field()
        # 产品价格
        price = scrapy.Field()
        # 产品信息
        sku_info = scrapy.Field()
        # 产品图片
        picture = scrapy.Field()
        # 产品图片
        link = scrapy.Field()
    

    四、网页分析

    4.1在XHR中可以找到后30条数据请求

    后30条数据Ajax加载请求

    4.2在详情页JS中找到价格请求

    价格加载包
    分析请求链接
    image.png
    化简请求为https://p.3.cn/prices/mgets?skuIds=J_100002117711
    查看分析请求结果,构造函数提取价格

    五、编写项目

    5.1spider编写

    # -*- coding: utf-8 -*-
    import scrapy
    import requests
    import re
    from JD.items import JdItem
    import json
    
    
    class JdSpiderSpider(scrapy.Spider):
        name = 'JD_spider'
        allowed_domains = ['jd.com']
        page = 1
        keyword = '笔记本电脑'
        s = 1
        start_urls = 'https://search.jd.com/Search?keyword={0}&enc=utf-8&qrst=1&rt=1&stop=1&vt=2&wq={1}&psort=3&page={2}&s={3}&click=0'
        next_url = 'https://search.jd.com/s_new.php?keyword={0}&enc=utf-8&qrst=1&rt=1&stop=1&vt=2&wq={1}&psort=3&page={2}&s={3}&scrolling=y&log_id=1552182240.83021&tpl=1_M&show_items={4}'
        def start_requests(self):
            yield scrapy.Request(self.start_urls.format(self.keyword, self.keyword, self.page, self.s), callback=self.parse)
    
        def parse(self, response):
            """
            爬取前30条商品链接
            :param response:
            :return:
            """
            sku_list = response.xpath("//ul[contains(@class,'gl-warp')]/li/@data-sku").extract()
            items = ",".join(sku_list)
            #构造详情页url
            for sku in sku_list:
                url = 'https://item.jd.com/{}.html'.format(sku)
                yield scrapy.Request(url, callback=self.detial_parse)
            self.page += 1
            self.s = (self.page-1)*30+1
            headers = {'referer': response.url}
            yield scrapy.Request(self.next_url.format(self.keyword, self.keyword, self.page, self.s, items), callback=self.next_parse, headers=headers)
    
    
        def next_parse(self, resposne):
            """
            爬取后30条商品的链接
            :param resposne:
            :return:
            """
            sku_list = resposne.xpath("//li/@data-sku").extract()
            # 构造详情页url
            for sku in sku_list:
                url = 'https://item.jd.com/{}.html'.format(sku)
                yield scrapy.Request(url, callback=self.detial_parse)
    
            if self.page < 200:
                self.page += 1
                self.s = (self.page-1)*30+1
                yield scrapy.Request(self.start_urls.format(self.keyword, self.keyword, self.page, self.s))
    
        def detial_parse(self, response):
            """
            产品详情页中的信息获取
            :param response:
            :return:
            """
            brand = response.xpath("//ul[@id='parameter-brand']/li/@title").extract()[0]
            name = response.xpath("//*[@class='p-parameter']/ul[2]/li[1]/@title").extract()[0]
            images_link = response.xpath("//ul[@class='lh']/li/img/@src").extract()
            pictures = []
            for image in images_link:
                picture = re.sub(r'/s.*_', "/s500x500_", image)
                pictures.append(picture)
            sku_number = response.xpath("//*[@class='p-parameter']/ul[2]/li[2]/@title").extract()[0]
            sku_info = {}
            for div in response.xpath("//div[@class='Ptable-item']"):
                h3 = div.xpath("./h3/text()").extract()[0]
                dts = div.xpath(".//dt/text()").extract()
                new_dts = self.new_dt(dts)
                dd = div.xpath(".//dd[not(@class)]/text()").extract()
                sku_info[h3] = {}
                for t, d in zip(new_dts, dd):
                    sku_info[h3][t] = d
            link = response.url
            price = self.get_price(sku_number)
    
            item = JdItem()
            item['brand'] = brand
            item['name'] = name
            item['price'] = price
            item['sku_info'] = sku_info
            item['picture'] = pictures
            item['link'] = link
            yield item
    
    
        def get_price(self, sku_number):
            """
            获取产品详情页中的价格
            :param sku_number:
            :return:
            """
            response = requests.get('https://p.3.cn/prices/mgets?skuIds=J_{}'.format(sku_number))
            price = json.loads(response.text)[0]['p'].split(".")[0]
            return int(price)
    
        def new_dt(self, dts):
            """
            替换产品信息中,以字典保存的条目key中的.号
            :param dts:
            :return:
            """
            new_dts = []
            for dt in dts:
                if dt.find("."):
                    dt = dt.replace(".", "-")
                new_dts.append(dt)
            return new_dts
    

    5.2pipeline编写(数据存储)

    import pymongo
    from JD import settings
    
    
    class JdPipeline(object):
        def __init__(self):
            # 获取setting中的主机名,端口号和集合名
            host = settings.MONGODB_HOST
            port = settings.MONGODB_POST
            dbname = settings.MONGODB_DBNAME
            collection = settings.MONGODB_COLLECTION
    
            # 创建一个mongo实例
            client = pymongo.MongoClient(host=host, port=port)
    
            # 访问数据库
            db = client[dbname]
    
            # 访问集合
            self.collection = db[collection]
    
        def process_item(self, item, spider):
            data = dict(item)
            self.collection.insert(data)
            return item
    

    六、爬取结果

    6.1使用cmd连接mongodb查看数据

    连接mongodb
    查看数据库状态

    可以看到JingDong大小7M,其中有5362条数据,具体大小为7380KB


    查看数据

    6.2使用MongoDB Compass(MongoDB的图形管理工具,类似于Mysql的Navicat)查看数据

    MongoDB中京东笔记本电脑5362条数据

    相关文章

      网友评论

        本文标题:Scrapy 爬取京东笔记本电脑

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