Python爬虫之Js逆向案例(17)- Scrapy JD版店铺详情|问答

2023-11-14

本案例是案例(16)的Scrapy版本

一次运行程序,同时获取内容:获取商店详情、商品问题、商品答案;

效果如下图:
在这里插入图片描述
在这里插入图片描述

一.Scrapy框架从安装到运行的过程

1.安装scrapy框架

控制台输入:pip3 install scrapy

2.验证安装结果

➜  ~ scrapy -v            
Scrapy 2.7.1 - no active project

Usage:
scrapy <command> [options] [args]

Available commands:
bench         Run quick benchmark test
commands      
fetch         Fetch a URL using the Scrapy downloader
genspider     Generate new spider using pre-defined templates
runspider     Run a self-contained spider (without creating a project)
settings      Get settings values
shell         Interactive scraping console
startproject  Create new project
version       Print Scrapy version
view          Open URL in browser, as seen by Scrapy

[ more ]      More commands available when run from project directory

Use "scrapy <command> -h" to see more info about a command

输入scrapy -v能显示出上面的内容说明安装成功了。

3.新建项目

scrapy startproject jd

4.新建 spider

scrapy genspider jd "jd.com"

注意新建spider时一定要进入到spiders目录下新建哦!

5.运行 spider

scrapy crawl jd_goods

下面会进行以下几步进行分析(下方演示过程全部使用chrome浏览器);
对应接口的分析过程同《案例16》分析过程相同咱就不在重复了啊!

下面来说scrapy配置细节

二.Scrapy配置

新建的scrapy项目都有一个settings.py文件的,这个文件中预留的配置非常多,我的配置如下

# Scrapy settings for jd project

import random

BOT_NAME = 'jd'

SPIDER_MODULES = ['jd.spiders']
NEWSPIDER_MODULE = 'jd.spiders'

# Crawl responsibly by identifying yourself (and your website) on the user-agent
USER_AGENT_LIST = [
    "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60",
    "Opera/8.0 (Windows NT 5.1; U; en)",
    "Mozilla/5.0 (Windows NT 5.1; U; en; rv:1.8.1) Gecko/20061208 Firefox/2.0.0 Opera 9.50"
]
USER_AGENT = random.choice(USER_AGENT_LIST)
# Obey robots.txt rules
ROBOTSTXT_OBEY = False

# 在项处理器(也称为项目管道)中并行处理的并发项目的最大数量(每个响应)。
CONCURRENT_REQUESTS = 16

# 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 = 2.3  # "%.1f" % random.random()
RANDOMIZE_DOWNLOAD_DELAY = True
LOG_ENABLED = True
LOG_ENCODING = 'utf-8'
# 日志级别 CRITICAL, ERROR, WARNING, INFO, DEBUG
# LOG_LEVEL = 'ERROR'

# 将由Scrapy下载程序执行的并发(即同时)请求的最大数量。
# CONCURRENT_REQUESTS_PER_DOMAIN = 8
# # 将对任何单个域执行的并发(即同时)请求的最大数量。
# CONCURRENT_REQUESTS_PER_IP = 8

# 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 = {
    "authority": "item-soa.jd.com",
    "accept": "*/*",
    "accept-language": "zh-CN,zh;q=0.9",
    "referer": "https://item.jd.com/",
    "sec-fetch-mode": "no-cors",
    "sec-fetch-site": "same-site",
}

MEDIA_ALLOW_REDIRECTS = True


HTTPERROR_ALLOWED_CODES = [302, 301, 401, 400]
# 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 = {
    'jd.pipelines.ShopInfoPineline': 300,
    # 'jd.pipelines.CommentPineline': 300,
    'jd.pipelines.QuestionPineline': 300,
    'jd.pipelines.AnswerPineline': 300,
}

PROXIES = [
    'http://113.124.92.228:17551',
]

# 获取到的数据的存放目录名称
DATA_URI = 'data_file'

# 手动设置每一个id翻页的最大页码,例如限制同一个id翻页请求时控制前50页,此处可设置为50;负数时为不限制最大页码,则此时获取全部翻页数据
MAX_PAGE = 5

三.spider部分相关代码编写

3.1 店铺详情

