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

news2024/11/20 6:35:40

本案例是案例(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」知识星球

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/388197.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于Flink CDC datastream mysql to mysql 序列化sql 数据同步

基于Flink CDC datastream mysql to mysql 序列化sql 数据同步 Flink CDC有两种方式同步数据库&#xff1a; 1. 一种是通过FlinkSQL直接输入两表数据库映射进行数据同步&#xff0c;缺点是只能单表进行同步&#xff1b; 2. 一种是通过DataStream开发一个maven项目&#xff0c…

华为机试题:HJ106 字符逆序(python)

文章目录&#xff08;1&#xff09;题目描述&#xff08;2&#xff09;Python3实现&#xff08;3&#xff09;知识点详解1、input()&#xff1a;获取控制台&#xff08;任意形式&#xff09;的输入。输出均为字符串类型。1.1、input() 与 list(input()) 的区别、及其相互转换方…

面试+算法:罗马数字及Excel列名与数字互相转换

概述 算法是一个程序员的核心竞争力&#xff0c;也是面试最重要的考查环节。 试题 判断一个罗马数字是否有效 罗马数字包含七种字符&#xff1a;I&#xff0c;V&#xff0c;X&#xff0c;L&#xff0c;C&#xff0c;D和M&#xff0c;如下 字符数值I1V5X10L50C100D500M1000…

libgdx导入blender模型

具体就是参考 官网 https://libgdx.com/wiki/graphics/3d/importing-blender-models-in-libgdx blender 教程可以看八个案例教程带你从0到1入门blender【已完结】 这里贴一下过程图。 1.初始环境搭建略过。 2.打开blender 选中摄像机和灯光&#xff0c;右键进行删除。 3.选中…

ES 7.7.0 数据迁移

本文使用 elasticdump 做数据迁移&#xff0c;支持在线和离线俩种方式&#xff0c;适用于数据量比较小的情况。 1、Node 安装 由于elasticdump 依赖于 node&#xff0c;首先需要安装下node。 1.1、 Linux 安装 $ wget https://nodejs.org/dist/v10.15.0/node-v10.15.0-linu…

[数据结构]:10-二叉排序树(无头结点)(C语言实现)

目录 前言 已完成内容 二叉排序树实现 01-开发环境 02-文件布局 03-代码 01-主函数 02-头文件 03-BinarySearchTreeCommon.cpp 04-BinarySearchTreeFunction.cpp 结语 前言 此专栏包含408考研数据结构全部内容&#xff0c;除其中使用到C引用外&#xff0c;全为C语言…

基于支持向量机SVM的房价预测,基于支持向量机SVM的回归分析

目录 支持向量机SVM的详细原理 SVM的定义 SVM理论 SVM应用实例,基于SVM的房价预测 支持向量机SVM的详细原理 SVM的定义 支持向量机(support vector machines, SVM)是一种二分类模型,它的基本模型是定义在特征空间上的间隔最大的线性分类器,间隔最大使它有别于感知机;…

【算法题】1958. 检查操作是否合法

插&#xff1a; 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 坚持不懈&#xff0c;越努力越幸运&#xff0c;大家一起学习鸭~~~ 题目&#xff1a; 给你一个下标从 0 开始的 8 x 8 网…

Vue响应式原理————Object.defineProperty()和proxy的用法分享

Vue框架一个比较核心的功能就是我们的数据是响应式的&#xff0c;这样我们在修改数据的时候&#xff0c;页面会自动帮我们更新&#xff0c;那么想要实现这个功能就要实现对一个数据的劫持&#xff0c;即在取值和设置值的同时我们能够检测到即数据劫持。vue2响应式的实现原理所依…

用原生js手写分页功能

分页功能如下&#xff1a; 数据分页显示&#xff0c;每页显示若干条数据&#xff0c;默认当前页码为第一页。例如&#xff1a;每页5条数据&#xff0c;则第一页显示 1-5 条&#xff0c;第二页显示 6-10 条&#xff0c;依此类推。当页码为第一页时&#xff0c;上一页为禁用状态…

IronPDF for .NET 2023.2.4 Crack

适用于 .NET 2023.2.4 的 IronPDF 添加对增量 PDF 保存的支持。 2023 年 3 月 2 日 - 10:23新版本 特征 添加了对 IronPdfEngine Docker 的支持。 添加了对增量 PDF 保存的支持。 重新设计了 PDF 签名和签名。 删除了 iTextSharp 依赖项。 在文本页眉/页脚中添加了 DrawDivider…

laravel8多模块、多应用和多应用路由

1、安装多应用模块 composer require nwidart/laravel-modules2、执行命令&#xff0c;config文件夹下生成一个modules.php配置文件 php artisan vendor:publish --provider"Nwidart\Modules\LaravelModulesServiceProvider"3、修改config文件夹下的modules.php&am…

python爬虫学习之路

【2023.3.3】一、爬虫概念 通过编写程序&#xff0c;模拟浏览器上网&#xff0c;然后让其去互联网上抓取数据的过程。 价值&#xff1a; 抓取互联网上的数据&#xff0c;为我所用&#xff0c;有了大量的数据&#xff0c;就如同有了一个数据银行一样&#xff0c;下一步做的就是如…

程序员怎么写出亮眼的简历?

要随时与正能量的人保持同行&#xff0c;因为他的心一直在靠近远方 什么是简历 我们先看下百度百科对于简历的描述&#xff1a; 可以看出&#xff0c;简历是受法律规定&#xff0c;因为简历本身的信息真实性较高&#xff0c;所以简历一直是早期被市场上进行数据交易的重要载…

windows 下 python 和repo 下载安装环境变量配置

repo 安装成功&#xff0c;但是下载代码 repo init的时候出错 不知道是不是repo windows版本有问题 python 最好下载2.6-2.7版本的 Python Releases for Windows | Python.org 不然下载代码会有问题&#xff0c;下不了&#xff0c;会提示安装2.6-2.7版本的 Windows下成功安…

人机界面艺术设计

人机界面艺术设计 2.1人机界面艺术设计思路 人们经常有意通过某种工具或创造来解决难题&#xff0c;然而这并不意味着人们乐于接受别人或其他事情&#xff0c;他们很难提出问题。在用户使用网页或软件的时候&#xff0c;他们有明确的目标&#xff0c;他们利用电脑来帮助自己达…

Hbase RegionServer的核心模块

RegionServer是HBase系统中最核心的组件&#xff0c;主要负责用户数据写入、读取等基础操作。RegionServer组件实际上是一个综合体系&#xff0c;包含多个各司其职的核心模块&#xff1a;HLog、MemStore、HFile以及BlockCache。 一、RegionServer内部结构 RegionServer是HBas…

Altium Designer PCB孤岛铜的去除方法教程

孤岛铜&#xff0c;也叫死铜&#xff0c;是指在PCB中孤立无连接的铜箔&#xff0c;一般都是在敷铜的时候产生&#xff0c;不利于生产。解决的办法比较简单&#xff0c;可以手工连线将其与同网络的铜箔相连&#xff0c;也可以通过打过孔的方式将其与同网络的铜箔相连。无法解决的…

Biomod2 (下):物种分布模型建模

这里写目录标题1.给出一个线性回归模型并求出因子贡献度2.biomod22.1 pseudo-absences:伪不存在点&#xff08;PA&#xff09;2.1.1 random2.2.2 disk2.2.3 user.defined method3.使用网格划分区域3.1 计算质心4. 完整案例1.给出一个线性回归模型并求出因子贡献度 ##---------…

【游戏逆向】FPS游戏玩家对象数据分析

玩家健康值 查找玩家健康值,玩家健康值是100,但是我们并不知道数值类型,我们可以使用精确搜索方式搜索100-所有类型 CE搜索 结果很多,我们可以使用手雷来减少血量 我们会得到两个结果 我们可以去尝试改变数值,最终发现一个是我们的客户端健康值,一个是服务器健康值,…