美文网首页虫虫
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