平时在购物过程中,各式各样的套套充斥着我们的眼球,很难判断出哪款套套好用?哪款用的人最多?哪款评价最高?今天用 Python 帮我们分析分析。

警告:本教程仅用作学习交流,请勿用作商业盈利,违者后果自负!如本文有侵犯任何组织集团公司的隐私或利益,请告知联系小编删除!!!

一、淘宝登录复习

前面我们已经介绍过了如何使用 requests 库登录淘宝,收到了很多同学的反馈和提问,猪哥感到很欣慰,同时对那些没有及时回复的同学说声抱歉!

顺便再提一下这个登录功能,代码是完全没有问题。如果你登录出现申请 st 码失败的错误时候,可以更换 _verify_password 方法中的所有请求参数。
在这里插入图片描述
在淘宝登录 2.0 改进中我们增加了 cookies 序列化的功能,目的就是为了方便爬取淘宝数据,因为如果你同一个 ip 频繁登录淘宝的话可能就会触发淘宝的反扒机制!

关于淘宝登录的成功率,在猪哥实际的使用中基本都能成功,如果不成功就按上面的方法更换登录参数!

二、淘宝商品信息爬取

这篇文章主要是讲解如何爬取数据,数据的分析放在下一篇。之所以分开是因为爬取淘宝遇到的问题太多,而猪哥又打算详细再详细的为大家讲解如何爬取,所以考虑篇幅及同学吸收率方面就分两篇讲解吧!宗旨还会不变:让小白也能看得懂!

本次爬取是调用淘宝 pc 端搜索接口,对返回的数据进行提取、然后保存为 excel 文件!

看似一个简单的功能却包含了很多问题,我们来一点一点往下看吧!

三、爬取单页数据

开始写一个爬虫项目我们都需要量化后再分步,而一般第一步便是先爬取一页试试!

1.查找加载数据 URL
我们在网页中打开淘宝网,然后登录,打开 chrome 的调试窗口,点击 network,然后勾选上 Preserve log,在搜索框中输入你想要搜索的商品名称。
在这里插入图片描述
这是第一页的请求,我们查看了数据发现:返回的商品信息数据插入到了网页里面,而不是直接返回的纯json数据!

在这里插入图片描述
2. 是否有返回纯json数据接口?

然后就好奇有没有返回纯json的数据接口呢?于是我就点了下一页(也就是第二页)
在这里插入图片描述
请求第二页后猪哥发现返回的数据竟然是纯 json,然后比较两次请求url,找到只返回 json 数据的参数!
在这里插入图片描述
通过比较我们发现搜索请求 url 中如果带 ajax=true 参数的话就直接返回 json 数据,那我们是不是可以直接模拟直接请求 json 数据!

所以猪哥就直接使用第二页的请求参数去请求数据(也就是直接请求json 数据),但是请求第一页就出现错误:
在这里插入图片描述
直接返回一个链接而 不是 json 数据,这个链接是什么鬼?点一下。。。
在这里插入图片描述
铛铛铛,滑块出现,有同学会问:用 requests 能搞定淘宝滑块吗?猪哥咨询过几个爬虫大佬,滑块的原理是收集响应时间,拖拽速度,时间,位置,轨迹,重试次数等然后判断是否是人工滑动。而且还经常变算法,所以猪哥选择放弃这条路!

3.使用请求网页接口
所以我们只能选择类似第一页(请求 url 中不带 ajax=true 参数,返回整个网页形式)的请求接口,然后再把数据提取出来!
在这里插入图片描述
这样我们就可以爬取到淘宝的网页信息了

四、提取商品属性

爬到网页之后,我们要做的就是提取数据,这里先从网页提取 json 数据,然后解析 json 获取想要的属性。

1.提取网页中商品 json 数据
既然我们选择了请求整个网页,我们就需要了解数据内嵌在网页的哪个位置,该怎么提取出来。

经过猪哥搜索比较发现,返回网页中的 js 参数:g_page_config 就是我们要的商品信息,而且也是 json 数据格式!
在这里插入图片描述
然后我们写一个正则就可以将数据提取出来了!

goods_match = re.search(r'g_page_config = (.*?)}};', response.text)

2.获取商品价格等属性
要想提取 json 数据,就要了解返回 json 数据的结构,我们可以将数据复制到一些 json 插件或在线解析
在这里插入图片描述
了解 json 数据结构之后,我们就可以写一个方法去提取我们想要的属性了。
在这里插入图片描述

五、保存为 excel

操作 excel 有很多库,网上有人专门针对excel操作库做了对比与测评感兴趣可以看看:https://dwz.cn/M6D8AQnq

选择使用 pandas 库来操作 excel,原因是 pandas 比较操作方便且是比较常用数据分析库!

1.安装库
pandas 库操作 excel 其实是依赖其他的一些库,所以我们需要安装多个库

pip install xlrd
pip install openpyxl
pip install numpy
pip install pandas

2.保存excel
在这里插入图片描述
这里有点坑的是 pandas 操作 excel 没有追加模式,只能先读取数据后使用 append 追加再写入 excel!

查看效果
在这里插入图片描述

六、批量爬取

一次爬取的整个流程(爬取、数据提取、保存)完成之后,我们就可以批量循环调用了。
在这里插入图片描述
这里设置的超时秒数是猪哥实践出来的,从 3 s、5 s到 10 s以上,太频繁容易出现验证码!
在这里插入图片描述
分多次爬取了两千多条数据。
在这里插入图片描述

七、爬取淘宝遇到的问题

爬取淘宝遇到了非常多的问题,这里为大家一一列举:

1.登录问题
在这里插入图片描述
问题:申请 st 码失败怎么办?
回答:更换 _verify_password 方法中的所有请求参数。

参数没问题的话登录基本都会成功!

2.代理池

为了防止自己的 ip 被封,猪哥使用了代理池。爬取淘宝需要高质量的ip 才能爬取,猪哥试了很多网上免费的 ip,基本都不能爬取。
在这里插入图片描述
但是有一个网站的 ip 很好 站大爷:http://ip.zdaye.com/dayProxy.html ,这个网站每小时都会更新一批 ip,猪哥试过还是有很多 ip 是可以爬取淘宝的。

3.重试机制

为了防止正常请求失败,猪哥在爬取的方法上加上了重试机制!
在这里插入图片描述
需要安装 retry 库

pip install retry

4.出现滑块

上面那些都没问题,但是还是会出现滑块,猪哥测试过很多次,有些爬取 20 次- 40 次左右最容易出现滑块。
在这里插入图片描述
出现滑块只能等个半小时后继续爬,因为目前还不能使用 requests 库解决滑块,后面学习 selenium 等其他框架看看是否能解决!

5.目前这只爬虫

目前这只爬虫并不完善,只能算是半成品,有很多可以改进的地方,比如自动维护 ip 池功能,多线程分段爬取功能,解决滑块问题等等,后面我们一起来慢慢完善这只爬虫,使他可以成为一只完善懂事的爬虫!

import re
import os
import json

import requests

'''
遇到不懂的问题?Python学习交流群:1136201545满足你的需求,资料都已经上传群文件,可以自行下载!
'''

s = requests.Session()
# cookies序列化文件
COOKIES_FILE_PATH = 'taobao_login_cookies.txt'


