Python爬取淘宝商品信息

爬虫原理

爬虫需要做如下事情: 
1. 模拟对服务端的Request请求; 

2. 接收Response 内容并解析、提取所需信息;

简单来说,就是模仿浏览器浏览网页信息。

实战讲解

在淘宝首页输入商品数据,搜索出来的商品信息是ajax动态加载出来的,这样的信息再源代码的是找不到,于是爬取这些信息可以选择selenium或者找到这个js文件进行解析,本文这次是抓到这个js文件进行解析的,首先打开淘宝页面,本文以搜索python为例

下面我们来分析爬取的页面结构。利用re正则表达式可以得到想要的结果。

先来看看网页源码。

我们使用的是Chrome浏览器,在上述搜索页面中,按F12开发者工具。可以看到如下页面

我们选择搜索页面点击进去,

从Headers里面可以看到Request URL地址,可以看到使用的是GET请求。

https://s.taobao.com/search?q=python&imgfile=&js=1&stats_click=search_radio_all%3A1&initiative_id=staobaoz_20180305&ie=utf8

搜索首页面任意输入商品搜索,发现规律,其变化参数如上图Query String Parameters,我们重点关注其中两个:

q:代表输入的搜索词,用来作为查询关键词

initiative_id=staobaoz_20180305 内部参数,带时间戳的显示控制方式

其他的比如

stats_click= 点击方式 

ie:显示编码方式

还有常见的

trackid=17 流量来源 17是钻展、1是直通车、2是淘客

isb= 是否商城

shop_type= 店铺类型

base64=1 手机端编码,支持功能手机

sort=排序方式,default是综合排序,renqi-desc是人气排名,sale-desc是销量排名,oldstart是新品排序

loc= 城市

paytype= 支付方式

topSearch=1 搜索显示方式

sid= 请求签名,手机端验证使用

from= 来源位置 spm是具体的点,一个页面什么位置,from是区块

ppath=属性

brand品牌

stats_click= 点击方式

tab=mall 区域 mall是天猫 old是二手 g是全球购

bucketid 内部参数,应该是显示控制方式

基于分析,我们定义一个简单搜索页面url

first_url = "https://s.taobao.com/search?imgfile=&js=1&stats_click=search_radio_all%3A1&ie=utf8"我

我们使用requests.get()用于请求目标网站,类型是一个HTTPresponse类型

使用带参数的GET请求:

先将参数填写在dict中,发起请求时params参数指定为dict

find_arg = {
    'q': find_word,
    'initiative_id': 'staobaoz_%s%02d%02d' % (t[0], t[1], t[2])
}
# 搜索页面url
# https://s.taobao.com/search?q=python&imgfile=&js=1&stats_click=search_radio_all%3A1&initiative_id=staobaoz_20180305&ie=utf8
first_url = "https://s.taobao.com/search?imgfile=&js=1&stats_click=search_radio_all%3A1&ie=utf8"

# url = 'https://s.taobao.com/search?q=python&imgfile=&commend=all&ssid=s5-e&search_type=item&sourceId=tb.index&spm=a21bo.2017.201856-taobao-item.1&ie=utf8&initiative_id=tbindexz_20170306'
# 发送请求
response = requests.get(first_url,params=find_arg)
html = response.text
接下来就是提取,筛选,清洗数据

翻页:

点击商品页面第二页,我们发现JS中多了很多个请求,如图,这里有隐藏着商品信息的js文件。注意,为了便于实时观察,我们可以先将上次的network信息clear(红色圓点旁边那个带斜杠的灰色圆圈)

同样观察Request URL,多试几次发现地址拼接规律。接下来我们就进行模拟请求发送。

首先找到获得这个文件的链接,如图,

https://s.taobao.com/search?data-key=s&data-value=44&ajax=true&_ksTS=1520221609561_908&callback=jsonp909&q=python&imgfile=&js=1&stats_click=search_radio_all%3A1&initiative_id=staobaoz_20180305&ie=utf8&bcoffset=4&ntoffset=0&p4ppushleft=1%2C48

