爬取淘宝商品信息
爬取淘宝商品信息

爬取淘宝商品信息

1.前言

这几天学习了Re库,正好可以用这个爬取淘宝的实例来加深一下印象,因为自己能力还不够,所以是跟着大佬们的脚步一起做的,对代码进行分析然后弄明白,在能力范围内也进行了一定的修改。


2.案例分析

先给出大佬的博客:

https://buwenbuhuo.blog.csdn.net/article/details/104987737

因为大佬写的不是很详细,所以我会按照自己的思路把每一行代码解释清楚

2.1.目标站点分析

我们要爬取的是淘宝网,url链接:https://www.taobao.com

查看robots协议,发现只有百度蜘蛛不能爬(吃瓜)

因为要爬取商品信息,所以我们在搜索栏随便输入一个商品名称,进入搜索结果一页,这里以红领巾为例,得到url地址:

取出文本:https://s.taobao.com/search?q=%E7%BA%A2%E9%A2%86%E5%B7%BE&imgfile=&js=1&stats_click=search_radio_all%3A1&initiative_id=staobaoz_20210127&ie=utf8

多次输入商品名称就会发现,前面的search?q=都是固定的,=后面就是输入的商品,只不过浏览器自动对url地址的中文部分进行了url解码,单独取出后看到的是没有被解码的url地址

还要注意的是进行爬取时需要模拟浏览器登录,因为现在很多网站都是需要登录才能查看信息的,如果不登录搜索时会跳转到登录页面,所以需要我们在GET操作时加入header信息,需要包含cookie在内

本站也是没有反爬措施,所以不会太难


2.2.代码分析

首先我们根据代码的功能可以把完整代码分成四个区块,1.爬取页面,2.录入数据,3.数据输出,4.主函数调用子函数,代码分区可以让代码更加简洁易读,并且可以提高代码的复用性。

1.主函数

因为要有个大的框架,所以我们先写主函数,再写子函数,写完把主函数放在最后就好了。这里只用到了requests和re两个库。

主函数主要是调用函数加上基本的输入输出和搜索规则限制

这里我们给出主函数代码:

  1. def main():
  2.     goods = input("请输入要爬取的商品:")
  3.     depth = eval(input("请输入爬取页数:"))  # 爬取深度
  4.     start_url = 'https://s.taobao.com/search?q=' + goods
  5.     indolist = []
  6.     for i in range(depth):
  7.         try:
  8.             url = start_url + '&s=' + str(44 * i)
  9.             html = getHTMLText(url)
  10.             parsePage(indolist, html)
  11.         except:
  12.             continue
  13.     printGoodlist(indolist)

因为每一页的url地址都是不同的,我们取出前四页的url地址放在一起比较

可以看到每个地址最后都有&s=,而44恰好是一页商品的数量,在拼接url地址时要加上这个变量,因为地址中都是字符型,所以我们要加上str(),然后用+将元素拼接起来

同时,我们要将每一页都进行遍历,遍历的结果都存入indolist列表中,最后一次性输出,通常我们在遍历时要用try…except捕获异常,保证在出现异常时程序可以正常运行

2.爬取页面

跟普通的爬取不同的时,我们在这里要加入模拟浏览器信息,主要是user-agent和cookie,这些信息在登录页面可以查看,按F12进入开发者工具栏,按如下步骤查找

按1,2,3找到消息头
在消息头里向下滑动找到请求头,cookie
找到User-Agent