def parse(self, response):

        data = response.json()
        p_id = response.meta['p_id']

        if data.get("shopInfo") == None:
            return
        shop = data.get("shopInfo").get("shop")
        # 自营店铺貌似也获取不到店铺的评价数据
        if shop is None:
            # 没有店铺评分的店,测试下来发现是封掉的店
            return
        shop_item = ShopItem()
        shop_item['product_id'] = p_id
        shop_item['shop_name'] = shop.get("name", '')
        shop_item['goods_name'] = tool.translate_chars(
            data.get('wareInfo').get("wname"))
        shop_item['shop_id'] = shop.get('shopId', '')
        shop_item['shop_rate'] = shop.get('scoreRankRateGrade', '')
        shop_item['evaluate_score'] = shop.get('evaluateScore', '')
        shop_item['logistics_score'] = shop.get('logisticsScore', '')
        shop_item['after_Sale_score'] = shop.get('afterSaleScore', '')

        item = {'key': 'shop', 'info': shop_item}
        yield item

        self.quesiton_params.update({'productId': p_id})

        question_url = self.question_base_url + urlencode(self.quesiton_params)
        yield Request(get_scraperapi_url(question_url),
                      cookies=self.cookies,
                      callback=self.question_parse,
                      meta={
                          "p_id": p_id,
                          'page': 1
                      })

3.2 商品问题

def question_parse(self, response):
        data = response.json()
        p_id = response.meta['p_id']  # product_id
        page = response.meta['page']

        totalPage = math.ceil(data.get("totalItem", 0) / 10)
        questionList = data.get('questionList', [])

        if page > totalPage:
            return

        if MAX_PAGE < page and MAX_PAGE > 0:
            return

        for question in questionList:

            question_item = QuestionItem()
            q_id = question.get('id')
            question_item['product_id'] = p_id  # 产品ID
            question_item['question_id'] = q_id  # 问题ID
            question_item['question'] = tool.translate_chars(
                question.get('content', ''))  # 问题
            question_item['question_user_id'] = question.get('userInfo').get(
                'nickName', '')  #  提问用户昵称
            question_item['question_time'] = question.get('created',
                                                          '')  # 提问日期

            item = {'key': 'question', 'info': question_item}
            yield item

            answerCount = question.get("answerCount", 0)

            # 直接保存
            if answerCount > 0 and answerCount <= 2:
                answerList = question.get('answerList', [])
                for answer in answerList:
                    answer_item = AnswerItem()
                    answer_item['product_id'] = p_id  # 产品ID
                    answer_item['question_id'] = answer.get('id', '')  # 问题ID
                    answer_item['answer'] = tool.translate_chars(
                        answer.get('content', ''))  # 答
                    answer_item['answer_name'] = answer.get('userInfo').get(
                        'nickName', '')  #  答问用户id|name
                    answer_item['answer_time'] = answer.get('created',
                                                            '')  # 答问日期
                    item = {'key': 'answer', 'info': answer_item}
                    yield item

            elif answerCount > 2:
                self.answer_params.update({'questionId': q_id, 'page': 1})
                answer_url = self.answer_base_url + urlencode(
                    self.answer_params)
                yield Request(get_scraperapi_url(answer_url),
                              callback=self.answer_parse,
                              meta={
                                  "p_id": p_id,
                                  "q_id": q_id,
                                  'page': 1
                              })

        if page == 1 and page < totalPage:
            for k in range(2, totalPage):
                if MAX_PAGE < k and MAX_PAGE > 0:
                    break

                self.quesiton_params.update({
                    'productId': p_id,
                    'page': k,
                })

                question_url = self.question_base_url + urlencode(
                    self.quesiton_params)
                yield Request(get_scraperapi_url(question_url),
                              callback=self.question_parse,
                              meta={
                                  "p_id": p_id,
                                  'page': k
                              })

四.pipelines|Item编写

4.1商品详情pipelines

