内容概要

本篇文章主要讲解如何在微信公众帐号上实现“历史上的今天”功能。这个例子本身并不复杂,但希望通过对它的学习,读者能够对正则表达式有一个新的认识,能够学会运用现有的网络资源丰富自己的公众账号。

何谓历史上的今天

回顾历史的长河,历史是生活的一面镜子;以史为鉴,可以知兴衰;历史上的每一天,都是喜忧参半;可以了解历史的这一天发生的事件,借古可以鉴今,历史是不能忘记的。查看历史上每天发生的重大事情,增长知识,开拓眼界,提高人文素养。

寻找接口(数据源)

要实现查询“历史上的今天”,首先我们要找到相关数据源。笔者经过搜索发现,网络上几乎没有现成的“历史上的今天”API可以使用,所以我们只能通过爬取、解析网页源代码的方式得到我们需要的数据。笔者发现网站http://www.rijiben.com/上包含“历史上的今天”功能,就用它做数据源了。

开发步骤

为了便于读者理解,我们需要清楚该应用实例的开发步骤,主要如下:

1)发起HTTP GET请求,获取网页源代码。

2)运用正则表达式从网页源代码中抽取我们需要的数据。

3)对抽取得到的数据进行加工(使内容呈现更加美观)。

4)将以上三步进行封装,供外部调用。

5)在公众账号后台调用封装好的“历史上的今天”查询方法。

代码实现

笔者将上述步骤1)、2)、3)中的代码实现封装成了TodayInHistoryService类,并对外提供了getTodayInHistory()方法来获取“历史上的今天”。实现代码如下:

  1. import java.io.BufferedReader;
  2. import java.io.InputStream;
  3. import java.io.InputStreamReader;
  4. import java.net.HttpURLConnection;
  5. import java.net.URL;
  6. import java.text.DateFormat;
  7. import java.text.SimpleDateFormat;
  8. import java.util.Calendar;
  9. import java.util.regex.Matcher;
  10. import java.util.regex.Pattern;
  11. /**
  12. * 历史上的今天查询服务
  13. *
  14. * @author liufeng
  15. * @date 2013-10-16
  16. *
  17. */
  18. public class TodayInHistoryService {
  19. /**
  20. * 发起http get请求获取网页源代码
  21. *
  22. * @param requestUrl
  23. * @return
  24. */
  25. private static String httpRequest(String requestUrl) {
  26. StringBuffer buffer = null;
  27. try {
  28. // 建立连接
  29. URL url = new URL(requestUrl);
  30. HttpURLConnection httpUrlConn = (HttpURLConnection) url.openConnection();
  31. httpUrlConn.setDoInput(true);
  32. httpUrlConn.setRequestMethod("GET");
  33. // 获取输入流
  34. InputStream inputStream = httpUrlConn.getInputStream();
  35. InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
  36. BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
  37. // 读取返回结果
  38. buffer = new StringBuffer();
  39. String str = null;
  40. while ((str = bufferedReader.readLine()) != null) {
  41. buffer.append(str);
  42. }
  43. // 释放资源
  44. bufferedReader.close();
  45. inputStreamReader.close();
  46. inputStream.close();
  47. httpUrlConn.disconnect();
  48. } catch (Exception e) {
  49. e.printStackTrace();
  50. }
  51. return buffer.toString();
  52. }
  53. /**
  54. * 从html中抽取出历史上的今天信息
  55. *
  56. * @param html
  57. * @return
  58. */
  59. private static String extract(String html) {
  60. StringBuffer buffer = null;
  61. // 日期标签:区分是昨天还是今天
  62. String dateTag = getMonthDay(0);
  63. Pattern p = Pattern.compile("(.*)(<div class=\"listren\">)(.*?)(</div>)(.*)");
  64. Matcher m = p.matcher(html);
  65. if (m.matches()) {
  66. buffer = new StringBuffer();
  67. if (m.group(3).contains(getMonthDay(-1)))
  68. dateTag = getMonthDay(-1);
  69. // 拼装标题
  70. buffer.append("≡≡ ").append("历史上的").append(dateTag).append(" ≡≡").append("\n\n");
  71. // 抽取需要的数据
  72. for (String info : m.group(3).split("  ")) {
  73. info = info.replace(dateTag, "").replace("(图)", "").replaceAll("</?[^>]+>", "").trim();
  74. // 在每行末尾追加2个换行符
  75. if (!"".equals(info)) {
  76. buffer.append(info).append("\n\n");
  77. }
  78. }
  79. }
  80. // 将buffer最后两个换行符移除并返回
  81. return (null == buffer) ? null : buffer.substring(0, buffer.lastIndexOf("\n\n"));
  82. }
  83. /**
  84. * 获取前/后n天日期(M月d日)
  85. *
  86. * @return
  87. */
  88. private static String getMonthDay(int diff) {
  89. DateFormat df = new SimpleDateFormat("M月d日");
  90. Calendar c = Calendar.getInstance();
  91. c.add(Calendar.DAY_OF_YEAR, diff);
  92. return df.format(c.getTime());
  93. }
  94. /**
  95. * 封装历史上的今天查询方法,供外部调用
  96. *
  97. * @return
  98. */
  99. public static String getTodayInHistoryInfo() {
  100. // 获取网页源代码
  101. String html = httpRequest("http://www.rijiben.com/");
  102. // 从网页中抽取信息
  103. String result = extract(html);
  104. return result;
  105. }
  106. /**
  107. * 通过main在本地测试
  108. *
  109. * @param args
  110. */
  111. public static void main(String[] args) {
  112. String info = getTodayInHistoryInfo();
  113. System.out.println(info);
  114. }
  115. }