将User-Agent和cookie加入header中,注意要设置timeout时间,防止响应时间过长,代码如下:

  1. def getHTMLText(url):  # 获得页面函数
  2.     try:
  3.         header = {
  4.             'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0',
  5.             'cookie': 'thw=cn; cna=npj6FSROsjACAdodv5gJhCng; hng=CN%7Czh-CN%7CCNY%7C156; tracknick=a459804692; _cc_=Vq8l%2BKCLiw%3D%3D; tg=0; enc=QBWbMOc0vQMNrTeNZMKkNSam6PhBuBNZFXZb7jRKj1R6%2FnZ%2FAztJW%2F4Ql%2BaqZxBArE4hIF2kV2dUg%2BBZSV88Eg%3D%3D; x=e%3D1%26p%3D*%26s%3D0%26c%3D0%26f%3D0%26g%3D0%26t%3D0%26__ll%3D-1%26_ato%3D0; miid=778994371391611536; t=c8d997f8c3862a9545d7aa8e0f55a811; _m_h5_tk=35adf942f203d5b2def2e96c38527acf_1579166355669; _m_h5_tk_enc=0f3e60d1c12cd628e261b932e21ab4a2; cookie2=15c929f24b3d18aec75687bbefabacb3; v=0; _tb_token_=e9b30e375eee7; alitrackid=www.taobao.com; lastalitrackid=www.taobao.com; __guid=154677242.3514116682747917000.1579155943921.0774; _uab_collina=157915745635971448621381; x5sec=7b227365617263686170703b32223a223365323561373037306538313737393931373032646330666464613337346335434b6154675045464549756a734e6e372f4a724f68674561444449314f5467344e5459344e6a55374d513d3d227d; JSESSIONID=391518D427C8B71F1754C4144A533BCA; monitor_count=8; isg=BE9PlXPQTWYkTEpDzc8TlxxO3uNZdKOWgEm-D2Fcj77tMG0yYUXr5h6mMmCOSHsO; l=cBa32tzuqwYH7YbhBOCwZuI8jO79hIRVguPRwnNXi_5dY18KvH_Oomns5eJ6cjWdtkLW4IFj7Ty9-etuiRHx3mx-g3fP.'
  6.         }
  7.         r = requests.get(url, headers=header, timeout=30)
  8.         r.raise_for_status()
  9.         r.encoding = r.apparent_encoding
  10.         return r.text
  11.     except:
  12.         return ""
3.录入数据

首先对页面进行观察,我们使用第一个商品名称和价格,在网页源代码页面进行搜索

价格为3.20,名称直接检索关键字红领巾

在源代码页面按ctrl+F即可调出搜索框,我们先对价格进行检索

可以看到价格为”view_price”:”3.20″,我们可以后续使用正则表达式进行匹配

名称方面,我们选取”raw_title”:”…”作为关键元素

然后我们使用正则表达式匹配这两个元素

  1. plt = re.findall(r'\"view_price\"\:\"[\d\.]*\"', html)
  2. tlt = re.findall(r'\"raw_title\"\:\".*?\"', html)  # 最小匹配

这两个正则表达式有个答主讲的很细致,https://zhidao.baidu.com/question/1695911847827365708.html

首先我们使用re.findall,它能返回一个列表,r’ ‘ 表示原生字符串,而在使用特殊符号时我们需要加上 ‘ \ ‘ 来进行标记,第二个正则表达式中 ‘ *? ‘ 是最小匹配,表示前一个字符0次或无限次扩展,而前一个字符是 ‘ . ‘ ,为任意单个字符,整体意思就是任意长度任意字符

在正则表达式匹配到响应元素后,我们使用.split函数将列表中每个值进行分隔,只取出后面的有效信息,再把两个信息一同存入新的列表,形成一个二维列表

4.数据输出

在输出的时候,我们先设定一个计时器,再对二维列表进行遍历,在遍历时每次让计时器+1作为输出结果的序号,代码如下:

  1. def printGoodlist(ilt):  # 输出结果信息到屏幕
  2.     tplt = "{:4}\t{:8}\t{:16}"          #\t缩进
  3.     print(tplt.format("序号", "价格", "商品名称"))
  4.     count = 0
  5.     for g in ilt:
  6.         count = count + 1
  7.         print(tplt.format(count, g[0], g[1]))

在遍历的时候不使用两层for循环而直接print格式化输出,简化了代码


3.运行演示