class TaoBaoLogin:

    def __init__(self, session):
        """
        账号登录对象
        :param username: 用户名
        :param ua: 淘宝的ua参数
        :param TPL_password2: 加密后的密码
        """
        # 检测是否需要验证码的URL
        self.user_check_url = 'https://login.taobao.com/member/request_nick_check.do?_input_charset=utf-8'
        # 验证淘宝用户名密码URL
        self.verify_password_url = "https://login.taobao.com/member/login.jhtml"
        # 访问st码URL
        self.vst_url = 'https://login.taobao.com/member/vst.htm?st={}'
        # 淘宝个人 主页
        self.my_taobao_url = 'http://i.taobao.com/my_taobao.htm'

        # 淘宝用户名
        self.username = ''
        # 淘宝重要参数,从浏览器或抓包工具中复制,可重复使用
        self.ua = '120#bX1bSEvjRMp2ze/4jvYZMs/fbcomv9OI3Z7HiwbBdwWe5C9weI+bNAODPeK7mAeX74EwzlBOp79GXyKATeKcH+75j64avOVN+mTcF2I59mFf5dA2vLLFVoFN/YwwDHtkmlIDnhIwRNBOWCSF2P+ELE+DfWY2DSWnbD0BdSzH2eb9ifkk/woTLHo04BUZww5HlHCfbcsYsJfeekLliXIXSERUyn+medmJbccH0zTz7+4upEgVj2eDUH7NwCY5C2eozwi7gMqBjbAGHsN9vh/roEcPbE/ejUMPEIhSO5O3E/h5y12b7UHgWMfbLvv2VlMPDC0Zv5HHyB8/yvYS7U4RNWt/EDht7CoRyEuSb5htujPMybYb75UyNPc8CoYyMbgcVOKib1G74JiugnfIv/0j8aFMqOvxt9oioQpO6FjFXN9n40fS/WCFEEXLqD+sb/1VBb0aIxpmaKnIkALWg5PbFYSSBX2K+1tc07NOiWG6jjuzl6P7/QiNidfvmALZ+aR/LwuURI9PJWGV3ssr+U2Mtpw48o3JARdyZwAEzwrUrc4Vuz6aCYMQ8uPDSgja1tpLtbv7WULp3L3U7ymExWRqZ9Ghr0drjSupU7WqtWU+2LTvq1mphGnKHUFLN7EA4333WL6ufy9j5A0kIt1dNO7QzncwUpeUKmk2ElvvzjY+V2C6G0bKFE9QAcPa8oeLXZ/69DN10q8/hWX5BHmaM8acZ20kxRJlAypphFaR6UJ/8Nf3ldkUJVURw2fUa08hVSb0O6x6Ya2Jmxtg+VlZ3jxSK2lajQJgrGvsiXHejhaiC3Wb+gPoo/lZLRXH3bp/XkW4dFoqAy3vFCXLAFHuphEZ0YQVyjfJd43pZJe/Ead7F3QDsSILQRCN5qBKXcI5CWFZjvDZLDJQAfy+eOqB4Ebrmu2HYondSHTUFE6ihDoQlJzzUPZNDJU/YF/tYU8HLZFrMb5pP5XaBFTAgLbfOBtFLVJuZgmZYJwjK7Ybe6qWbSTld9TPaGyWhS+1hXud1iiquzDPxQ8AyMBGp179yPvwyNmp3jJH6sH6/Ug4i2nRFBnMkEHwHMyug1+YfnCqx/R1bnS5OJLOQUHD6hiySAkEg8p6ctwjPtkZnaHLPSBpQKh/I9dB6Vr84/fLI2O3le1se/DA6drRuZKE6s2lTYX3pRz+ZIYrpjKCCkIwKvcMUwJ6QLvG8LFjmA3/B+QI5Mrf6LEq3KZBB344KceXdzekSnno+XQ0lr7ZSVzSP5S/t/tb1PUb+ThSyg0GAORLEky4dGYsBxUtWuqThI9XDocYFxJhfW0zLF1WeIHQ7TnDx0EY0QE2VzfDkvO2ZEN/6i5IsBaMtmBDWGEceNktMG4w5hkITuTKt2fIJqHcc8qbNGFjJXxhCq1MvyFInY//asgUSqcpTSJ3q30E6P0HfnMSq2qx4dbBjsf82WJhA8OvjYmkNA9mM6COw0uL2ViR1vgYMFoB3vJXBvp4jeF4QU35ICEaxo+hT4LiEplhwRbPF2A0QWu+i7WoVABGta1u01e3JWiVr871aQ9JCw+pgTOU+EBUceLR7whdC9FFXXIcWuNoqAwib5BbTN3q34EvPbJ6Zx2wD9NsUTHuHqz78/voSKfkzH61Ba56L9eeXuzxNKGg8gLPk5AUdSIBLZejsRwg2lsZh6rdiEUtgbuBb/JscO//vVi/WkmrB424s+SlfJxnFYRiR8qiZi1+yY3HBuLFOYktMxhN7u0P8TPUT8UGQr4WPi8a+3qrIk4oOgFd+IY3d7DnFQtbeJ7lMyrMnvfWgEuuZnMhSAPEtyfu6sypunUH5ouwCcB+AJ0VRgG63ewKh7juRT2jOb89YqhGJ92n4RwMr8RSaBrol9nAvSUVAXZHZQkUBum32YAsTXaDDX1lKT3ATcQc'
        # 加密后的密码,从浏览器或抓包工具中复制,可重复使用
        self.TPL_password2 = '48b989ba5227430fde9317aab6d9f01c751b70ab66f716b205277add6d34e5d59386af23b970c76a3a95a7c42ff6e38b53d9c6b62c4cb94ad65db52b0733f5ef1e5682385daf204057f989b59c694ecd8912d6912696f2f94a63e9457b32b4c462afab6e503853c54a7fab236728404f52dd87c93e8de60ee0086789a1b1710e'

        # 请求超时时间
        self.timeout = 3
        # session对象,用于共享cookies
        self.session = session

        if not self.username:
            raise RuntimeError('请填写你的淘宝用户名')

    def _user_check(self):
        """
        检测账号是否需要验证码
        :return:
        """
        data = {
            'username': self.username,
            'ua': self.ua
        }
        try:
            response = self.session.post(self.user_check_url, data=data, timeout=self.timeout)
            response.raise_for_status()
        except Exception as e:
            print('检测是否需要验证码请求失败,原因:')
            raise e
        needcode = response.json()['needcode']
        print('是否需要滑块验证:{}'.format(needcode))
        return needcode

    def _verify_password(self):
        """
        验证用户名密码,并获取st码申请URL
        :return: 验证成功返回st码申请地址
        """
        verify_password_headers = {
            'Connection': 'keep-alive',
            'Cache-Control': 'max-age=0',
            'Origin': 'https://login.taobao.com',
            'Upgrade-Insecure-Requests': '1',
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36',
            'Content-Type': 'application/x-www-form-urlencoded',
            'Referer': 'https://login.taobao.com/member/login.jhtml?from=taobaoindex&f=top&style=&sub=true&redirect_url=https%3A%2F%2Fi.taobao.com%2Fmy_taobao.htm',
        }
        # 登录toabao.com提交的数据,如果登录失败,可以从浏览器复制你的form data
        verify_password_data = {
            'TPL_username': self.username,
            'ncoToken': '78401cd0eb1602fc1bbf9b423a57e91953e735a5',
            'slideCodeShow': 'false',
            'useMobile': 'false',
            'lang': 'zh_CN',
            'loginsite': 0,
            'newlogin': 0,
            'TPL_redirect_url': 'https://s.taobao.com/search?q=%E9%80%9F%E5%BA%A6%E9%80%9F%E5%BA%A6&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',
            'from': 'tb',
            'fc': 'default',
            'style': 'default',
            'keyLogin': 'false',
            'qrLogin': 'true',
            'newMini': 'false',
            'newMini2': 'false',
            'loginType': '3',
            'gvfdcname': '10',
            # 'gvfdcre': '68747470733A2F2F6C6F67696E2E74616F62616F2E636F6D2F6D656D6265722F6C6F676F75742E6A68746D6C3F73706D3D61323330722E312E3735343839343433372E372E33353836363032633279704A767526663D746F70266F75743D7472756526726564697265637455524C3D6874747073253341253246253246732E74616F62616F2E636F6D25324673656172636825334671253344253235453925323538302532353946253235453525323542412532354136253235453925323538302532353946253235453525323542412532354136253236696D6766696C65253344253236636F6D6D656E64253344616C6C2532367373696425334473352D652532367365617263685F747970652533446974656D253236736F75726365496425334474622E696E64657825323673706D253344613231626F2E323031372E3230313835362D74616F62616F2D6974656D2E31253236696525334475746638253236696E69746961746976655F69642533447462696E6465787A5F3230313730333036',
            'TPL_password_2': self.TPL_password2,
            'loginASR': '1',
            'loginASRSuc': '1',
            'oslanguage': 'zh-CN',
            'sr': '1440*900',
            'osVer': 'macos|10.145',
            'naviVer': 'chrome|76.038091',
            'osACN': 'Mozilla',
            'osAV': '5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36',
            'osPF': 'MacIntel',
            'appkey': '00000000',
            'mobileLoginLink': 'https://login.taobao.com/member/login.jhtml?redirectURL=https://s.taobao.com/search?q=%E9%80%9F%E5%BA%A6%E9%80%9F%E5%BA%A6&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&useMobile=true',
            'showAssistantLink': '',
            'um_token': 'TD0789BC99BFBBF893B3C8C0E1729CCA3CB0469EA11FF6D196BA826C8EB',
            'ua': self.ua
        }
        try:
            response = self.session.post(self.verify_password_url, headers=verify_password_headers, data=verify_password_data,
                              timeout=self.timeout)
            response.raise_for_status()
            # 从返回的页面中提取申请st码地址
        except Exception as e:
            print('验证用户名和密码请求失败,原因:')
            raise e
        # 提取申请st码url
        apply_st_url_match = re.search(r'<script src="(.*?)"></script>', response.text)
        # 存在则返回
        if apply_st_url_match:
            print('验证用户名密码成功,st码申请地址:{}'.format(apply_st_url_match.group(1)))
            return apply_st_url_match.group(1)
        else:
            raise RuntimeError('用户名密码验证失败!response:{}'.format(response.text))

    def _apply_st(self):
        """
        申请st码
        :return: st码
        """
        apply_st_url = self._verify_password()
        try:
            response = self.session.get(apply_st_url)
            response.raise_for_status()
        except Exception as e:
            print('申请st码请求失败,原因:')
            raise e
        st_match = re.search(r'"data":{"st":"(.*?)"}', response.text)
        if st_match:
            print('获取st码成功,st码:{}'.format(st_match.group(1)))
            return st_match.group(1)
        else:
            raise RuntimeError('获取st码失败!response:{}'.format(response.text))

    def login(self):
        """
        使用st码登录
        :return:
        """
        # 加载cookies文件
        if self._load_cookies():
            return True
        # 判断是否需要滑块验证
        self._user_check()
        st = self._apply_st()
        headers = {
            'Host': 'login.taobao.com',
            'Connection': 'Keep-Alive',
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36'
        }
        try:
            response = self.session.get(self.vst_url.format(st), headers=headers)
            response.raise_for_status()
        except Exception as e:
            print('st码登录请求,原因:')
            raise e
        # 登录成功,提取跳转淘宝用户主页url
        my_taobao_match = re.search(r'top.location.href = "(.*?)"', response.text)
        if my_taobao_match:
            print('登录淘宝成功,跳转链接:{}'.format(my_taobao_match.group(1)))
            self._serialization_cookies()
            return True
        else:
            raise RuntimeError('登录失败!response:{}'.format(response.text))

    def _load_cookies(self):
        # 1、判断cookies序列化文件是否存在
        if not os.path.exists(COOKIES_FILE_PATH):
            return False
        # 2、加载cookies
        self.session.cookies = self._deserialization_cookies()
        # 3、判断cookies是否过期
        try:
            self.get_taobao_nick_name()
        except Exception as e:
            os.remove(COOKIES_FILE_PATH)
            print('cookies过期,删除cookies文件!')
            return False
        print('加载淘宝登录cookies成功!!!')
        return True

    def _serialization_cookies(self):
        """
        序列化cookies
        :return:
        """
        cookies_dict = requests.utils.dict_from_cookiejar(self.session.cookies)
        with open(COOKIES_FILE_PATH, 'w+', encoding='utf-8') as file:
            json.dump(cookies_dict, file)
            print('保存cookies文件成功!')

    def _deserialization_cookies(self):
        """
        反序列化cookies
        :return:
        """
        with open(COOKIES_FILE_PATH, 'r+', encoding='utf-8') as file:
            cookies_dict = json.load(file)
            cookies = requests.utils.cookiejar_from_dict(cookies_dict)
            return cookies

    def get_taobao_nick_name(self):
        """
        获取淘宝昵称
        :return: 淘宝昵称
        """
        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36'
        }
        try:
            response = self.session.get(self.my_taobao_url, headers=headers)
            response.raise_for_status()
        except Exception as e:
            print('获取淘宝主页请求失败!原因:')
            raise e
        # 提取淘宝昵称
        nick_name_match = re.search(r'<input id="mtb-nickname" type="hidden" value="(.*?)"/>', response.text)
        if nick_name_match:
            print('登录淘宝成功,你的用户名是:{}'.format(nick_name_match.group(1)))
            return nick_name_match.group(1)
        else:
            raise RuntimeError('获取淘宝昵称失败!response:{}'.format(response.text))


if __name__ == '__main__':
    ul = TaoBaoLogin(s)
    ul.login()
    ul.get_taobao_nick_name()

本文转载:CSDN博客