class ShopInfoPineline(object):

    def open_spider(self, spider):
        # 产品ID	店铺名  商品名  店铺id  店铺星级	商品评价    物流履约    售后服务
        if spider.name == 'jd_goods':
            self.shop_info_line = "Product_Id,Shop_Name,Goods_Name,Shop_ID,Shop_Rate,Evaluate_Score,Logistics_Score,After_Sale_Score\n"
            data_dir = os.path.join(settings.DATA_URI)
            #判断文件夹存放的位置是否存在,不存在则新建文件夹
            if not os.path.exists(data_dir):
                os.makedirs(data_dir)
            file_path = data_dir + '/shop.csv'
            new_sku_path = data_dir + '/new_sku_id.csv'
            self.file = open(file_path, 'w', encoding='utf-8')
            self.new_sku_file = open(new_sku_path, 'w', encoding='utf-8')
            self.file.write(self.shop_info_line)

    def close_spider(self, spider):  # 在关闭一个spider的时候自动运行
        self.file.close()
        self.new_sku_file.close()

    def process_item(self, item, spider):

        try:
            if spider.name == 'jd_goods' and item['key'] == 'shop':
                info = item['info']
                shop_info_line = '{},{},{},{},{},{},{},{}\n'.format(
                    info.get('product_id'),
                    info.get('shop_name', ''),
                    info.get('goods_name', ''),
                    info.get('shop_id', ''),
                    info.get('shop_rate', ''),
                    info.get('evaluate_score', ''),
                    info.get('logistics_score', ''),
                    info.get('after_Sale_score', ''),
                )
                self.file.write(shop_info_line)
                self.new_sku_file.write('{}\n'.format(info.get('product_id')))
        except BaseException as e:
            print("ShopInfo错误在这里>>>>>>>>>>>>>", e, "<<<<<<<<<<<<<错误在这里")
        return item

4.2商品详情的Item


class ShopItem(scrapy.Item):

    product_id = scrapy.Field()  # 产品ID
    shop_name = scrapy.Field()  # 店铺名
    goods_name = scrapy.Field()  # 商品名
    shop_id = scrapy.Field()  # 店铺id
    shop_rate = scrapy.Field()  # 店铺星级
    evaluate_score = scrapy.Field()  # 商品评价
    logistics_score = scrapy.Field()  # 物流履约
    after_Sale_score = scrapy.Field()  # 售后服务

五.项目目录结构及结果

在这里插入图片描述

总结:scrapy框架作为一个分布式、多线程框架非常适合爬数据量非常大的网站,整体使用感觉非常不错,配置方便快捷、爬取速度也很快。不过也有缺点,就是容易被识别到,一不小心就被关小黑屋了、、、本案例只是自己纯学习练习使用!源码已同步到知识星 球!


后期会持续分享爬虫案例-100例,不想自己造轮子的同学可加入我的知识星球,有更多技巧、案例注意事项、案例坑点终结、答疑提问特权等你哦!!!

欢迎加入「python、爬虫、逆向Club」知识星球

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Python爬虫之Js逆向案例(17)- Scrapy JD版店铺详情|问答 的相关文章