完整代码如下:

  1. import requests
  2. import re
  3.  
  4. def getHTMLText(url):  # 获得页面函数
  5.     try:
  6.         header = {
  7.             'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0',
  8.             'cookie': 'thw=cn; cna=npj6FSROsjACAdodv5gJhCng; hng=CN%7Czh-CN%7CCNY%7C156; tracknick=a459804692; _cc_=Vq8l%2BKCLiw%3D%3D; tg=0; enc=QBWbMOc0vQMNrTeNZMKkNSam6PhBuBNZFXZb7jRKj1R6%2FnZ%2FAztJW%2F4Ql%2BaqZxBArE4hIF2kV2dUg%2BBZSV88Eg%3D%3D; x=e%3D1%26p%3D*%26s%3D0%26c%3D0%26f%3D0%26g%3D0%26t%3D0%26__ll%3D-1%26_ato%3D0; miid=778994371391611536; t=c8d997f8c3862a9545d7aa8e0f55a811; _m_h5_tk=35adf942f203d5b2def2e96c38527acf_1579166355669; _m_h5_tk_enc=0f3e60d1c12cd628e261b932e21ab4a2; cookie2=15c929f24b3d18aec75687bbefabacb3; v=0; _tb_token_=e9b30e375eee7; alitrackid=www.taobao.com; lastalitrackid=www.taobao.com; __guid=154677242.3514116682747917000.1579155943921.0774; _uab_collina=157915745635971448621381; x5sec=7b227365617263686170703b32223a223365323561373037306538313737393931373032646330666464613337346335434b6154675045464549756a734e6e372f4a724f68674561444449314f5467344e5459344e6a55374d513d3d227d; JSESSIONID=391518D427C8B71F1754C4144A533BCA; monitor_count=8; isg=BE9PlXPQTWYkTEpDzc8TlxxO3uNZdKOWgEm-D2Fcj77tMG0yYUXr5h6mMmCOSHsO; l=cBa32tzuqwYH7YbhBOCwZuI8jO79hIRVguPRwnNXi_5dY18KvH_Oomns5eJ6cjWdtkLW4IFj7Ty9-etuiRHx3mx-g3fP.'
  9.         }
  10.         r = requests.get(url, headers=header, timeout=30)
  11.         r.raise_for_status()
  12.         r.encoding = r.apparent_encoding
  13.         return r.text
  14.     except:
  15.         return ""
  16.  
  17. def parsePage(ilt, html):  # 解析获得的页面
  18.     try:
  19.         plt = re.findall(r'\"view_price\"\:\"[\d\.]*\"', html)
  20.         tlt = re.findall(r'\"raw_title\"\:\".*?\"', html)   #最小匹配
  21.         for i in range(len(plt)):
  22.             price = eval(plt[i].split(':')[1])  #将plt的第i个元素取出,利用:划分为列表,取出第二个元素,获得:后面的有效信息
  23.             title = eval(tlt[i].split(':')[1])
  24.             ilt.append([price, title])      #一个元素中有两个值,二维列表
  25.     except:
  26.         print("")
  27.  
  28. def printGoodlist(ilt):  # 输出结果信息到屏幕
  29.     tplt = "{:4}\t{:8}\t{:16}"          #\t缩进
  30.     print(tplt.format("序号", "价格", "商品名称"))
  31.     count = 0
  32.     for g in ilt:
  33.         count = count + 1
  34.         print(tplt.format(count, g[0], g[1]))
  35.  
  36. def main():  # 主函数
  37.     goods = input("请输入要爬取的商品:")
  38.     depth = eval(input("请输入爬取页数:"))  # 爬取深度
  39.     start_url = 'https://s.taobao.com/search?q=' + goods
  40.     indolist = []  # 输出结果
  41.     for i in range(depth):
  42.         try:
  43.             url = start_url + '&s=' + str(44 * i)
  44.             html = getHTMLText(url)
  45.             parsePage(indolist, html)
  46.         except:
  47.             continue
  48.     printGoodlist(indolist)
  49.  
  50. main()

后续还可以对它进行优化,比如对金额进行判断,差别过大的数据直接舍去,还可以进行数据可视化等

需要注意的是cookie是有期限的,需要加入自己的cookie才行

运行结果如下: