师哥教你运用代码的力量,买到心仪的低价手机

  • 作者
  • 戈寒
  • 2020-08-10 04:31:09
  • 阅读  97

背景

师哥最近想买部手机,前同事给了一个网站。说这个网站,每天价格,会有一定变化,时高时低的。

师哥一想,我只要每一天,去记录一下手机的价格,那么就可以用一个相对低一点的价格,买入手机了。

师哥是一个程序员,不可能真的每天去看一个购物网站(每天看购物网站的程序员,不是真程序员,除非是看机械键盘)。

那么,方法,就只有一个了。师哥,自己写一个程序。每天替我去看看这个网站,并记录下数据,再进行数据分析,在价格相对较低的情况,发封邮件通知我。

开码

开发流程

  • URL中GET参数分析
    多点击几个相关页面,仔细观察变化的参数,并推测其意义及关联性。不变的参数,在开发中直接抄过来即可。
  • 页面结构分析
  • 页面所需元素的分析
    主要分析,所需信息的元素,在什么位置;其相关的css样式,id参数之类的。后续解析和提取数据时,需要用到这些参数。
  • 页面所需要URL分析
    仔细观察,点击所需元素时,url或者http请求中,发生变化的参数。并找出这些参数来源,及相关性。
  • HTTP请求分析
    我是直接用chrome的F12,直接观察的。主要注意,http请求头,cookie等信息。

这里需要注意的是,有的网站,因为禁用了鼠标右键之类的原因。不方便直接观察和分析页面时,就需要用抓包工具,抓出请求时的参数。再直接编码使用这些抓到参数,将页面抓下来放到本地分析。

当完成这些分析时,其实爬虫工作就已经接近成功了。

需求分析

我们需要的就是将手机名称及价格保存下来。(下文中我将该网站的域名,使用{URL}代替)
URL:{URL}?pp=&km=华为.手机.畅享20系列7&network=&tykhgsdm=
我们通过观察分析,得出:

  • km参数是由手机的一级,二级,三级分类的名称,加上“.”号,组合而成。
  • HTTP请求时还有一个cookie:JSESSION:**************
  • 左侧分类通过html.xpath("//a[contains(@onclick, 'javascript:kmjump')]"),进行提取
  • 手机名称通过html.xpath("//a[contains(@href, 'goodsdetails.action?sptid=')]/h5/text()"),进行提取
  • 手机价格通过html.xpath("//p[@class='pull-left']/text()"),进行提取

技术选型

数据存储

数据抽取出来,师哥现在只想存到execl中,就直接选中了POI。如果,是想存CSV呢,可以用superCSV。想存数据库的话,选择就比较多。可以使用原生JDBC,也可以mybatis,hibernate等ORM框架。

Jsoup or WebMagic

师哥,刚毕业工作时,公司安排一个类似的工作,让我每天去抓一个供应商的数据,然后入库。(这样可以省一个人,去干这个事。写完了,给了我2000奖金)那次,师哥用的JSOUP是,去解析和提取网页元素。

这次开始码代码之前,我想这么多年过去了,应该会有些更好用的工具。果然有很多新鲜玩意,最后决定这次尝试一下WebMagic。 在实际开发中,这两者差别并不是太大。

  • JSOUP
    Jsoup中,其实也包含了HttpClient组件,可以直接获取网页。Jsoup.connect("xxx")方法返回一个Connection对象。通过Connection,可以执行get或者post来执行请求。执行请求之前,可以设置一些请求信息。比如:头信息,cookie,请求等待时间,代理等等来模拟浏览器的行为。 Jsoup比较厉害的地方,在于丰富的获取HTML元素的API。 可以使用DOM的方式来取得,例如:
getElementById(String id);                                  //通过id来获取
getElementsByTag(String tagName);                           //通过标签名字来获取
getElementsByClass(String className);                       //通过类名来获取
getElementsByAttribute(String key);                         //通过属性名字来获取
getElementsByAttributeValue(String key, String value):     //通过指定的属性名字,属性值来获取
getAllElements();                                           //获取所有元素

也可以通过,类似于css或jQuery的选择器来查找元素,例如:

Elements links = doc.select("a[href]");                   //带有href属性的a元素
Elements pngs = doc.select("img[src$=.png]");             //扩展名为.png的图片
Element masthead = doc.select("div.masthead").first();    //class等于masthead的div标签
Elements resultLinks = doc.select("h3.r > a");            //在h3元素之后的a元素

除此之外,Jsoup还可以通过JsoupXPath方式寻找元素。

JsoupXPath补充小课堂:
JsoupXPath有4种寻找节点的方式:
1.绝对路径语法。就是以“/”开头,一级一级描述标签的层级路径,不可以跨层级。
"/父元素/子元素/孙元素/..."
String str = "/body/table/tbody/tr/td";            //绝对路径找节点 

2.相对路径语法。已有JXNode节点对象的情况下,通过此节点再往下寻找此对象内的其他节点。
"./子元素/孙元素"
JXNode jxn = jxd.selNOne("/body/table/tbody");
List<JXNode> sel = jxn.sel("./tr");

3.全文搜索路径。在全局搜索对应的标签,不需要从根目录开始搜索。
"//元素"                           全局搜索元素 
"//元素/子元素或@元素属性"            全局搜索元素后的子路径中找子元素或属性
List<JXNode> jxNodes = jxd.selN("//td/input/@type");        //返回type属性节点对象

4.条件筛选语法。根据条件筛选过滤节点,前面部分为筛选的条件,后面可以加上操作的动作
//元素[@属性=value]                                               筛选属性为value值的节点对象
//元素[@属性=value]/text() 或者 //元素[@属性=value]/html()          获取元素为value值的标签体内容对象
List<JXNode> jxNodes = jxd.selN("//input[@type=checkbox]/html()");
  • Webmagic
    Webmagic与Jsoup解析网页方面,差别并不大。只是Webmagic功能更多,更全。它覆盖整个爬虫的生命周期(链接提取、页面下载、内容抽取、持久化),支持多线程抓取,分布式抓取,并支持自动重试、自定义UA/cookie等功能。所以,使用webmagic之后,在多线程请求,循环抓取方面,以及去重等抓取流程上相关点,就会少操很多心。

码途要点

在要点,这个小节,就主要说一下,师哥在开发这个小Demo中,遇到的cookie问题。

如何获取和设置cookie

这段代码,师哥没用多久,就写完了。可是写完抓了几遍之后,就抓不到数据了。仔细检查后,师哥发现浏览器请求中有cookie,而我的代码中是没有的。
这个问题,在师哥刚毕业那会儿,想的办法实在是太low了。师哥那时候,是用swing画了个UI,让同事在浏览器中真实地登录一次,再把cookie复制到我画的UI上面。
这次,师哥找到了一个东西,能够稍微优雅一点,解决这个问题。
想要爬取的网站需要登录时,可以用Selenium模拟浏览器登录,获取cookie,设置到webMagic的site中。

<dependency>
  <groupId>org.seleniumhq.selenium</groupId>
  <artifactId>selenium-java</artifactId>
  <version>2.48.0</version>
</dependency>

Selenium 3.0以上版本需要JDK8。模拟浏览器登录,需要浏览器驱动,我使用的是Chrome,驱动Chromedriver。 下载地址:http://chromedriver.storage.googleapis.com/index.html(注意版本对应关系)。

设置cookie相关代码:

 public static void getCookie(String path) {
  System.setProperty("webdriver.chrome.driver", path); // 注册驱动
  WebDriver driver = new ChromeDriver();

  driver.get("url?currentPage=1");// 打开网址
  try {
   Thread.sleep(1000);
  } catch (InterruptedException e) {
   e.printStackTrace();
  }

  // 获取cookie信息
  cookies = driver.manage().getCookies();
  driver.close();
 }
  --------------------------
 public Site getSite() {
  // 将获取到的cookie信息添加到webmagic中
  if(GetAllTypeHtml.cookies != null && !GetAllTypeHtml.cookies.isEmpty()) {
   for (Cookie cookie : GetAllTypeHtml.cookies) {
    site.addCookie(cookie.getName().toString(), cookie.getValue().toString());
   }
  }
  return site;
 }

结语

师哥这个工具,每天抓一次,所有手机的价格。然后就可以在降幅比较大的时候出手了。不过现在这个程序,只实现了前面抓取,存储数据。后面分析这块还有没有写,等后面有时间,有钱了,准备买手机的时候,就去补上。
另外,此次抓取数据,由于对方网页反扒意识还不够,所以比较容易。一般爬虫会遇到的数据加密,IP封禁等问题都没有遇到,所以没有实战。下次,希望有机会研究和学习一下,这几个问题。
这个Demo的所有代码,我已上传到Github上面。各位基友,可以去看看,提出意见修改修改。

Git:https://github.com/KING754/getHtmlDemo

Gitee:https://gitee.com/wejias/getHtmlDemo

师哥教你运用代码的力量,买到心仪的低价手机

  • 2020-08-10 04:31:09
  • 阅读  97

背景

师哥最近想买部手机,前同事给了一个网站。说这个网站,每天价格,会有一定变化,时高时低的。

师哥一想,我只要每一天,去记录一下手机的价格,那么就可以用一个相对低一点的价格,买入手机了。

师哥是一个程序员,不可能真的每天去看一个购物网站(每天看购物网站的程序员,不是真程序员,除非是看机械键盘)。

那么,方法,就只有一个了。师哥,自己写一个程序。每天替我去看看这个网站,并记录下数据,再进行数据分析,在价格相对较低的情况,发封邮件通知我。

开码

开发流程

  • URL中GET参数分析
    多点击几个相关页面,仔细观察变化的参数,并推测其意义及关联性。不变的参数,在开发中直接抄过来即可。
  • 页面结构分析
  • 页面所需元素的分析
    主要分析,所需信息的元素,在什么位置;其相关的css样式,id参数之类的。后续解析和提取数据时,需要用到这些参数。
  • 页面所需要URL分析
    仔细观察,点击所需元素时,url或者http请求中,发生变化的参数。并找出这些参数来源,及相关性。
  • HTTP请求分析
    我是直接用chrome的F12,直接观察的。主要注意,http请求头,cookie等信息。

这里需要注意的是,有的网站,因为禁用了鼠标右键之类的原因。不方便直接观察和分析页面时,就需要用抓包工具,抓出请求时的参数。再直接编码使用这些抓到参数,将页面抓下来放到本地分析。

当完成这些分析时,其实爬虫工作就已经接近成功了。

需求分析

我们需要的就是将手机名称及价格保存下来。(下文中我将该网站的域名,使用{URL}代替)
URL:{URL}?pp=&km=华为.手机.畅享20系列7&network=&tykhgsdm=
我们通过观察分析,得出:

  • km参数是由手机的一级,二级,三级分类的名称,加上“.”号,组合而成。
  • HTTP请求时还有一个cookie:JSESSION:**************
  • 左侧分类通过html.xpath("//a[contains(@onclick, 'javascript:kmjump')]"),进行提取
  • 手机名称通过html.xpath("//a[contains(@href, 'goodsdetails.action?sptid=')]/h5/text()"),进行提取
  • 手机价格通过html.xpath("//p[@class='pull-left']/text()"),进行提取

技术选型

数据存储

数据抽取出来,师哥现在只想存到execl中,就直接选中了POI。如果,是想存CSV呢,可以用superCSV。想存数据库的话,选择就比较多。可以使用原生JDBC,也可以mybatis,hibernate等ORM框架。

Jsoup or WebMagic

师哥,刚毕业工作时,公司安排一个类似的工作,让我每天去抓一个供应商的数据,然后入库。(这样可以省一个人,去干这个事。写完了,给了我2000奖金)那次,师哥用的JSOUP是,去解析和提取网页元素。

这次开始码代码之前,我想这么多年过去了,应该会有些更好用的工具。果然有很多新鲜玩意,最后决定这次尝试一下WebMagic。 在实际开发中,这两者差别并不是太大。

  • JSOUP
    Jsoup中,其实也包含了HttpClient组件,可以直接获取网页。Jsoup.connect("xxx")方法返回一个Connection对象。通过Connection,可以执行get或者post来执行请求。执行请求之前,可以设置一些请求信息。比如:头信息,cookie,请求等待时间,代理等等来模拟浏览器的行为。 Jsoup比较厉害的地方,在于丰富的获取HTML元素的API。 可以使用DOM的方式来取得,例如:
getElementById(String id);                                  //通过id来获取
getElementsByTag(String tagName);                           //通过标签名字来获取
getElementsByClass(String className);                       //通过类名来获取
getElementsByAttribute(String key);                         //通过属性名字来获取
getElementsByAttributeValue(String key, String value):     //通过指定的属性名字,属性值来获取
getAllElements();                                           //获取所有元素

也可以通过,类似于css或jQuery的选择器来查找元素,例如:

Elements links = doc.select("a[href]");                   //带有href属性的a元素
Elements pngs = doc.select("img[src$=.png]");             //扩展名为.png的图片
Element masthead = doc.select("div.masthead").first();    //class等于masthead的div标签
Elements resultLinks = doc.select("h3.r > a");            //在h3元素之后的a元素

除此之外,Jsoup还可以通过JsoupXPath方式寻找元素。

JsoupXPath补充小课堂:
JsoupXPath有4种寻找节点的方式:
1.绝对路径语法。就是以“/”开头,一级一级描述标签的层级路径,不可以跨层级。
"/父元素/子元素/孙元素/..."
String str = "/body/table/tbody/tr/td";            //绝对路径找节点 

2.相对路径语法。已有JXNode节点对象的情况下,通过此节点再往下寻找此对象内的其他节点。
"./子元素/孙元素"
JXNode jxn = jxd.selNOne("/body/table/tbody");
List<JXNode> sel = jxn.sel("./tr");

3.全文搜索路径。在全局搜索对应的标签,不需要从根目录开始搜索。
"//元素"                           全局搜索元素 
"//元素/子元素或@元素属性"            全局搜索元素后的子路径中找子元素或属性
List<JXNode> jxNodes = jxd.selN("//td/input/@type");        //返回type属性节点对象

4.条件筛选语法。根据条件筛选过滤节点,前面部分为筛选的条件,后面可以加上操作的动作
//元素[@属性=value]                                               筛选属性为value值的节点对象
//元素[@属性=value]/text() 或者 //元素[@属性=value]/html()          获取元素为value值的标签体内容对象
List<JXNode> jxNodes = jxd.selN("//input[@type=checkbox]/html()");
  • Webmagic
    Webmagic与Jsoup解析网页方面,差别并不大。只是Webmagic功能更多,更全。它覆盖整个爬虫的生命周期(链接提取、页面下载、内容抽取、持久化),支持多线程抓取,分布式抓取,并支持自动重试、自定义UA/cookie等功能。所以,使用webmagic之后,在多线程请求,循环抓取方面,以及去重等抓取流程上相关点,就会少操很多心。

码途要点

在要点,这个小节,就主要说一下,师哥在开发这个小Demo中,遇到的cookie问题。

如何获取和设置cookie

这段代码,师哥没用多久,就写完了。可是写完抓了几遍之后,就抓不到数据了。仔细检查后,师哥发现浏览器请求中有cookie,而我的代码中是没有的。
这个问题,在师哥刚毕业那会儿,想的办法实在是太low了。师哥那时候,是用swing画了个UI,让同事在浏览器中真实地登录一次,再把cookie复制到我画的UI上面。
这次,师哥找到了一个东西,能够稍微优雅一点,解决这个问题。
想要爬取的网站需要登录时,可以用Selenium模拟浏览器登录,获取cookie,设置到webMagic的site中。

<dependency>
  <groupId>org.seleniumhq.selenium</groupId>
  <artifactId>selenium-java</artifactId>
  <version>2.48.0</version>
</dependency>

Selenium 3.0以上版本需要JDK8。模拟浏览器登录,需要浏览器驱动,我使用的是Chrome,驱动Chromedriver。 下载地址:http://chromedriver.storage.googleapis.com/index.html(注意版本对应关系)。

设置cookie相关代码:

 public static void getCookie(String path) {
  System.setProperty("webdriver.chrome.driver", path); // 注册驱动
  WebDriver driver = new ChromeDriver();

  driver.get("url?currentPage=1");// 打开网址
  try {
   Thread.sleep(1000);
  } catch (InterruptedException e) {
   e.printStackTrace();
  }

  // 获取cookie信息
  cookies = driver.manage().getCookies();
  driver.close();
 }
  --------------------------
 public Site getSite() {
  // 将获取到的cookie信息添加到webmagic中
  if(GetAllTypeHtml.cookies != null && !GetAllTypeHtml.cookies.isEmpty()) {
   for (Cookie cookie : GetAllTypeHtml.cookies) {
    site.addCookie(cookie.getName().toString(), cookie.getValue().toString());
   }
  }
  return site;
 }

结语

师哥这个工具,每天抓一次,所有手机的价格。然后就可以在降幅比较大的时候出手了。不过现在这个程序,只实现了前面抓取,存储数据。后面分析这块还有没有写,等后面有时间,有钱了,准备买手机的时候,就去补上。
另外,此次抓取数据,由于对方网页反扒意识还不够,所以比较容易。一般爬虫会遇到的数据加密,IP封禁等问题都没有遇到,所以没有实战。下次,希望有机会研究和学习一下,这几个问题。
这个Demo的所有代码,我已上传到Github上面。各位基友,可以去看看,提出意见修改修改。

Git:https://github.com/KING754/getHtmlDemo

Gitee:https://gitee.com/wejias/getHtmlDemo

诗与远方

  • 请你用慈悲心和温和的态度
    把你的不满与委屈说出来
    别人就容易接受得多地多

诗与远方

  • 诚实的面对你内心的矛盾和缺点
    不要欺骗你自己

诗与远方

  • 成功要爬上梯子才能到达
    双手插在口袋里的人是爬不上去的

诗与远方

  • 良心是每一个人最公正的审判官
    你骗得了别人
    却永远骗不了你自己的良心

诗与远方

  • 不要因为小小的争执
    疏远了你的至亲好友
    也不要因为小小的怨恨
    忘记了别人的恩情

诗与远方

  • 凡是能多站在别人的角度着想
    就能做到,理解,体谅

诗与远方

  • 当幻想和现实面对时
    总是很痛苦的
    要么你被痛苦击倒
    要么你把痛苦踩在脚下

诗与远方

  • 梯子的梯阶从来不是用来搁脚的
    它只是让人们的脚踏上去
    以便让另一只脚能够再往上攀登

诗与远方

  • 毁灭一个人只要一句话
    培植一个人却要千句话
    所以请你多多口下留情

诗与远方

  • 财以不炫为富,官以不显为贵
    名以不彰为誉,施以不报为惠

诗与远方

  • 世界原本就不是属于你
    因此你用不着抛弃
    要抛弃的是一切的执着
    万物皆为我所用,但非我所属

诗与远方

  • 只要自觉心安,东西南北都好
    如有一人未度,切莫自觉逃了

诗与远方

  • 崇高的理想就像生长在高山上的鲜花
    如果要摘下它
    勤奋才是攀登的途径

诗与远方

  • 人之谤我也
    与其能辩,不如能容
    人之侮我也
    与其能防,不如能化

诗与远方

  • 不要在你的智慧中夹杂着傲慢
    不要使你的谦虚缺乏智慧

诗与远方

  • 看透大事者超脱,看不透者执着
    看透小事者豁达,看不透者计较

诗与远方

  • 坚韧是成功的一大要素
    只要在成功之门上敲得够久够大声
    终会把成功唤醒

诗与远方

  • 根本不必回头去看咒骂你的人是谁
    如果有一条疯狗咬了你一口
    难道你也要趴下去反咬它一口吗

诗与远方

  • 交有道之人,莫结无义之友
    饮清净之茶,莫贪花色之酒
    开方便之门,闲是非之口

诗与远方

  • 谦虚但不自卑
    自信但不自大
    自由但不放纵
    人一生很难做到这三点

诗与远方

  • 活着一天,就是有福气,就该珍惜
    当我哭泣没有鞋子穿的时候
    我发现有人没有脚……

诗与远方

  • 不要让追求之舟停泊在幻想的港湾
    而应扬起奋斗的风帆
    驶向现实生活的大海

诗与远方

  • 不要刻意去猜测他人的想法
    如果你没有智慧与经验的正确判断
    通常都会有偏差的

诗与远方

  • 心中装满自己的看法与想法的人
    是听不见别人的声音的

诗与远方

  • 要了解一个人
    只需要看他的出发点与目的地是否相同
    就可以知道他是否真心

诗与远方

  • 一个人如果不能从内心去原谅别人
    那他就放不下怨恨,得不到快乐的生活

诗与远方

  • 你不要一直不满人家
    你应该一直检讨自己才是
    不满人家,是苦了你自己

诗与远方

  • 你硬要把单纯的事情看得很复杂
    那你会很痛苦

诗与远方

  • 当你劝告别人时
    若不顾及别人的自尊心
    那么再好的言语都是没有用的

诗与远方

  • 一份耕耘,一份收获,付出就会有回报
    不曾遭遇过失败,因为一直往成功方向发展
    所碰到的都是暂时的挫折

诗与远方

  • 同样的瓶子
    你为什么要装毒药呢
    同样的心理
    你为什么要充满着烦恼呢

诗与远方

  • 把气氛的心境转化为柔和
    把柔和的心境转化为爱
    如此,这个世间将更加完美

诗与远方

  • 说话不要有攻击性
    不要有杀伤力
    不夸已能,勿扬人恶,自然能化敌为友

诗与远方

  • 如果你不给自己烦恼
    别人也永远不可能给你烦恼
    因为你不会放在自己的心上

诗与远方

  • 懦弱的人只会裹足不前
    莽撞的人只能引火烧身
    只有真正勇敢的人才能所向披靡

诗与远方

  • 多一分心力去注意别人
    就少一分心力反省自己

诗与远方

  • 有时候我们要冷静问问自己
    我们再追求什么
    我们活着为了什么

诗与远方

  • 彩云飘在空中,自然得意洋洋
    但最多智能换取几声赞美
    唯有化作雨并倾注于土壤之中
    才能给世界创造芳菲

诗与远方

  • 当你快乐时你要想,这快乐不是永恒的
    当你痛苦时你要想,这痛苦也不是永恒的

诗与远方

  • 快乐是一份自然
    做自己想做的事
    做好自己选择的事
    自然地做人,自然地笑,自然地生活

诗与远方

  • 狂妄的人有救
    自卑的人没有救
    认识自己,相信自己,改变自己
    才能改变别人对你的态度

诗与远方

  • 只要永不放弃,持之以恒
    每次挫折,都是你进步的阶梯
    如果你逃避退缩,那就等于自毁前途

诗与远方

  • 用伤害别人的手段来掩饰自己缺点的人是可耻的

诗与远方

  • 玩像玩的,干像干的
    人生苦短,能享受时就享受,能轻松时就轻松
    不要跟自己过不去,要保持一种良好的心境

诗与远方

  • 责人要含蓄,忌太尽
    劝人要委婉,忌太直
    警人要疑似,忌太真

诗与远方

  • 你一定要宽恕众生
    不论他有多坏,甚至伤害过你
    你只有放下了,才能得到真正的快乐

诗与远方

  • 要是面前有一堵墙
    不要轻易退缩逃避
    要想办法绕过去,超越过去
    即使有困难也不要轻易放弃

诗与远方

  • 势不可使尽,聪明不可用尽
    福不可享尽,便宜不可占尽

诗与远方

  • 当你对自己诚实的时候
    世界上没有人能够欺骗得了你

诗与远方

  • 心是最大的骗子
    别人能骗你一时
    而它却会骗你一辈子

诗与远方

  • 大多数的人一辈子只做了三件事
    自欺,欺人,被人欺

诗与远方

  • 一个人如果没有感受过苦难
    就不会体会到他人的苦难
    你要学救苦救难的精神,就得先受苦受难

诗与远方

  • 每一个人都拥有生命
    但并非每个人都懂得生命,珍惜生命
    不了解生命的人,体会不到生命的价值

诗与远方

  • 生活可以是甜的,也可以是苦的
    但不能是没味的
    你可以胜利,也可以失败
    但你不能屈服

随意打赏