scrapy爬虫实战——爬取京东男装商品信息
根据出完整的100-200字摘要:
本次任务要求使用Scrapy爬虫技术从京东男装商品详情页面抓取以下关键信息:商品名称、商家名称、评分、价格(对应每一种颜色和尺码)以及多张图片链接。为了实现这一目标:
使用Scrapy搭建爬虫项目并配置相关参数:
- 设置ROBOTSTXT_OBEY=False以遵循目标网页的规则
- 设置合理的下载延时(3秒)、浏览器信息头及代理IP
- 使用scrapy.pipelines定义文本存储管道(JingdongPipeline)和图片存储管道(JingsummaryPipeline)
创建并运行完整的Scrapy爬虫代码:- 使用LinkExtractor匹配商品详情页链接并调用parse_item方法进行解析
- 在parse_item方法中通过正则表达式提取颜色、尺码等信息
- 使用JavaScript文件链接获取价格信息
- 通过URL拼接构建评价链接并调用相应方法进行解析
确保数据完整性和一致性:- 检查每个字段提取是否准确
- 确保图片路径正确无误
- 验证价格与库存ID对应关系
处理潜在问题:- 设置反向代理IP以规避封禁机制
- 禁用默认robots.txt规则以允许目标网页访问
最终输出结果将包含所有商品详情页中的商品数据及对应图片路径等详细信息,并严格控制字数在100-200字之间保持简洁明了。
以上摘要严格遵循了原文的所有要点,并对具体实现细节进行了简要说明。
一、scrapy爬虫实战项目要求——爬取京东男装商品信息
工具:采用ScrapyCrawler爬虫工具[模板]组件
获取商品信息中的名称、商家信息中的名称、商品评价中的评分、单件商品价格(根据不同颜色和尺码)、多张图片信息
3.提示:容易被封ip,需做好防范
二、完成爬虫项目的框架构思
1.创建爬虫项目:scrapy startproject jingdong
- 创建爬虫文件(基于京东网的商品页面链接数量较多这一特点,在实际操作中更倾向于选择该平台提供的爬虫框架以提高效率):
使用Scrapy启动一个名为jdspider的爬虫任务,并指向目标地址进行数据抓取
- 调整settings配置文件:① 设置ROBOTSTXT_OBEY = False 从而使得机器人遵循目标网页的爬取规定被规避;这将导致无法获取该目标网页上的任何有价值的信息。
②设置下载延迟、浏览器信息头、代理ip
③开启管道(因为涉及到爬取文本信息和图片,因此要设置两个管道)
④设置图片下载路径
4.设置items文件:将需要获取的网页信息设置在items文件中
5.编写jdspider爬虫文件
6.设置pipelines管道文件:设置存储文本和图片的管道
核心思想:通过各种途径得到items文件中设置的所有内容
三、项目源代码及编程思想
1.settings
# -*- coding: utf-8 -*-
# Scrapy settings for jingdong project
#
# For simplicity, this file contains only settings considered important or
# commonly used. You can find more settings consulting the documentation:
#
# https://docs.scrapy.org/en/latest/topics/settings.html
# https://docs.scrapy.org/en/latest/topics/downloader-middleware.html
# https://docs.scrapy.org/en/latest/topics/spider-middleware.html
BOT_NAME = 'jingdong'
SPIDER_MODULES = ['jingdong.spiders']
NEWSPIDER_MODULE = 'jingdong.spiders'
# Crawl responsibly by identifying yourself (and your website) on the user-agent
#USER_AGENT = 'jingdong (+http://www.yourdomain.com)'
# Obey robots.txt rules
ROBOTSTXT_OBEY = False #不遵循目标网页规定
# Configure maximum concurrent requests performed by Scrapy (default: 16)
#CONCURRENT_REQUESTS = 32
# Configure a delay for requests for the same website (default: 0)
# See https://docs.scrapy.org/en/latest/topics/settings.html#download-delay
# See also autothrottle settings and docs
DOWNLOAD_DELAY = 3 #设置下载延时为3s,防止访问过快被网站识别为恶意程序
# The download delay setting will honor only one of:
#CONCURRENT_REQUESTS_PER_DOMAIN = 16
#CONCURRENT_REQUESTS_PER_IP = 16
# Disable cookies (enabled by default)
#COOKIES_ENABLED = False
# Disable Telnet Console (enabled by default)
#TELNETCONSOLE_ENABLED = False
# Override the default request headers:
DEFAULT_REQUEST_HEADERS = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
# 'Accept-Language': 'en',
} #浏览器信息头,模拟浏览器的正常访问,可多设置几个不同浏览器的信息头并随机使用,可降低被网站拦截概率
# Enable or disable spider middlewares
# See https://docs.scrapy.org/en/latest/topics/spider-middleware.html
#SPIDER_MIDDLEWARES = {
# 'jingdong.middlewares.JingdongSpiderMiddleware': 543,
#}
IMAGES_STORE = 'C:\ Users\ Administrator\ PycharmProjects\ jingdong\ jingdong\ pic'
#图片下载路径,注意此处需打两个反斜杠\ ,因为\U为关键字
# Enable or disable downloader middlewares
# See https://docs.scrapy.org/en/latest/topics/downloader-middleware.html
#DOWNLOADER_MIDDLEWARES = {
# 'jingdong.middlewares.JingdongDownloaderMiddleware': 543,
#}
# Enable or disable extensions
# See https://docs.scrapy.org/en/latest/topics/extensions.html
#EXTENSIONS = {
# 'scrapy.extensions.telnet.TelnetConsole': None,
#}
# Configure item pipelines
# See https://docs.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
'jingdong.pipelines.JingdongPipeline': 300, #文本存储管道
'jingdong.pipelines.JingdongImagePipeline': 1 #图片存储管道
}#分别设置文本和图片的管道,由于文本中需要存储图片存储路径,所以图片存储管道的优先级要高于文本管道
# Enable and configure the AutoThrottle extension (disabled by default)
# See https://docs.scrapy.org/en/latest/topics/autothrottle.html
#AUTOTHROTTLE_ENABLED = True
# The initial download delay
#AUTOTHROTTLE_START_DELAY = 5
# The maximum download delay to be set in case of high latencies
#AUTOTHROTTLE_MAX_DELAY = 60
# The average number of requests Scrapy should be sending in parallel to
# each remote server
#AUTOTHROTTLE_TARGET_CONCURRENCY = 1.0
# Enable showing throttling stats for every response received:
#AUTOTHROTTLE_DEBUG = False
# Enable and configure HTTP caching (disabled by default)
# See https://docs.scrapy.org/en/latest/topics/downloader-middleware.html#httpcache-middleware-settings
#HTTPCACHE_ENABLED = True
#HTTPCACHE_EXPIRATION_SECS = 0
#HTTPCACHE_DIR = 'httpcache'
#HTTPCACHE_IGNORE_HTTP_CODES = []
#HTTPCACHE_STORAGE = 'scrapy.extensions.httpcache.FilesystemCacheStorage'
2.items
import scrapy
class JingdongItem(scrapy.Item):
# define the fields for your item here like:
name = scrapy.Field() #商品名称
color = scrapy.Field() #商品颜色
size = scrapy.Field() #商品尺码
price = scrapy.Field() #商品价格
rate = scrapy.Field() #评分
business = scrapy.Field() #商家
image_urls = scrapy.Field() #爬取图片url
images = scrapy.Field() #本地存储图片url
3.pipelines
import scrapy
from scrapy.exceptions import DropItem
from scrapy.pipelines.images import ImagesPipeline
class JingdongPipeline(object):
def process_item(self, item, spider):
with open("jd2.txt","a+") as f:
f.write(item["size"]+item["color"]+" "+item["name"]+" "+item["business"]+" "+item["price"]+" "+item["rate"]+"\n")
return item
class JingdongImagePipeline(ImagesPipeline):
def get_media_requests(self, item, info):
for images_url in item['image_urls']:
yield scrapy.Request("http:"+images_url)
def item_completed(self, results, item, info):
images_path = [x['path'] for ok,x in results if ok]
if not images_path:
raise DropItem("item contains no images")
item['images'] = images_path
return item
4.jdspider(此处为分段详解,最后会给出完整爬虫代码)
class JdspiderSpider(CrawlSpider):
name = 'jdspider'
#allowed_domains = ['https://search.jd.com']
start_urls = ['https://search.jd.com/Search?keyword=男装&enc=utf-8&wq=&pvid=6eafd16d86f9458c87ba95412966155e']
rules = (
Rule(LinkExtractor(tags=('a',), attrs=('href',),allow=r"//item\.jd\.com/\d+\.html"), callback='parse_item', follow=False),
)
基于项目需求,在所有数据都需要爬取的情况下,请确保从商品页面提取所有相关商品的详情页链接,并在后续步骤中继续进行。
①allowed_domains = ['https://search.jd.com']
在京东网站上开发第一个爬虫时会遇到的第一个挑战是在获取商品详情页链接时发现缺少完整的HTTP协议(https://)。由于scrapy获取的京东商品详情页链接中缺少完整的HTTP协议(https:),该域名配置将阻止scrapy有效抓取商品详情页链接。因此建议将allowed_domains配置为['//search.jd.com']或直接注释掉相关代码
②tags=('a',), attrs=('href',)
通过分析男装页面的HTML内容可以看出,在a标签的href属性中隐藏着所有商品详情页的信息。根据上述设置流程进行操作即可完成所需功能。请注意tags和attrs这两个属性都是元组类型,在括号内最后面必须添加逗号‘,’以确保完整性。
③allow=r"//item.jd.com/\d+.html")
此处基于正则表达式建立解析商品详情页链接规则。例如某男装的具体页面链接为http://item.jd.com/43439763045.html时 通过正则表达式的解析方式 可以有效识别并提取出所有商品详情页的链接。
④callback='parse_item'
该回调函数已实现对'parse_item'方法的执行。具体操作为将上一步骤中获取的商品详情链接传递给该方法,并对该商品详情页内容进行深入分析。
⑤follow=False
当该参数被设置为True时, 系统会沿商品详情页链接进行深入挖掘与分析以提取符合条件的信息; 然而由于我们只需获取商品详情页的链接信息而不必进行这些额外操作, 请将该参数设置为False.
def parse_item(self, response):
jd = JingdongItem()
sku_url = eval(re.findall(r"colorSize: \[\{.+]",response.text)[0].replace("colorSize: ",""))
for sku in sku_url:
skuid = sku["skuId"]
priceurl = "https://c0.3.cn/stock?skuId=%s&area=10_698_699_45967&venderId=10169893&buyNum=1&choseSuitSkuIds=&cat=1315,1342,1349&extraParam={'originid':'1'}&fqsp=0&pdpin=&pduid=1563945305409384606669&ch=1&callback=jQuery9945552"%skuid
rateurl = "https://club.jd.com/comment/productCommentSummaries.action?referenceIds=%s&callback=jQuery1963680&_=1564219857332"%skuid
jd["color"] = sku["颜色"]
jd["name"] = response.xpath("//div[@id='spec-list']/ul/li/img/@alt").extract()[0]
jd["business"] = response.xpath("//div[@class='popbox-inner']/div/h3/a/@title").extract()[0]
jd["image_urls"] = response.xpath("//div[@id='spec-list']/ul/li/img/@src").extract()
print("+++++++价格链接++++++++++++",priceurl)
print("----------评分链接---------",rateurl)
print(jd["size"],"\n",jd["color"],"\n",jd["name"],"\n",jd["business"],"\n",jd["image_urls"],"\n")
requset = scrapy.Request(priceurl,meta={"item":jd,"rateurl":rateurl},callback=self.parse_item1)
yield requset
按照项目需求,在详情页面需从网页获取以下信息:商品名称与商家信息(这些数据可在网页内容中直接提取),顾客评价以及多张图片资料(其中单件售价是依据不同款式与尺寸而定)。需要注意的是,在纯文本提取的基础上还需考虑多图上传的情况。由于衣服款式与尺寸的不同导致售价存在差异,并非所有情况都能通过简单的文本抓取完成这一任务。此即爬虫开发中的第二个难点