代码解读:

1)27-58行代码是httpRequest()方法,用于发起http get请求,获取指定url的网页源代码。

2)66-92行代码是extract()方法,运用正则表达式从网页源代码中抽取“历史上的今天”数据。

3)111-118行代码是getTodayInHistory()方法,封装给外部调用查询“历史上的今天”。

4)125-128行代码是main方法,用于在本地的开发工具中测试。

5)75-76行代码的作用是判断获取到的“历史上的今天”数据是当天的还是前一天的(因为不能保证www.rijiben.com上的数据一定在凌晨零点准时更新,所以为了保证数据的准确性必须做此判断)。

6)第71行代码是本文的重点,笔者编写的正则表达式规则是“(.*)(<div class=\"listren\">)(.*?)(</div>)(.*)”。正则表达式规则需要根据网页源代码进行编写的,特别是包含“历史上的今天”数据的那部分HTML标签,所以我们先来查看网页源代码。通过httpRequest("http://www.rijiben.com/")方法获取到的网页源代码,与我们通过浏览器访问http://www.rijiben.com/页面再点击右键选择“查看网页源代码”所得到的结果完全一致。我们通过浏览器查看http://www.rijiben.com/的网页源代码,然后找到“历史上的今天”数据所在位置,如下图所示:

从上面的源代码截图中可以看到,我们需要的数据被包含在<div class="listren">标签内,这样就不难理解为什么正则表达式要这样写:

(.*)(<div class=\"listren\">)(.*?)(</div>)(.*)

我们使用括号()将正则表达式规则分成了5组,下面是这些分组的说明:

第1组:(.*)表示网页源代码中<div class="listren">标签之前还有任意多个字符。
第2组:(<div class=\"listren\">)中的反斜杠表示转义,所以该规则就是用于匹配<div class="listren">。
第3组:(.*?)表示在标签<div class="listren">和</div>之间的所有内容,这才是我们真正需要的数据所在。
第4组:(</div>)就是用于匹配<div class="listren">的结束标签。
第5组:(.*)表示在</div>标签之后还有任意多的字符。

掌握了正则表达式规则的含义,就不难理解为什么在extract()方法中全都是在使用m.group(3),因为m.group(3)就表示匹配到数据的第3个分组。m.group(3)的内容如下:

  1. <ul>                      <li><a href="/news6836/" title="0690年10月16日 武则天登上皇位">0690年10月16日 武则天登上皇位</a>  (图)</li>                      <li><a href="/news6837/" title="1854年10月16日 唯美主义运动的倡导者王尔德诞辰">1854年10月16日 唯美主义运动的倡导者王尔德诞辰</a>  </li>                      <li><a href="/news6838/" title="1854年10月16日 德国社会主义活动家考茨基诞生">1854年10月16日 德国社会主义活动家考茨基诞生</a>  </li>                      <li><a href="/news6839/" title="1908年10月16日 阿尔巴尼亚领导人恩维尔·霍查诞辰">1908年10月16日 阿尔巴尼亚领导人恩维尔·霍查诞辰</a>  (图)</li>                      <li><a href="/news6840/" title="1913年10月16日 中国“两弹一星”元勋钱三强诞辰">1913年10月16日 中国“两弹一星”元勋钱三强诞辰</a>  (图)</li>                      <li><a href="/news6841/" title="1922年10月16日 开滦煤矿工人罢工失败">1922年10月16日 开滦煤矿工人罢工失败</a>  (图)</li>                      <li><a href="/news6842/" title="1927年10月16日 德国诺贝尔文学奖得主格拉斯诞生">1927年10月16日 德国诺贝尔文学奖得主格拉斯诞生</a>  (图)</li>                      <li><a href="/news6843/" title="1933年10月16日 抗日同盟军失败">1933年10月16日 抗日同盟军失败</a>  (图)</li>                      <li><a href="/news6844/" title="1950年10月16日 人民解放军进军西藏">1950年10月16日 人民解放军进军西藏</a>  (图)</li>                      <li><a href="/news6845/" title="1954年10月16日 俞平伯《关于红楼梦研究问题的信》发表">1954年10月16日 俞平伯《关于红楼梦研究问题的信》发表</a>  (图)</li>                      <li><a href="/news6846/" title="1959年10月16日 美军将领、国务卿马歇尔去世">1959年10月16日 美军将领、国务卿马歇尔去世</a>  (图)</li>                      <li><a href="/news6847/" title="1964年10月16日 勃列日涅夫取代赫鲁晓夫  成为苏共中央第一书记">1964年10月16日 勃列日涅夫取代赫鲁晓夫  成为苏共中央第一书记</a>  </li>                      <li><a href="/news6848/" title="1964年10月16日 我国第一颗原子弹爆炸成功">1964年10月16日 我国第一颗原子弹爆炸成功</a>  (图)</li>                      <li><a href="/news6849/" title="1973年10月16日 震撼世界的石油危机爆发">1973年10月16日 震撼世界的石油危机爆发</a>  (图)</li>                      <li><a href="/news6850/" title="1978年10月16日 约翰·保罗二世当选新教皇">1978年10月16日 约翰·保罗二世当选新教皇</a>  </li>                      <li><a href="/news6851/" title="1979年10月16日 哈克将军宣布巴基斯坦推迟大选解散政党">1979年10月16日 哈克将军宣布巴基斯坦推迟大选解散政党</a>  </li>                      <li><a href="/news6852/" title="1984年10月16日 图图主教荣获“诺贝尔和平奖”">1984年10月16日 图图主教荣获“诺贝尔和平奖”</a>  </li>                      <li><a href="/news6853/" title="1988年10月16日 北京正负电子对撞机对撞成功">1988年10月16日 北京正负电子对撞机对撞成功</a>  (图)</li>                      <li><a href="/news6854/" title="1991年10月16日 美国小镇枪杀案22人丧生">1991年10月16日 美国小镇枪杀案22人丧生</a>  </li>                      <li><a href="/news6855/" title="1991年10月16日 莫扎特死因有新说">1991年10月16日 莫扎特死因有新说</a>  </li>                      <li><a href="/news6856/" title="1991年10月16日 钱学森获“国家杰出贡献科学家”殊荣">1991年10月16日 钱学森获“国家杰出贡献科学家”殊荣</a>  (图)</li>                      <li><a href="/news6857/" title="1994年10月16日 德国总理科尔四连任">1994年10月16日 德国总理科尔四连任</a>  </li>                      <li><a href="/news6858/" title="1994年10月16日 第十二届广岛亚运会闭幕">1994年10月16日 第十二届广岛亚运会闭幕</a>  </li>                      <li><a href="/news6859/" title="1994年10月16日 修秦陵制秦俑工匠墓葬被发现">1994年10月16日 修秦陵制秦俑工匠墓葬被发现</a>  </li>                      <li><a href="/news6860/" title="1995年10月16日 美国百万黑人男子大游行">1995年10月16日 美国百万黑人男子大游行</a>  (图)</li>                    </ul>

可以看到, 通过正则表达式抽取得到的m.group(3)中仍然有大量的html标签、空格、换行、无关字符等。 我们要想办法把它们全部过滤掉,第83行代码的作用正是如此。

组装文本消息

  1. // 组装文本消息(历史上的今天)
  2. TextMessage textMessage = new TextMessage();
  3. textMessage.setToUserName(fromUserName);
  4. textMessage.setFromUserName(toUserName);
  5. textMessage.setCreateTime(new Date().getTime());
  6. textMessage.setMsgType(WeixinUtil.RESP_MESSAGE_TYPE_TEXT);
  7. textMessage.setFuncFlag(0);
  8. textMessage.setContent(TodayInHistoryService.getTodayInHistoryInfo());

对于公众帐号的消息回复在本系列教程的第5篇已经讲的很详细了,所以在这里笔者只是简单的组装了文本消息。最后,我们来看一下在微信公众帐号上的演示效果:

微信公众帐号开发教程第16篇-应用实例之历史上的今天相关推荐

  1. [041] 微信公众帐号开发教程第17篇-应用实例之智能翻译

    内容概要 本篇文章为大家演示怎样在微信公众帐号上实现"智能翻译",本例中翻译功能是通过调用"百度翻译API"实现的.智能翻译是指用户随意输入想要翻译的内容(单词 ...

  2. 微信公众帐号开发教程第17篇-应用实例之智能翻译

    内容概要 本篇文章为大家演示如何在微信公众帐号上实现"智能翻译",本例中翻译功能是通过调用"百度翻译API"实现的.智能翻译是指用户任意输入想要翻译的内容(单词 ...

  3. 微信公众帐号开发教程第18篇-应用实例之音乐搜索

    引言及内容概要 微信公众平台支持向用户回复音乐消息,用户收到音乐消息后,点击即可播放音乐.通过音乐消息,公众账号可以实现音乐搜索(歌曲点播)功能,即用户输入想听的音乐名称,公众账号返回对应的音乐(歌曲 ...

  4. [042] 微信公众帐号开发教程第18篇-应用实例之音乐搜索

    引言及内容概要 微信公众平台支持向用户回复音乐消息,用户收到音乐消息后,点击即可播放音乐.通过音乐消息,公众账号可以实现音乐搜索(歌曲点播)功能,即用户输入想听的音乐名称,公众账号返回对应的音乐(歌曲 ...

  5. 微信公众帐号开发教程第6篇-消息及消息处理工具的封装

    本篇内容主要讲解如何将微信公众平台定义的消息及消息相关的操作封装成工具类,方面后期的使用  官方文档 接下来要做的就是将消息请求.回复中定义的消息进行封装,建立与之对应的Java类(Java是一门面向 ...

  6. 微信公众帐号开发教程第1篇-引言(转)

    接触微信公众帐号已经有两个多月的时间了,在这期间,除了陆续完好个人公众帐号xiaoqrobot以外,还带领团队为公司开发了两个企业应用:一个是普通类型的公众帐号,还有一个是会议类型的公众帐号.经过这3 ...

  7. 微信公众帐号开发教程第13篇-图文消息全攻略

    引言及内容概要 已经有几位读者抱怨"柳峰只用到文本消息作为示例,从来不提图文消息,都不知道图文消息该如何使用",好吧,我错了,原本以为把基础API封装完.框架搭建好,再给出一个文本 ...

  8. [025] 微信公众帐号开发教程第1篇-引言

    接触微信公众帐号已经有两个多月的时间了,在这期间,除了陆续完善个人公众帐号xiaoqrobot以外,还带领团队为公司开发了两个企业应用:一个是普通类型的公众帐号,另一个是会议类型的公众帐号.经过这3个 ...

  9. 微信公众帐号开发教程第13篇-图文消息全攻略 -- 转载

    引言及内容概要 已经有几位读者抱怨"柳峰只用到文本消息作为示例,从来不提图文消息,都不知道图文消息该如何使用",好吧,我错了,原本以为把基础API封装完.框架搭建好,再给出一个文本 ...

最新文章

  1. CentOS7 service network start命令启动时报错解决方法
  2. 2018 中国AI人才大调查:14张图表解读他们来自何处,又将去往何方?
  3. iOS Core Data
  4. TortoiseSVN新人使用指南
  5. 【BZOJ】2655: calc 动态规划+拉格朗日插值
  6. 云栖专辑 | 阿里开发者们的第6个感悟:享受折磨
  7. 【树莓派学习笔记】二、(无屏幕)SSH远程登录、图形界面及系统配置
  8. 只用一套解决方案,就可解决80%的交通物流行业信息难题
  9. 无法将 lambda 表达式 转换为类型“System.Delegate”,因为它不是委托类型
  10. 【lucene】lucene高亮显示
  11. [原创]Firefox扩展
  12. Microsoft Fakes
  13. 人工智能革命:人类将永生或者灭绝
  14. 川土微电子 | CA-IS3050U隔离式CAN收发器
  15. 【企业开源】小米开源:站在巨人肩膀上的创新
  16. 专访吴军:“腾讯无2B基因,谷歌太平庸”,“我说错了吗?”
  17. 华为PPPoE、PPP/MP、IP-Trunk配置
  18. 有趣的微分方程之齐次方程
  19. C++ 与 elang 通信注意问题
  20. Quartz定时器的时间设置

热门文章

  1. php 隐藏地址栏,工具栏,php – 我怎么能隐藏#!在浏览器地址栏上?
  2. python operator __gt___Python operator.gt方法代码示例
  3. JS学习笔记4-JavaScript 注释
  4. (王道408考研数据结构)第八章排序-第四节2:快速排序
  5. [Python]网络爬虫(十):一个爬虫的诞生全过程(以山东大学绩点运算为例)
  6. P2转P3时出现‘utf-8‘ codec can‘t decode byte 0xb3 in position 0: invalid start byte(\x、decode解码)
  7. golismero web扫描器
  8. Linux系统编程:mmap介绍和使用mmap实现进程间通信
  9. JAVA Web基础6-EL表达式与JSTL标签库
  10. Android 获取信号强度