参数有:

Query String Parameters
data-key:s
data-value:44
ajax:true
_ksTS:1520221609561_908
callback:jsonp909
q:python
imgfile:
js:1
stats_click:search_radio_all:1
initiative_id:staobaoz_20180305
ie:utf8
bcoffset:4
ntoffset:0
p4ppushleft:1,48

其实这个链接很复杂,但是我们可以修改,吧其他东西去掉,剩下这样的

https://s.taobao.com/search?data-key=s&;data-value=44&;ajax=true&;_ksTS=1479917597216_854&;callback&;q={},
大括号里面的是我们自己填充的搜索内容,访问修改过的网址跟hearers里面的那个网址产生的内容是一样的

,于是我们使用这个简化版的url 

继续对淘宝的页面进行Inspect,发现每一框的店铺数据储存在g_page_config中的auctions中,并且g_page_config中的数据为json格式。


获取原始数据 (g_page_config以及auctions)

在Python中,可以通过专门的解析器来完成。通过其中的loads 和dumps 可以轻易的在json以及str之间做转换。将str转换为json格式,在通过pandas 的获取json中的数据信息。
 

每一框店铺的数据涵括非常丰富的店铺信息以及销售信息,我们可以收集宝贝分类(category)、评论数(comment_count)、宝贝位置(item_loc)、店铺名称(nick)、宝贝名称(raw_title)、原价格(reserve_price)、显示价格(view_price)、销量(view_sales)进行分析.

具体分析过程:

依然在F12页面,我们之前浏览的三Headers信息,现在我们点击Response,可以看到json格式的字符串,为了便于观察分析,我们将其拷贝到在线json解析工具,并转换。

在线转换:

由此一层层的结构关系显而易见。

最终结果展示:

获得数据后我们可以利用画图工具进行饼状图,直方图,折线图等各自图表分析。本文不作详细探讨,后续再聊。

全部代码如下:

# !/usr/bin/python3
# -*- coding:utf-8 -*-
__auther__ = 'gavin'

import requests
import re
import json
import time
from hashlib import md5
import xlwt

# 数据
DATA = []

t = time.localtime()
# 搜索关键字
find_word = 'python'
# 参数
find_arg = {
    'q': find_word,
    'initiative_id': 'staobaoz_%s%02d%02d' % (t[0], t[1], t[2])
}
# 搜索页面url
# https://s.taobao.com/search?q=python&imgfile=&js=1&stats_click=search_radio_all%3A1&initiative_id=staobaoz_20180305&ie=utf8
first_url = "https://s.taobao.com/search?imgfile=&js=1&stats_click=search_radio_all%3A1&ie=utf8"

# url = 'https://s.taobao.com/search?q=python&imgfile=&commend=all&ssid=s5-e&search_type=item&sourceId=tb.index&spm=a21bo.2017.201856-taobao-item.1&ie=utf8&initiative_id=tbindexz_20170306'
# 发送请求
response = requests.get(first_url,params=find_arg) #response.json()方法同json.loads(response.text)
html = response.text
# 提取,筛选,清洗数据
content = re.findall(r'g_page_config = (.*?)g_srp_loadCss',html,re.S)[0] # 正则表达式处理的结果是一个列表,取第一个元素(字典)

# 格式化,将json格式的字符串切片
content = content.strip()[:-1]
# 将json转为dict
content = json.loads(content)

# 借助json在线解析分析,取dict里的具体data
data_list = content['mods']['itemlist']['data']['auctions']


# 提取数据
for item in data_list:
    temp = {
        'title' : item['title'],
        'view_price' : item['view_price'],
        'view_sales' : item['view_sales'],
        'view_fee' : '否' if float(item['view_fee']) else '是' ,
        'isTmall' : '是' if item['shopcard']['isTmall'] else '否',
        'area' : item['item_loc'],
        'name' : item['nick'],
        'detail_url' : item['detail_url'],
    }
    DATA.append(temp)

print(len(DATA)) # 36 首页有12条异步加载的数据 ,应该是48

