苏宁图书爬虫第一版

最近一直在忙于工作,当我知道这些都是借口,毕竟某些博主大佬深夜还在更新订阅号更新微博,或许这就是自制力的差距吧。不啰嗦了,今天要写的主要是一篇关于如何爬取“苏宁图书”,当然只是半成品,但是大部分问题都已经解决,在这里记录一下发生过的问题,以免再次出现

下面切入正题,这个爬虫我主要是想练一下Scrapy框架,因为是这两天才开始自学的,断断续续,后面学前面忘,很是苦恼

主要用到的框架是Scrapy

主要用到的类是:re,json,requests

废话不多说,直接上spider

# -*- coding: utf-8 -*-
import scrapy
#from SuningBook.items import SuningbookItem
import re
import requests
import json

class SuningbooklistSpider(scrapy.Spider):
    name = 'SuningBooklist'
    allowed_domains = ['list.suning.com']
    start_urls = ['https://list.suning.com/emall/showProductList.do?ci=502320&pg=03&cp=0&cc=010']

    def parse(self, response):
        one_page_url = []
        last_30_books = "https://list.suning.com/emall/showProductList.do?ci=502320&pg=03&cp=0&cc=010&paging=1"
        li_list = response.xpath("//ul[@class='clearfix']/li")
        for li in li_list:
            # item = {}
            #获取一页中前30本书详情页地址
            one_page_url.append("https:" + li.xpath(".//a[@class='sellPoint']/@href").extract_first())
            # print (one_page_url)
            #获取一页中后30本书详情页地址
        yield scrapy.Request(
            last_30_books,
            callback = self.last_30_books,
            meta = {"one_page_url": one_page_url},
            dont_filter=True
        )


    def last_30_books(self,response):
        one_page_url = response.meta["one_page_url"]
        print ('*' * 100)
        li_list = response.xpath("//li")
        for li in li_list:
            one_page_url.append("https:" + li.xpath(".//a[@class='sellPoint']/@href").extract_first())
        print (one_page_url)
        for url in one_page_url:
            item = {}
            yield scrapy.Request(
                url,
                callback= self.purse_detail,
                meta = {"item": item},
                dont_filter= True
            )

    def purse_detail(self,response):
        print ('*'*100)
        item = response.meta["item"]
        #获取图片
        item["book_img"] = response.xpath("//div[@class='imgzoom-main']/a/img/@src").extract_first()
        if item["book_img"] is None:
            item["book_img"] = response.xpath("//div[@class='imgzoom-main']/a/img/@src2").extract_first()
        #获取书名
        item["book_title"] = response.xpath("//div[@class='proinfo-title']/h1/text()").extract_first()
        item["book_title"] = item["book_title"].replace('\n', '').replace('\r', '').replace('\t', '').replace(' ', '')
        #获取作者
        item["book_author"] = response.xpath("//ul[@class='bk-publish clearfix']/li[1]/text()").extract_first()
        item["book_author"] = item["book_author"].replace('\n', '').replace('\r', '').replace('\t', '').replace(' ', '')
        #获取图书价格(包括定价和易购价)
        # ninePartNumber = re.findall(r".*\"ninePartNumber\":\"(.*?)\",", response.body.decode())[0]
        vendorCode = re.findall(r".*\"vendorCode\":\"(.*?)\",", response.body.decode())[0]
        cmmdtyType = re.findall(r".*\"cmmdtyType\":\"(.*?)\",", response.body.decode())[0]
        catenIds = re.findall(r"\"catenIds\":\"(.*?)\",", response.body.decode())[0]
        partNumber = re.findall(r"\"partNumber\":\"(.*?)\",", response.body.decode())[0]
        weight = re.findall(r"\"weight\":\"(.*?)\"", response.body.decode())[0]
        # print (vendorCode, cmmdtyType, catenIds, partNumber, weight)
        item["ref_price"], item["net_price"] = self.book_price(vendorCode, cmmdtyType, catenIds, partNumber, weight)
        #获取出版社
        item["book_product"] = response.xpath("//ul[@class='bk-publish clearfix']/li[2]/text()").extract_first()
        if item["book_product"] == None:
            item["book_product"] = None
        #如果没有出版商,在这里获取出版时间
            item["book_sell_date"] = response.xpath("//ul[@class='bk-publish clearfix']/li[2]/span[2]/text()").extract_first()
        else:
            item["book_product"] = item["book_product"].replace('\n', '').replace('\r', '').replace('\t', '').replace(' ', '')
        #如果有出版商,在这里获取出版时间
            item["book_sell_date"] = response.xpath("//ul[@class='bk-publish clearfix']/li[3]/span[2]/text()").extract_first()
        item["book_sell_date"] = item["book_sell_date"].replace('\n', '').replace('\r', '').replace('\t', '').replace(' ', '')
        yield item
        # print (item)
        #将数据保存到txt中
        # with open(r"D:\Python-start\python-project\python-program\SuningBook\SuningBook\run-result\result.txt", 'a', encoding = 'utf-8') as f:
        #     f.write(str(item))
        #     f.write('\n')
        # print ('保存成功')


    def book_price(self, vendorCode, cmmdtyType, catenIds, partNumber, weight):
        price_url = 'https://pas.suning.com/nspcsale_0_'+ partNumber + '_' + partNumber +'_'+ vendorCode +'_10_010_0100101_502282_1000000_9017_10106_'+ cmmdtyType +'___'+ catenIds +'_'+ weight +'___.html'
        print (price_url)
        response = requests.get(price_url)
        json_str = response.content.decode().replace('pcData(', '').replace(')', '')
        json_dict = json.loads(json_str)
        # print (json_str)
        return (json_dict['data']['price']['saleInfo'][0]['refPrice'], json_dict['data']['price']['saleInfo'][0]['netPrice'])

