一、项目介绍
主要目标
使用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条数据请求

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

分析请求链接

化简请求为
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查看数据


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

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

网友评论