文章目录

  • 1. 前言
  • 2. 故事的背景
  • 3. 爬虫的分类?
  • 4. 淘宝对爬虫都有哪些限制?
  • 5. 抓包
  • 6. 编码
  • 7. 运行代码
  • 8. 数据可视化
  • 9. 扩展

1. 前言

上一篇博客 “爬虫让我再次在女同学面前长脸了~(现实版真实案例)” 说到了帮女同学批量下载试题,我把文章同步到了CSDN,竟然有41个赞 + 21个评论 + 155个收藏,难道大家和我的目的都一样:爬虫 liao mei ?

本篇文章主要介绍如何“快速”抓取淘宝商品信息,从几个维度统计并且进行可视化,本次案例使用关键字 “安踏篮球鞋男鞋”,(仅仅是因为我最近买了一双鞋子,然后就想到了这个商品而已)

2. 故事的背景

没有故事,没有背景,就是突然想。。。

3. 爬虫的分类?

先来接地气几个词,一般来说爬虫可以分为 【通用爬虫 、垂直爬虫】,那么是如何定义的呢?

  • 通用爬虫:通用爬虫不需要理会网站哪些资源是需要的,哪些是不需要的,一并抓取并将其文本部分做索引(比如:百度爬虫、搜狗爬虫。。。)
  • 垂直爬虫:垂直爬虫往往在某一领域具有其专注性,并且垂直爬虫往往只需要其中一部分具有垂直性的资源,所以垂直爬虫相比通用爬虫更加精确,这也是比较常见的爬虫。

如果上面的解释还不够接地气的话,举个简单例子
小明想找一个女朋友,但是小明对女朋友要求为 null,只要 [ 是一个女的都行 ] 那种,这就可以理解为通用爬虫;
但是小明如果眼光比较挑,要求女朋友必须是 [ 身高165+,体重90,大眼睛,长头发,小蛮腰 ],这就可以理解为垂直爬虫。

4. 淘宝对爬虫都有哪些限制?

淘宝爬虫,很多文章都在讲解淘宝登录,然后分析ua参数等,以前我也很执着去分析过,真的挺难,后面我就没有继续跟下去了,就使用浏览器驱动实现登录,如 selenium (需要修改参数,否则淘宝能识别) 或者 JxBrowser 等等。。。