# 保存一下cookie
cookie_ = response.cookies

# 首页有12条异步加载的数据
# url2 = 'https://s.taobao.com/api?_ksTS=1520072935603_226&callback=jsonp227&ajax=true&m=customized&sourceId=tb.index&q=python&spm=a21bo.2017.201856-taobao-item.1&s=36&imgfile=&initiative_id=tbindexz_20170306&bcoffset=0&commend=all&ie=utf8&rn=e061ba6ab95f8c06a23dbe5bfe9a5d94&ssid=s5-e&search_type=item'
ksts = str(int(time.time() * 1000))
url2 = "https://s.taobao.com/api?_ksTS={}_219&callback=jsonp220&ajax=true&m=customized&stats_click=search_radio_all:1&q=java&s=36&imgfile=&bcoffset=0&js=1&ie=utf8&rn={}".format(
    ksts, md5(ksts.encode()).hexdigest())
# 发送请求
response2 = requests.get(url2, params=find_arg, cookies=cookie_)

html2 = response2.text
# print(html2)

data_list = json.loads(re.findall(r'{.*}',html2,re.S)[0])['API.CustomizedApi']['itemlist']['auctions']

# 提取数据
for item in data_list:
    temp = {
        'title' : item['title'],
        'view_price' : item['view_price'],
        'view_sales' : item['view_sales'],
        'view_fee' : '否' if float(item['view_fee']) else '是' ,
        'isTmall' : '是' if item['shopcard']['isTmall'] else '否',
        'area' : item['item_loc'],
        'name' : item['nick'],
        'detail_url' : item['detail_url'],
    }
    DATA.append(temp)

print(len(DATA)) # +12 首页有12条异步加载的数据


# 翻页
get_args = {}
cookies = response2.cookies #更新一下cookies
for i in range(1,10):
    ktsts = time.time()
    get_args['_ksTS'] = "%s_%s" % (int(ktsts*1000),str(ktsts)[-3:])
    get_args['callback'] = "jsonp%s" % (int(str(ktsts)[-3:]) + 1)
    get_args['data-value'] = 44 * i
    get_args['q'] = 'python'

    #url = 'https://s.taobao.com/search?data-key=s&data-value=44&ajax=true&_ksTS=1520091448743_4613&callback=jsonp4614&q=python&imgfile=&commend=all&ssid=s5-e&search_type=item&sourceId=tb.index&spm=a21bo.2017.201856-taobao-item.1&ie=utf8&initiative_id=tbindexz_20170306&bcoffset=4&ntoffset=0&p4ppushleft=1%2C48&s=0'
    url = "https://s.taobao.com/search?data-key=s&data-value=44&ajax=true&imgfile=&js=1&stats_click=search_radio_all%3A1&ie=utf8&bcoffset=4&ntoffset=0&p4ppushleft=1%2C48".format(
        time.time())

    if i > 1:
        get_args['s'] = 44*(i-1)
    r3 = requests.get(url,params=get_args,cookies = cookies)
    html = r3.text
    content = re.findall(r'{.*}',html,re.S)[0]
    content = json.loads(content)
    # print(content['mods']['itemlist']['data']['auctions'])
    data_list = content['mods']['itemlist']['data']['auctions']

    # 提取数据
    for item in data_list:
        temp = {
            'title': item['title'],
            'view_price': item['view_price'],
            'view_sales': item['view_sales'],
            'view_fee': '否' if float(item['view_fee']) else '是',
            'isTmall': '是' if item['shopcard']['isTmall'] else '否',
            'area': item['item_loc'],
            'name': item['nick'],
            'detail_url': item['detail_url'],
        }
        DATA.append(temp)

    cookie_ = r3.cookies

    print(len(DATA))  # +12 首页有12条异步加载的数据

    # exit(0) # for test 1 times