在查看详情页面代码时会注意到,在页面中不仅仅包含了HTML代码的同时还有JS代码。其中,在一个名为"colorsize"的列表里包含了颜色、尺码以及每个产品对应的库存ID信息。(例如)因此我们只需在一个JS文件中定位到库存ID(skuid)与价格标签('p')之间的数据关联路径 就能提取出每一种颜色 尺码与其对应的价格信息 这项工作需要耐心完成。
colorSize: [{"尺码":"5xl","skuId":49093784956,"颜色":"TT55黑色"},{"尺码":"L","skuId":49093784950,"颜色":"TT55黑色"},{"尺码":"4xl","skuId":49093784955,"颜色":"TT55黑色"},{"尺码":"M","skuId":49093784951,"颜色":"TT55黑色"},{"尺码":"3XL","skuId":49093784961,"颜色":"TT55白色"},{"尺码":"2XL","skuId":49093784960,"颜色":"TT55白色"},{"尺码":"4xl","skuId":49093784962,"颜色":"TT55白色"},{"尺码":"3XL","skuId":49093784954,"颜色":"TT55黑色"},{"尺码":"XL","skuId":49093784952,"颜色":"TT55黑色"},{"尺码":"L","skuId":49093784957,"颜色":"TT55白色"},{"尺码":"2XL","skuId":49093784953,"颜色":"TT55黑色"},{"尺码":"M","skuId":49093784958,"颜色":"TT55白色"},{"尺码":"XL","skuId":49093784959,"颜色":"TT55白色"},{"尺码":"5xl","skuId":49093784963,"颜色":"TT55白色"}]
同样的情况下,在评分内容中也会涉及到库存ID(skuid),这要求我们通过脚本从JS文件中获取所有相关的文件链接信息。以下是我依据这些链接编写的相关代码逻辑;需要注意的是此代码生成结果可能会有所变动,请作为参考使用。
jd = JingdongItem()
sku_url = eval(re.findall(r"colorSize: \[\{.+]",response.text)[0].replace("colorSize: ",""))
for sku in sku_url:
skuid = sku["skuId"]
priceurl = "https://c0.3.cn/stock?skuId=%s&area=10_698_699_45967&venderId=10169893&buyNum=1&choseSuitSkuIds=&cat=1315,1342,1349&extraParam={'originid':'1'}&fqsp=0&pdpin=&pduid=1563945305409384606669&ch=1&callback=jQuery9945552"%skuid
rateurl = "https://club.jd.com/comment/productCommentSummaries.action?referenceIds=%s&callback=jQuery1963680&_=1564219857332"%skuid
首先,在详情页中使用正则表达式依次提取所有的sku信息后,在配置文件中删除与颜色尺寸相关的字段,则会得到一个包含多个字典元素的列表结构。该字符串包含一个包含多个字典元素的结构,并非直接呈现出来的形式;因此需要通过eval函数将其转换为一个可迭代的对象,并将其赋值给变量sku_url以便进一步处理或获取所需信息
"priceurl"与"rateurl"分别对应价格数据及评价信息的JavaScript文件路径。将其中的商品库存ID替换成"sku_url"中的循环提取出的"skuid"。从而生成每个商品对应的价格与评价的具体链接。
jd["size"] = sku["尺码"]
jd["color"] = sku["颜色"]
jd["name"] = response.xpath("//div[@id='spec-list']/ul/li/img/@alt").extract()[0]
jd["business"] = response.xpath("//div[@class='popbox-inner']/div/h3/a/@title").extract()[0]
jd["image_urls"] = response.xpath("//div[@id='spec-list']/ul/li/img/@src").extract()
print("+++++++价格链接++++++++++++",priceurl)
print("----------评分链接---------",rateurl)
print(jd["size"],"\n",jd["color"],"\n",jd["name"],"\n",jd["business"],"\n",jd["image_urls"],"\n")
requset = scrapy.Request(priceurl,meta={"item":jd,"rateurl":rateurl},callback=self.parse_item1)
yield requset
基于提供的代码基础,在sku_url列表中遍历解析获取商品尺码与颜色信息;同时,在详情页中提取商品名称、商家名称,并获取图片下载链接。
目前剩下的任务包括:从价格及评价的链接中提取出对应的价格信息及其相关的评价内容;同时需要利用图片下载接口将获取的图片文件导入至工程模块中。
在解析两个目标链接的过程中,在请求对象中的meta属性中暂时存储其键值对形式,并将其打包处理。随后触发调用parse_item1函数来处理价格相关的链接。
def parse_item1(self,response):
jd = response.meta["item"]
rateurl = response.meta["rateurl"]
jd["price"] = re.findall(r'"p":"\d+',response.text)[0].split(":")[1]
request = scrapy.Request(rateurl,meta={"item":jd},callback=self.parse_item2)
return request
该方法旨在对价格链接进行解析。首先执行数据提取操作后,在后续步骤中使用正则表达式精确捕获价格信息。随后取出上一次存储的内容,并通过计算确定当前的价格值。最后将当前获取的价格数据与上一阶段保存的数据结合存储起来,并调用parse_item2函数处理评价链接的部分内容。整个流程与前一阶段一致。
def parse_item2(self,response):
data = json.loads(response.text)
jd = response.meta["item"]
jd["rate"] = data["goodRateShow"]
yield jd
本节主要阐述编写爬虫程序的基本思路,并在下文呈现完整的代码实现方案To reference.
# -*- coding: utf-8 -*-
import re
import json
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from jingdong.items import JingdongItem
class JdspiderSpider(CrawlSpider):
name = 'jdspider'
start_urls = ['https://search.jd.com/Search?keyword=男装&enc=utf-8&wq=&pvid=6eafd16d86f9458c87ba95412966155e']
rules = (
Rule(LinkExtractor(tags=('a',), attrs=('href',),allow=r"//item\.jd\.com/\d+\.html"), callback='parse_item', follow=False),
)
def parse_item(self, response):
jd = JingdongItem()
sku_url = eval(re.findall(r"colorSize: \[\{.+]",response.text)[0].replace("colorSize: ",""))
for sku in sku_url:
skuid = sku["skuId"]
priceurl = "https://c0.3.cn/stock?skuId=%s&area=10_698_699_45967&venderId=10169893&buyNum=1&choseSuitSkuIds=&cat=1315,1342,1349&extraParam={'originid':'1'}&fqsp=0&pdpin=&pduid=1563945305409384606669&ch=1&callback=jQuery9945552"%skuid
rateurl = "https://club.jd.com/comment/productCommentSummaries.action?referenceIds=%s&callback=jQuery1963680&_=1564219857332"%skuid
jd["size"] = sku["尺码"]
jd["color"] = sku["颜色"]
jd["name"] = response.xpath("//div[@id='spec-list']/ul/li/img/@alt").extract()[0]
jd["business"] = response.xpath("//div[@class='popbox-inner']/div/h3/a/@title").extract()[0]
jd["image_urls"] = response.xpath("//div[@id='spec-list']/ul/li/img/@src").extract()
print("+++++++价格链接++++++++++++",priceurl)
print("----------评分链接---------",rateurl)
print(jd["size"],"\n",jd["color"],"\n",jd["name"],"\n",jd["business"],"\n",jd["image_urls"],"\n")
requset = scrapy.Request(priceurl,meta={"item":jd,"rateurl":rateurl},callback=self.parse_item1)
yield requset
def parse_item1(self,response):
jd = response.meta["item"]
rateurl = response.meta["rateurl"]
jd["price"] = re.findall(r'"p":"\d+',response.text)[0].split(":")[1]
request = scrapy.Request(rateurl,meta={"item":jd},callback=self.parse_item2)
return request
def parse_item2(self,response):
data = json.loads(response.text)
jd = response.meta["item"]
jd["rate"] = data["goodRateShow"]
yield jd