除开淘宝登录来看,淘宝的底线还是比较低的,暂时没有很高防线,你携带 cookie 即可,没错就是携带“饼干”给淘宝,他就给你通行了,所以本篇博客是通过携带 cookie 进行快速获取到数据的,(个人:有时间就去学习逆向分析,但是真正实际爬的话,使用最低的成本获取最高的效益,巧劲最大化

题外话,虽然说一般的大厂不轻易封IP (误伤太大),但如果真的想海量采集淘宝商品数据的话,还必须要花点时间研究反爬策略的,否则很难进行海量抓取,因为文章后面有可能是有点触发反爬了 (后面再说)

5. 抓包

如果在电脑首次上淘宝的话,你想搜索商品是必须要求登录的,淘宝的要求登录就不是我上一篇文章(撩妹佳文)那种假登录限制了,而且整体接口都要求必须携带凭证(cookie)访问的了,这里介绍一个巧劲,因为如果使用登录的 cookie 访问淘宝的话,淘宝实际上是知道你是谁的,有你访问的记录的,然后你上淘宝可能会给你推送商品的,甚至对你账号进行限制 、反爬等,因此写一个爬虫没必要搭上那么大的风险吧,所以登录完成之后,就退出,这个时候浏览器还是可以正常搜索产品的,意思就是说不要使用登录的 cookie 进行爬取数据。。。

五个抓包步骤如下

观察 json 结构:复制上面的抓包的数据,然后进行 json 视图查看

如何分页?

上面的抓包只是一页的数据,那么如何抓取淘宝搜索其他页呢?
我可以直接告诉你的是,通过在 url 传递一个 s={最后一个商品的位置} 实现的分页…

好吧 我们来简单看一下吧:

  • 在上面抓包中,我们并没有看到 url 传递了 s 这个参数
  • 然后我们先来点击一下 “下一页”,
    多点几次观察几次,你就知道了 s 参数的变化了

观察 s 变化,第二页s=44,第三页s=88,第四页s=132,…

首页没有 s 参数
第二页:https://s.taobao.com/search?q=%E5%AE%89%E8%B8%8F%E7%AF%AE%E7%90%83%E9%9E%8B%E7%94%B7%E9%9E%8B&imgfile=&js=1&stats_click=search_radio_all%3A1&initiative_id=staobaoz_20200705&ie=utf8&bcoffset=3&ntoffset=3&p4ppushleft=1%2C48&s=44
第三页:https://s.taobao.com/search?q=%E5%AE%89%E8%B8%8F%E7%AF%AE%E7%90%83%E9%9E%8B%E7%94%B7%E9%9E%8B&imgfile=&js=1&stats_click=search_radio_all%3A1&initiative_id=staobaoz_20200705&ie=utf8&bcoffset=0&ntoffset=6&p4ppushleft=1%2C48&s=88
第三页:https://s.taobao.com/search?q=%E5%AE%89%E8%B8%8F%E7%AF%AE%E7%90%83%E9%9E%8B%E7%94%B7%E9%9E%8B&imgfile=&js=1&stats_click=search_radio_all%3A1&initiative_id=staobaoz_20200705&ie=utf8&bcoffset=0&ntoffset=6&p4ppushleft=1%2C48&s=132

来到这里,可以去掉其他不必要的参数,结论是:https://s.taobao.com/search?q={url encode keywords}&s={开始位置}

6. 编码

经过上面繁琐的步骤,终于可以着手编码了,因为爬取数据之后需要进行可视化,所以数据肯定是需要持久化的,为了方便就保存到 csv 文件中。

逗号分隔值(Comma-Separated Values,CSV,有时也称为字符分隔值,因为分隔字符也可以不是逗号),其文件以纯文本形式存储表格数据(数字和文本)。纯文本意味着该文件是一个字符序列,不含必须像二进制数字那样被解读的数据。

在代码里注释得很清楚的了,这里直接贴代码出来即可

为了简洁,我贴出主要的代码,想要获取整体源码项目的话,关注微信公告号( it_loading 回复 “淘宝爬虫” 即可)

FastHttpClient.java

https://blog.csdn.net/JinglongSource/article/details/107136862

TaoBaoSpider1.java

import cn.shaines.spider.util.FastHttpClient;
import cn.shaines.spider.util.FastHttpClient.Response;
import cn.shaines.spider.util.FastHttpClient.ResponseHandler;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONPath;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.hc.client5.http.classic.methods.HttpGet;import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Collectors;/*** @author for.houyu@qq.com*/
@Slf4j
public class TaoBaoSpider1 {private static final String listUrlTemplate = "https://s.taobao.com/search?q=${keywords}&s=${index}";private static String cookie = "enc=l5xju4OJMzTwwtBkVJWbvFPcQ%2B6n6%2FWdRaE4iECxQOQiEtA45RBOyXQu0gZbOcIVEO6oxrRWvtu6mgSv4JZa8w%3D%3D; thw=cn; hng=CN%7Czh-CN%7CCNY%7C156; sgcookie=ESMDjIxrMIKwi48qmS8xP; tfstk=cCOGBNOa3dW_xoWHFf16ovEkfpHRZKNVqIRBTCGS8wNPBsdFiO2UUAat-GxMM-1..; tracknick=; cna=8XsbF0X0cVkCAbcg0Pt/qWLy; t=bb72251ba9e04aa4ffe5119a746b1f35; v=0; cookie2=1d43fd087e0cf8b4268f2e8ddcd4aea0; _tb_token_=e30835115bdfe; alitrackid=www.taobao.com; lastalitrackid=www.taobao.com; JSESSIONID=07CB217DBB9367EF2E0CCF7AF29AA9A0; isg=BDo6WaueMbQrb7zN4rxs3mhmi2Bc677FZ6MxoUQ_y0y9N99xLH6N1UYBh8PrpzZd; l=eBMkD1V4QZHXKy_vBO5whurza77ONdAfCsPzaNbMiInca6ZFN1uJuNQqG5uBldtjgtfj7etrb3kJjRUpziUdg2HvCbKrCyCk6Yp6-";private FastHttpClient httpClient;private BufferedWriter writer;private Set<String> filter;public static void main(String[] args) throws Exception {// 搜索关键字final String keywords = "安踏篮球鞋男鞋";// 每页44条数据final int limit = 44;TaoBaoSpider1 spider = new TaoBaoSpider1();spider.init(keywords);for (int page = 0; page <= 99; page++) {log.info("正在准备下载页数: {}", page + 1);String html = spider.getListHtml(keywords, page * limit);List<Goods> list  = spider.parse(html);log.info("解析得到数量: [{}]", list.size());if (list.isEmpty()) {break;}list = spider.doFilter(list);log.info("过滤后数量: [{}]", list.size());list.forEach(v -> {List<String> row = Arrays.asList(v.getRaw_title(), v.getView_price(), v.getView_fee(), v.getNick(), v.getItem_loc(), v.getView_sales(), v.getPic_url(), v.getDetail_url(), v.getComment_url(), v.getShopLink(), "_" + v.getNid(), "_" + v.getPid());spider.writeRow(row);});// 睡眠3 ~ 10秒Thread.sleep(ThreadLocalRandom.current().nextLong(3000, 10000));log.info("\r\n");}}private List<Goods> doFilter(List<Goods> list) {list = list.stream().filter(v -> !filter.contains(v.getNid())).collect(Collectors.toList());filter.addAll(list.stream().map(Goods::getNid).collect(Collectors.toSet()));return list;}/*** 写入一行数据* @param row 一行数据*/protected void writeRow(List<String> row) {// 写入一行数据, csv的一行格式为,分割的, 但是这里使用","分割,主要就是为了统一作为字符串// 如://      "姓名","年龄"//      "张三","123"String line = row.stream().map(v -> v.replace("\"", "\"\"").replace(",", ",,")).collect(Collectors.joining("\",\"", "\"", "\""));try {writer.write(line);writer.newLine();writer.flush();} catch (IOException e) {e.printStackTrace();}}/*** 解析html* @param html the html*/protected List<Goods> parse(String html) {if (StringUtils.isEmpty(html) || (html.contains("登录页面") && html.contains("全登陆不允许iframe嵌入"))) {throw new RuntimeException("获取列表HTML失败,请检查,如更新cookie等...");}String script = Arrays.stream(StringUtils.substringsBetween(html, "<script", "</script>"))// 过滤包含 g_page_config 和 auctions 的 script 脚本.filter(v -> v.contains("g_page_config") && v.contains("itemlist"))// 获取第一个符合条件的脚本(原则来上说这里只能返回一个, 否则说明上面的过滤不严谨).findFirst()// 如果没有匹配的就抛异常, 说明解析页面失败.orElseThrow(() -> new RuntimeException("解析页面失败,请检查更新代码"));// 观察 script 内部其实是一个json格式的字符串, 因此找到分割点进行切割字符串返回一个json串String json = StringUtils.substringBetween(script, "g_page_config", "g_srp_init");json = json.substring(json.indexOf("{"), json.lastIndexOf("}") + 1);// 使用 JSONPath 实现快速解析json, 这里的意思是查找n级 itemlist 下的 n级auctions (说明:alibaba fastjson 就有 JSONPath 的实现)Object eval = JSONPath.eval(JSON.parseObject(json), "$..itemlist..auctions");if (!(eval instanceof List)) {throw new RuntimeException("解析JSON列表失败, 请检查更新代码");}List<JSONObject> auctions = (List<JSONObject>) eval;// 转换为目标对象 GoodsList<Goods> result = auctions.stream().map(v -> v.toJavaObject(Goods.class)).collect(Collectors.toList());// result.forEach(System.out::println);return result;}/*** 根据关键字获取列表HTML* @param keywords 关键字 如: 安踏篮球鞋男鞋* @param index 开始位置, 如0, 每页显示44条数据, 因此0, 44, 88, ...*/protected String getListHtml(String keywords, int index) {// 中文进行URL编码keywords = FastHttpClient.encodeURLText(keywords, StandardCharsets.UTF_8);String url = listUrlTemplate.replace("${keywords}", keywords).replace("${index}", index + "");// 创建一个GET请求HttpGet httpGet = httpClient.buildGet(url);// 执行请求, 获取响应Response<String> response = httpClient.execute(httpGet, ResponseHandler.ofString());return response.getData();}/*** 资源初始化*/protected void init(String keywords) {// 初始化 httpClient 并且设置 cookie 每次请求都会携带cookie信息httpClient = FastHttpClient.builder().setCookie(cookie).build();filter = new HashSet<>(1024);try {// 初始化 CSV 文件呢File file = Paths.get(System.getProperty("user.dir"), "temp", "spider", keywords + ".csv").toFile();file.getParentFile().mkdirs();writer = new BufferedWriter(new FileWriter(file, false));// 准备headerList<String> header = Arrays.asList("标题", "单价", "运费", "店名", "发货地址", "售量", "首页图", "明细地址", "评论地址", "购买地址", "nid", "pid");this.writeRow(header);} catch (IOException e) {e.printStackTrace();}}public static class Goods {/** nid */private String nid;/** pid */private String pid;/** 标题 */private String raw_title;/** 首页图 */private String pic_url;/** 明细地址 */private String detail_url;/** 单价 */private String view_price;/** 运费 */private String view_fee;/** 发货地址 */private String item_loc;/** 售量 */private String view_sales;/** 店名 */private String nick;/** 评论地址 */private String comment_url;/** 购买地址 */private String shopLink;public String getNid() {return nid;}public void setNid(String nid) {this.nid = nid;}public String getPid() {return pid;}public void setPid(String pid) {this.pid = pid;}public String getRaw_title() {return raw_title;}public void setRaw_title(String raw_title) {this.raw_title = raw_title;}public String getPic_url() {return pic_url;}public void setPic_url(String pic_url) {this.pic_url = pic_url;}public String getDetail_url() {return detail_url;}public void setDetail_url(String detail_url) {this.detail_url = detail_url;}public String getView_price() {return view_price;}public void setView_price(String view_price) {this.view_price = view_price;}public String getView_fee() {return view_fee;}public void setView_fee(String view_fee) {this.view_fee = view_fee;}public String getItem_loc() {return item_loc;}public void setItem_loc(String item_loc) {this.item_loc = item_loc;}public String getView_sales() {return view_sales;}public void setView_sales(String view_sales) {this.view_sales = view_sales;}public String getNick() {return nick;}public void setNick(String nick) {this.nick = nick;}public String getComment_url() {return comment_url;}public void setComment_url(String comment_url) {this.comment_url = comment_url;}public String getShopLink() {return shopLink;}public void setShopLink(String shopLink) {this.shopLink = shopLink;}@Overridepublic String toString() {final StringBuilder sb = new StringBuilder("Goods{");sb.append("nid='").append(nid).append('\'');sb.append(", pid='").append(pid).append('\'');sb.append(", raw_title='").append(raw_title).append('\'');sb.append(", pic_url='").append(pic_url).append('\'');sb.append(", detail_url='").append(detail_url).append('\'');sb.append(", view_price='").append(view_price).append('\'');sb.append(", view_fee='").append(view_fee).append('\'');sb.append(", item_loc='").append(item_loc).append('\'');sb.append(", view_sales='").append(view_sales).append('\'');sb.append(", nick='").append(nick).append('\'');sb.append(", comment_url='").append(comment_url).append('\'');sb.append(", shopLink='").append(shopLink).append('\'');sb.append('}');return sb.toString();}}}

7. 运行代码

效果如下

文件保存路径默认在项目路径 $/temp/spider/xxx.csv

一共爬了2074条数据,爬到大概50页的时候,发现返回的数据都是重复的了(根据nid过滤)
大概猜测原因:

  • nid 字段不具备唯一性过滤
  • 淘宝反爬
  • 淘宝限制返回数量(虽然写着99页,实际真实客户也不可能是真的翻了99页找商品…)

8. 数据可视化

以下可视化数据仅供学习参考,不具备任何依据判断,不承担任何责任。

1. 淘宝 - 安踏篮球鞋男鞋 - 发货地词云图

2. 淘宝 - 安踏篮球鞋男鞋 - 店铺售量冲浪榜

仅展示店铺售量1900+以上店铺,不上榜的店铺纳入 “其他”

3. 淘宝 - 安踏篮球鞋男鞋 - 价格&售量表1

4. 淘宝 - 安踏篮球鞋男鞋 - 价格&售量表2

9. 扩展

上面的代码完成一个关键字的搜索,顶多可以算一个爬虫,并不能算一个完整的爬虫系统,一个爬虫系统应该具备以下的几个组件。(待完善)


获取源码,请关注我的公众号 :IT加载中(it_loading)
回复 "淘宝爬虫"即可


公众号:IT加载中(it_loading)
CSDN:https://blog.csdn.net/JinglongSource
博客:https://ihouyu.cn/
邮箱:for.houyu@qq.com

程序员 [ 后宇 ],是一个关注编程,热爱技术的Java后端开发者,热衷于 [ Java后端 ],[ 数据爬虫领域 ]。不定期分享 I T 技能和干货!!欢迎关注 “IT加载中”,一个只出 干货 和 实战 的公众号。

淘宝垂直爬虫之关键字搜索(实战+源码+可视化)相关推荐

  1. python爬虫-淘宝商品密码(图文教程附源码)

    今天闲着没事,不想像书上介绍的那样,我相信所有的数据都是有规律可以寻找的,然后去分析了一下淘宝的商品数据的规律和加密方式,用了最简单的知识去解析了需要的数据. 这个也让我学到了,解决问题的方法不止一个 ...

  2. android电子书App、自定义图表、仿腾讯漫画App、仿淘宝优惠券、3D选择容器等源码...

    Android精选源码 仿支付宝记账本功能,饼状图:数字键盘 android一款功能完善的电子书应用源码 Android自定义图标库,使用方便,扩展性强 android 3D立体无限旋转容器源码 an ...

  3. android电子书App、自定义图表、仿腾讯漫画App、仿淘宝优惠券、3D选择容器等源码... 1

    Android精选源码 仿支付宝记账本功能,饼状图:数字键盘 android一款功能完善的电子书应用源码 Android自定义图标库,使用方便,扩展性强 android 3D立体无限旋转容器源码 an ...

  4. android电子书App、自定义图表、仿腾讯漫画App、仿淘宝优惠券、3D选择容器等源码

    Android精选源码 仿支付宝记账本功能,饼状图:数字键盘 android一款功能完善的电子书应用源码 Android自定义图标库,使用方便,扩展性强 android 3D立体无限旋转容器源码 an ...

  5. 淘宝代购系统、海外代购系统·代购源码、代购程序、电商API、淘宝API开发

    新余市万邦科技有限公司是一家专业提供全方位代购系统.代购源码.代购程序.电商API.淘宝API.转运系统.外贸网站建设服务的公司.万邦经过近10年的实际运营改进,不断完善,功能非常强大,业务流程合理, ...

  6. 淘宝/天猫、1688API-按关键字搜索商品item_search

    本帖只展示部分代码及接口 需了解更多或开发系统请移步注册测试 测试地址:https://console.open.onebound.cn/console/index.php?i=Anzexi 测试数据 ...

  7. 淘宝天猫API item_search-按关键字搜索淘宝商品

    可免费测试注册试用

  8. 淘宝开源代码检测工具!(附源码)

    点击上方[全栈开发者社区]→右上角[...]→[设为星标⭐]          正文   好的代码一定是整洁的,并且能够帮助阅读的人快速理解和定位.好的代码可以加快应用的开发迭代速度,不必花过多的时间 ...

  9. 【淘宝SEO技巧】解读淘宝新规及SEO技术实战指南

    为了协助广大卖家了解规则,做好优化,而且与淘宝SEO的愛好者与实践者交换,特此撰文,解读一下在新规则下哪个是赢家,而且給出一些实战技术.文章可能有点长,但绝对有效,有缘人(碰到此文的人)可能受益,有心 ...

最新文章

  1. 文件节点的linux指令,Java工程师必学的Linux命令(一)文件与目录管理
  2. iphone XCode调试技巧之EXC_BAD_ACCESS中BUG解决
  3. Selenium之Android使用学习
  4. 整型和浮点型的区别_浮点整型强转的一个题目解析
  5. python移除系统多余大文件
  6. 问卷 假设检验 t检验_真实问题的假设检验
  7. wpf 将Style应用到 ListView 中的 ListViewItem 元素
  8. 第七节:Asp.Net Core内置日志记录
  9. 三道题就能考察你对Vue掌握了多少!
  10. `json in遍历
  11. tomcat、netty以及nodejs的helloworld性能对比
  12. Webdriver常用的元素定位
  13. c语言里的除法运算定律,加法乘法运算,乘法需要打括号吗?
  14. 冷山的博客思听有声书摘下载索引页
  15. 策略模式Java实现
  16. 深入浅出Docker 读书笔记(九)
  17. PaddlePaddle - 人脸关键点检测课程笔记
  18. 数据,数据流,数据管道
  19. 2012-04-30《说说老朋友》
  20. iview button根据条件 disabled可用或者不可用

热门文章

  1. ThinkPad X61 高分屏安装Win8.1,Ubuntu
  2. Python爬取图片+百度人脸检测过滤高颜值美女
  3. Ckp的约会(xmu oj)贪心算法问题 by C++
  4. 嵌入式常用算法:时间触发下的嵌入式软件设计模式
  5. 非关系型数据库 之 文档型数据库 MongoDB 的使用(Python3)
  6. 解决elementaryos下报错W: Possible missing firmware /lib/firmware/rtl_nic/rtl8125a-3.fw for module r8169
  7. 快启动u盘启动盘安装系统
  8. Javascript 设计模式 单例
  9. 思否官方祝各位社区开发者 2019 春节快乐
  10. 如何去除百度地图下方的文字标识