拼多多商品信息爬取

爬取完几个主流电商平台的信息,今天想着也去攻克一下拼多多。于是先去GitHub上面找一下有没有哪位大神搞过了借鉴一下,然后果然发现一个好用的接口。

想着既然找到了就先下载下来跑一下,嗯。。。“热门”的感觉可以,应该很简单。然后就兴高采烈地扩展一下别的商品种类,果然很多坑。。

一、思路分析

经过谷歌F12工具一番分析,总结一下爬取思路:

1、“热门”商品比较特殊,其他商品种类有细分小分类,故需从大分类获取到小分类地址,然后进一步请求。以“女装”为例:地址为:http://apiv3.yangkeduo.com/operation/14/groups?page=1&size=100&opt_type=1 ,size为单页显示商品数量,最大可以400,大分类opt_type为1,小分类opt_type为2,14是“女装”ID。

2、大分类和小分类页面有时第一次请求会失败,需多次请求才能成功,故需要设置一个循环。

3、店铺信息在商品页列表里面每次请求会有所缺失,故需到商品页里面获取,但是商品页需要带请求参数,主要是cookieAccessToken(访问令牌)。这两个参数会过期,解决方法后面讨论。

二、爬取字段

店铺信息获取比较麻烦,如果不要的话会简单很多,商品评价如果要获取可以参考一下上面的GitHub地址,这里先注释掉。

    goods_id = scrapy.Field()  # 商品ID
    goods_name = scrapy.Field()  # 商品名字
    price = scrapy.Field()  # 拼团价格 返回的字段多乘了100
    sales = scrapy.Field()  # 已拼单数量
    normal_price = scrapy.Field()  # 单独购买价格
    # comments = scrapy.Field()  # 商品评价
    opt_id = scrapy.Field()  # 小分类ID
    opt_name = scrapy.Field()  # 小分类名字
    link_url = scrapy.Field()  # 商品地址
    mall_id = scrapy.Field()  # 店铺ID
    mall_name = scrapy.Field()  # 店铺名字
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

三、spider代码

这次爬虫采用的是scrapy_redis分布式爬取(顺便再熟悉一下),下面放上spider代码:

# -*- coding: utf-8 -*-
import scrapy
import json
from pinduoduo.items import PinduoduoItem
from copy import deepcopy
from scrapy_redis.spiders import RedisSpider
import re


class PdditemSpider(RedisSpider):
    name = 'pdditem'
    allowed_domains = ['yangkeduo.com']
    page = 1  # 起始页码数
    size = 400  # 单页显示数量(最大400)
    opt_type = 1
    offset = 0  # 偏移量100为一页
    # start_urls = ['http://apiv3.yangkeduo.com/operation/14/groups?page={}&size={}&opt_type={}'.format(page, size,opt_type)]  # 女装商品
    # 'http://apiv3.yangkeduo.com/operation/14/groups?page=1&size=400&opt_type=1'
    redis_key = "pinduoduo"

    def parse(self, response):  # 解析分类页
        goods_list_json = json.loads(response.body)
        url = response.url
        keys = goods_list_json.keys()
        key = list(keys)[2]
        if key == "error_code":  # 请求失败获取到错误json代码,重新请求
            print(key, "再次尝试请求")
            yield scrapy.Request(
                url,
                callback=self.parse,
                dont_filter=True
            )
        else:
            item = PinduoduoItem()
            opt_infos = goods_list_json['opt_infos']
            if opt_infos is not None:
                for info in opt_infos:
                    item['opt_id'] = info['id']
                    item['opt_name'] = info['opt_name']
                    yield scrapy.Request(
                        'http://apiv3.yangkeduo.com/operation/'+ str(item['opt_id'])+ '/groups?offset=0&size=100&opt_type=2',
                        callback=self.parse_good,
                        meta={"item": deepcopy(item)}
                    )

    def parse_good(self, response):  # 解析商品页
        item = response.meta["item"]
        goods_list_json = json.loads(response.body)
        url = response.url
        keys = goods_list_json.keys()
        key = list(keys)[-1]
        if key == "error_code":
            print(key,"再次尝试请求")
            yield scrapy.Request(
                url,
                callback=self.parse_good,
                meta={"item": deepcopy(item)},  # 大坑!!重新请求需要带上item
                dont_filter=True
            )
        else:
            goods_list = goods_list_json['goods_list']
            # 判断是否是最后一页
            if not goods_list:
                return
            for each in goods_list:
                item['goods_name'] = each['goods_name']
                item['price'] = float(each['group']['price']) / 100  # 拼多多的价格默认多乘了100
                item['sales'] = each['cnt']
                item['normal_price'] = float(each['normal_price']) / 100
                item['goods_id'] = each['goods_id']
                item['link_url'] = 'http://yangkeduo.com/'+ each['link_url']
                yield scrapy.Request(
                     item['link_url'],
                     callback=self.get_mall_data, meta={"item": item},)

            self.page += 1
            self.offset = self.page*100  # 构造下一页地址
            yield scrapy.Request(url='http://apiv3.yangkeduo.com/operation/'+ str(item['opt_id'])+ '/groups?offset={}&size=100&opt_type=2'.format(self.offset),
                                 callback=self.parse_good,
                                 meta={"item": item}
                                 )

    def get_mall_data(self, response):  # 获取店铺信息
        item = response.meta["item"]
        Cookie = response.request.headers.getlist('Cookie')
        mall_name = response.xpath("//div[@class='goods-mall-name']/text()").extract_first()
        if mall_name is not None:
            item['mall_name'] = mall_name
        data = response.body.decode('utf-8')
        pattern = re.compile(r'mall_id=(.*?)\"', re.S)
        result = re.search(pattern, data)
        if result is not None:
            item['mall_id'] = result.group(1)
        else:
            yield scrapy.Request(
                response.url,
                callback=self.get_mall_data,
                meta={"item": item},
                dont_filter=True
            )
        if item['mall_name'] and item['mall_id']:
            print(item)
            yield (item)
        else:
            print('数据获取不全,重新获取')



  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108

四、最后

不知道什么时候的数据
在这里插入图片描述

具体过程和参考代码请见:博客

发表评论

邮箱地址不会被公开。 必填项已用*标注