随机推荐

  • QT中实现应用程序的单例化

    一介绍 通过编写一个QSingleApplication类 来实现Qt程序的单例化 原文的作者是在Windows Vista Qt4 4 下实现的 不过应用在其他平台上是没问题的 本文是我在http www qtcentre org wik
  • 女程序员的转型

    女 30 做了开发6年 各种语言都多少有点用 但以前都是野路子开发 两个人 一个人做pc端 一个人做web端 或者一个人做app 一个人做web 没有ui 没有前段 所有的人都是你 虽然之前也算是项目经理 但没有一个规范的流程 甚至连流程图
  • OpenCV中imread/imwrite与imdecode/imencode的异同

    OpenCV中的cv imdecode函数是从指定的内存缓存中读一幅图像 而cv imencode是将一幅图像写进内存缓存中 cv imread是从指定文件载入一幅图像 cv imwrite是保存一幅图像到指定的文件中 cv imread和
  • 如何阅读大型程序

    近期想读下程序 但发现非常难 比如一个网络程序模块 有6个cpp源程序 共5916行源代码 不包括头文件中引用的别的地方的代码 1 我对业务还算熟悉 但是看到那么多变量定义时还是感到理解起来很吃力 因为有些变量根本就是过程变量 2 函数层层
  • rust安装link.exe报错

    rust安装link exe报错 今天安装rust完成之后 直接cargo new一个hello world程序 等到cargo run代码的时候发现报错 linking with link exe failed exit code 113
  • PLC-串口通信实例

    随着微电子及控制技术的不断发展 PLC已逐渐成为一种智能型 综合型控制器 由PLC构成的集散控制是现代工业控制的一个重要组成部分 PLC具有使用简单方便 故障率低 抗干扰能力强等优点 但同时 数据的计算处理与管理能力较弱 且无法提供良好的人
  • 使用清华镜像编译aosp

    基础环境编译 与上一篇mac 编译 aosp 一样 http blog csdn net karts article details 54971227 只是 可以在 https mirrors tuna tsinghua edu cn he
  • 【第三阶段 day24】Axios的补充 IDEA自动注入mapper异常说明 Ajax的简化操作

    文章目录 1 Axios的补充 1 1 Axios Post请求 1 2 控制层 2 IDEA自动注入mapper异常说明 3 Ajax的简化操作 3 1 简化axios请求的前缀 3 2 关于promise对象的说明 3 3 Ajax 回
  • 让我们彻底了解Maven(二)--- Maven私服的搭建

    首先我们为什么需要搭建Maven私服 一切技术来源于解决需求 因为我们在实际开发中 当我们研发出来一个公共组件 为了能让别的业务开发组用上 则搭建一个远程仓库很有必要 写完公用组件后 直接发布到远程仓库 别人需要用到时 直接从远程仓库拉取即
  • 共享单车项目数据可视化+需求策略分析

    一 项目背景 自行车共享系统是一种租赁自行车的方式 其中获得会员资格 租赁和归还自行车的过程是通过遍布城市的站点网络自动完成的 使用这些系统 人们可以从一个地方租用自行车 并根据需要将其归还到另一个地方 共享单车是近年来流行起来的新兴产业
  • MOS管参数每一个参数详解-收藏版

    MOS管参数 在使用MOS管设计开关电源或者马达驱动的时候 一般都要考虑MOS的导通电阻 最大电压等 最大电流等因素 MOSFET是电压型驱动器材 驱动的进程即是栅极电压的建立进程 这是经过对栅源及栅漏之间的电容充电来完成的 下面将有此方面
  • PCB铜箔厚度单位盎司(OZ)的具体信息

    盎司 OZ 本身是一个重量单位 盎司和克 g 的换算公式为 1OZ 28 35g 在PCB行业中 1OZ意思是重量1OZ的铜均匀平铺在1平方英尺 FT2 的面积上所达到的厚度 它是用单位面积的重量来表示铜箔的平均厚度 用公式来表示即 1OZ
  • Java基础篇——面向对象编程

    活动地址 CSDN21天学习挑战赛 学习的最大理由是想摆脱平庸 早一天就多一份人生的精彩 迟一天就多一天平庸的困扰 各位小伙伴 如果您 想系统 深入学习某技术知识点 一个人摸索学习很难坚持 想组团高效学习 想写博客但无从下手 急需写作干货注
  • npm 切换源_nrm:npm包管理工具

    一 简介 nrm npm registry manager 是npm的镜像源管理工具 可以方便的更换npm的包源 可解决问题 1 更换国内镜像包源 如淘宝npm镜像 解决国内npm国外包慢的问题 2 更换某些内网独立包源 实现安装内网独立的
  • 「技术综述」人脸妆造迁移核心技术总结

    2020 07 10 12 02 36 美颜和美妆是人脸中很常见的技术 在网络直播以及平常的社交生活中都有很多应用场景 常见的如磨皮 美白 塑形等美颜技术我们已经比较熟悉了 而本文重点介绍的是人脸妆造迁移的核心技术及其相关资源 作者 编辑
  • Spring框架详解(二)

    一 IOC基本原理 1 什么是IOC容器 容器 容器是一种为特定组件的运行提供必要支持的一个软件环境 例如Tomcat就是一个Servlet容器 它可以为Servlet的运行提供运行环境 类似Docker这样的软件也是一个容器 它提供了必要
  • 由于找不到msvcp120.dll无法执行此代码的解决方法

    电脑系统中的msvcp120 dll文件如果丢失或者损坏 那么会有很多游戏跟软件就会出现无法打开运行的情况 msvcp120 dll是Windows系统动态连接组件中非常重要的文件 小编今天就把修复教程分享给大家 修复方法如下 首先是打开电
  • gradle 查看依赖类库版本_如何查找第三方库(Gradle引用)的依赖?

    答 三种方法查找 1 执行 Gradle Task androidDependencies 可以直接执行 gradle androidDependencies 执行结果将在控制台直接输出 可以在控制台直接查看 但是如果依赖很多的时候 控制无
  • BeanUtils.copyProperties()和JSONObject.parseObject()分别是哪种拷贝类型(浅拷贝 or 深拷贝)

    目录 一 结论 二 证明BeanUtils copyProperties 是浅拷贝 三 证明JSONObject parseObject 是深拷贝 四 总结 一 结论 BeanUtils copyProperties 浅拷贝 JSONObj
  • Python爬虫之Js逆向案例(17)- Scrapy JD版店铺详情|问答

    本案例是案例 16 的Scrapy版本 一次运行程序 同时获取内容 获取商店详情 商品问题 商品答案 效果如下图 一 Scrapy框架从安装到运行的过程 1 安装scrapy框架 控制台输入 pip3 install scrapy 2 验证