首先说明一下,这里之所以说是半成品,因为我现在只是实现了抓取一个页面上的图书及详细内容等,还没有让页面循环起来,不过这部分已经想好要怎么去做了,但这并不是最难的部分

下面详细记录一下遇到的问题:

1.第一个问题:苏宁图书虽然在Elements中可以看到全是在一个页面中显示的,但是在实际的Network中,我们只能在相应的Response中找到一部分图书,那么剩下的部分呢?

由于苏宁图书一页显示的是60本,经过对比发现,能够在直接请求URL的响应中找到的只有30本,而且是前30本,那么后30本在哪呢?经过搜索,我发现后30本藏在了一个名叫showProductlist的文件中,而这个文件的请求地址是这样的:https://list.suning.com/emall/showProductList.do?ci=502320&pg=03&cp=0&il=0&iy=0&adNumber=0&n=1&ch=4&prune=0&sesab=ACBAAB&id=IDENTIFYING&paging=1&sub=0

经过删删减减,最终能够正常响应的最精简地址为:

https://list.suning.com/emall/showProductList.do?ci=502320&pg=03&cp=0&cc=010&paging=1

细心的我们可以明显地发现这个地址中一定是存在某些规律的,经过尝试后我发现,这个地址中,cp代表着的页码(这里从0开始),paging代表着一页中的上半页或者下半页(即0代表前30本书,1代表着后30本书),对于pg和cc还没有研究明白,后面会在研究明白后不上,这里先留着BUG1

OK,既然了解到规律,那么我们可以找到其实页面的地址,这里就不写明了,这算是遇到的第一个问题,不算麻烦

2.第二个问题:很多我们想要的信息,比如:作者,出版社,出版时间,简介等信息都在响应中可以找到,而且很好提取,但是最主要的一个信息【价格】在响应中并没有找到,那么在哪呢?

苏宁图书中存在两个价格,一个是定价,一个是易购价,这两个价格在目标链接响应中都找不到。所以只能从别的文件入手,我随机挑了一本书,将定价和易购加分别在所有的响应文件中进行检索,差不多每个关键词能找到5-7个相关联的文件,很好,这样已经将范围缩小很多了。

接下来我一个个翻看这7个文件,很幸运,在一个名叫nspcsale后面接一大串数字符号的文件中,找到了我想要的内容,而且两个价格是在一起的,一个是"refPrice":"56.0"(这个显然是定价),一个是"netPrice":"45.00"(这个就是易购价),为了证实确实如此,我后面又找了多本书,现象确实是这样的,也就是说这个nspcsale后面接一大串数字符号的文件中存放的至少有我们需要的信息,只要我们可以成功请求到这个链接,就可以轻松拿到书的价格,为什么说轻松呢?因为他是个类json字符串,只要稍加修改,就可以成为一个真正的json字符串,之后的事情就不用多说了吧【手动哈哈】

相信大家对上面那个文件的名字一定十分反感,什么叫做“nspcsale后面接一大串数字符号”,为什么我会这么说呢,因为他长成这样:

nspcsale_0_000000010674063778_000000010674063778_0070298971_10_010_0100101_502282_1000000_9017_10106_Z001___R9011194_0.01___.html?callback=pcData&_=1552205571188

而这个文件的求情地址,也只是在它名字的基础上加上了苏宁的基本网址头罢了(手动掀桌)

下面我们要弄清楚的就是那些乱七八糟的字符到底是什么,同样我将上面的链接做了精简,精简成下面这样:https://pas.suning.com/nspcsale_0_000000010674063778_000000010674063778_0070298971_10_010_0100101_502282_1000000_9017_10106_Z001___R9011194_0.01___.html

通过一个字段一个字段的检索,很幸运,这些字段中的某些值可以在最原始的响应中找到,具体的对应结果我就不在这里展示了,大家可自己找找。当然还有几个字段是固定的,或许是因为同一类型的书那几个字段是固定的,这也只是我的猜测,还没有来得及正式,后续会去证实一下,BUG2

但由于这是一个额外的请求,所以我在爬虫中只能单独写了一个函数,用来专门处理这个需要单独请求的url。问题2到这里也算是告一段落了

剩下的就是一些基础的问题了,比如数据类型,数据获取方式之类的,通过翻阅基础资料都得到了解决,特别是response.body.decode(),在这个问题上我耽误了一段时间,看来基础还是太薄弱了,接下来我会抽时间继续完成,然后解决掉上面的BUG,先暂时写到这里

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值