# 分析
'''
# 画图
data1 = { '包邮':0, '不包邮':0}
data2 = {'天猫':0, '淘宝':0}
# 地区分布
data3 = {}

for item in DATA:
    if item['view_fee'] == '否':
        data1['不包邮'] += 1
    else:
        data1['包邮'] += 1

    if item['isTmall'] == '是':
        data1['天猫'] += 1
    else:
        data1['淘宝'] += 1

    data3[ item['area'].split(' ')[0] ] = data3.get(item['area'].split(' ')[0], )
print(data1)
draw.pie(data1,'是否包邮')
draw.pie(data2,'是否天猫')
draw.bar(data3,'地区分布')

'''


# 持久化
f = xlwt.Workbook(encoding='utf-8')
sheet01 = f.add_sheet(u'sheet1',cell_overwrite_ok=True)
# 写标题
sheet01.write(0,0,'标题')
sheet01.write(0,1,'标价')
sheet01.write(0,2,'购买人数')
sheet01.write(0,3,'是否包邮')
sheet01.write(0,4,'是否天猫')
sheet01.write(0,5,'地区')
sheet01.write(0,6,'店名')
sheet01.write(0,7,'url')

# write data
for i in range(len(DATA)):
    sheet01.write(i + 1, 0, DATA[i]['title'])
    sheet01.write(i + 1, 1, DATA[i]['view_price'])
    sheet01.write(i + 1, 2, DATA[i]['view_sales'])
    sheet01.write(i + 1, 3, DATA[i]['view_fee'])
    sheet01.write(i + 1, 4, DATA[i]['isTmall'])
    sheet01.write(i + 1, 5, DATA[i]['area'])
    sheet01.write(i + 1, 6, DATA[i]['name'])
    sheet01.write(i + 1, 7, DATA[i]['detail_url'])

f.save(u'搜索%s的结果.xls' % find_word) # 'python'


本文采用的面向过程编程,主要用于学习爬虫,分析数据等。若在实战项目中,可以利用函数式编程或者面向对象风格。

附:百度爬图

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
'''
@File    :   spider_baidu.py    
@Contact :   476423448@qq.com
@License :   (C)Copyright 2020-2021, AIgroup-KPCQ
爬取百度图片 爬虫 输入关键字和页数即可
@Modify Time      @Author    @Version    @Desciption
------------      -------    --------    -----------
7/17/21 2:21 PM   gavin      1.0         None
'''

import requests
from threading import Thread
import re
import time
import hashlib
import os

import requests
import os
import urllib

class Spider_baidu_image():
    def __init__(self,path):
        self.url = 'http://image.baidu.com/search/acjson?'
        self.headers = {
            'User-Agent':'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36' }
        self.headers_image = {
            'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36',
            'Referer': 'https://image.baidu.com/search/index?tn=baiduimage&ct=201326592&lm=-1&cl=2&ie=gb18030&word=%D3%EA%DA%A1&fr=ala&ala=1&alatpl=normal&pos=0'
        }
        self.keyword = input("请输入搜索图片关键字:")
        self.paginator = int(input("请输入搜索页数,每页30张图片:"))
        self.path = path
        # self.paginator = 50
        # print(type(self.keyword),self.paginator)
        # exit()

    def get_param(self):
        """
        获取url请求的参数,存入列表并返回
        :return:
        """
        keyword = urllib.parse.quote(self.keyword)
        params = []
        for i in range(1, self.paginator + 1):
            params.append(
                'tn=resultjson_com&ipn=rj&ct=201326592&is=&fp=result&queryWord={}&cl=2&lm=-1&ie=utf-8&oe=utf-8&adpicid=&st=-1&z=&ic=&hd=1&latest=0&copyright=0&word={}&s=&se=&tab=&width=&height=&face=0&istype=2&qc=&nc=1&fr=&expermode=&force=&cg=star&pn={}&rn=30&gsm=78&1557125391211='.format(
                    keyword, keyword, 30 * i))
        return params

    def get_urls(self, params):
        """
        由url参数返回各个url拼接后的响应,存入列表并返回
        :return:
        """
        urls = []
        for i in params:
            urls.append(self.url + i)
        return urls

    def get_image_url(self, urls):
        image_url = []
        for url in urls:
            json_data = requests.get(url, headers=self.headers).json()
            json_data = json_data.get('data')
            for i in json_data:
                if i:
                    image_url.append(i.get('thumbURL'))
        return image_url

    def get_image(self, image_url):
        """
        根据图片url,在本地目录下新建一个以搜索关键字命名的文件夹,然后将每一个图片存入。
        :param image_url:
        :return:
        """
        #cwd = os.getcwd()
        file_name = os.path.join(self.path, self.keyword)
        if not os.path.exists(self.keyword):
            os.makedirs(file_name)
        for index, url in enumerate(image_url, start=1):
            file_save = os.path.join(file_name,'{}.jpg'.format(index))
            with open(file_save, 'wb') as f:
                f.write(requests.get(url, headers=self.headers_image).content)
            if index != 0 and index % 30 == 0:
                print('{}第{}页下载完成'.format(self.keyword, index / 30))

    def __call__(self, *args, **kwargs):
        params = self.get_param()
        urls = self.get_urls(params)
        image_url = self.get_image_url(urls)
        self.get_image(image_url)


# if __name__ == '__main__':
#     spider = Spider_baidu_image()
#     spider()


class BaiDu(object):
    """
    爬取百度图片
    """
    def __init__(self, name, page, path):
        self.start_time = time.time()
        self.name = name
        self.page = page
        #self.url = 'https://image.baidu.com/search/acjson?tn=resultjson_com&ipn=rj&rn=60&'
        self.url = 'https://image.baidu.com/search/acjson'
        self.header = {'User-Agent':'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36' }# 添加为自己的
        self.num = 0
        self.path = path

    def queryset(self):
        """
        将字符串转换为查询字符串形式
        """
        pn = 0
        for i in range(int(self.page)):
            pn += 60 * i
            name = {'word': self.name, 'pn': pn, 'tn':'resultjson_com', 'ipn':'rj', 'rn':60}
            url = self.url
            self.getrequest(url, name)

    def getrequest(self, url, data):
        """
        发送请求
        """
        print('[INFO]: 开始发送请求:' + url)
        ret = requests.get(url, headers=self.header, params=data)

        if str(ret.status_code) == '200':
            print('[INFO]: request 200 ok :' + ret.url)
        else:
            print('[INFO]: request {}, {}'.format(ret.status_code, ret.url))

        response = ret.content.decode()
        img_links = re.findall(r'thumbURL.*?\.jpg', response)
        links = []
        # 提取url
        for link in img_links:

            links.append(link[11:])

        self.thread(links)

    def saveimage(self, link):
        """
        保存图片
        """
        print('[INFO]:正在保存图片:' + link)
        m = hashlib.md5()
        m.update(link.encode())
        name = m.hexdigest()
        ret = requests.get(link, headers = self.header)
        image_content = ret.content
        if not os.path.exists(self.path + self.name):
            os.mkdir(self.path + self.name )
        filename = self.path + self.name + '/'  + name + '.jpg'
        with open(filename, 'wb') as f:
            f.write(image_content)
        print('[INFO]:保存成功,图片名为:{}.jpg'.format(name))

    def thread(self, links):
        """多线程"""
        self.num +=1
        for i, link in enumerate(links):
            print('*'*50)
            print(link)
            print('*' * 50)
            if link:
                # time.sleep(0.5)
                t = Thread(target=self.saveimage, args=(link,))
                t.start()
                # t.join()
            self.num += 1
        print('一共进行了{}次请求'.format(self.num))

    def __del__(self):

        end_time = time.time()
        print('一共花费时间:{}(单位秒)'.format(end_time - self.start_time))

def main():
    # names = ['冰雹', '降雪', '降雨', '结冰', '露', '霜', '雾霾', '雾凇', '雨凇']
    # for name in names:
    name = '雨凇'
    page = 10
    baidu = BaiDu(name, page, '/media/gavin/home/gavin/DataSet/tmp')
    baidu.queryset()


if __name__ == '__main__':
    #main()

    spider = Spider_baidu_image('/media/gavin/home/gavin/DataSet/tmp')
    spider()

评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值