微信公号开发实战之历史上的今天

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

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

何谓历史上的今天

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

寻找接口(数据源)

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

代码解读:

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

效果示例:

说明:与其说这是一篇关于公众帐号应用开发的教程,倒不如说这是一篇关于网页数据爬取的教程。本文旨在为读者开辟思路,介绍一种数据获取方式。当然,这种做法也是有弊端的,当网页改版源代码结构发生变化时,就需要重新改写数据抽取代码。

责任编辑:徐川 来源: blog
相关推荐

2013-11-13 01:25:33

微信微信公号微信公众账号

2013-11-13 00:14:16

微信微信公号微信公众账号

2013-11-13 00:51:22

微信微信公号微信公众账号

2013-11-13 00:37:12

微信微信公号微信公众账号

2012-01-12 14:06:34

2013-11-12 23:32:53

微信公号微信公众账号

2013-09-09 16:38:01

诺基亚微软

2013-11-13 00:20:01

微信微信公号微信公众账号

2011-07-01 10:20:32

2010-09-15 08:59:04

开源交易

2023-12-19 11:22:05

2024-03-19 08:00:00

测试漏洞

2023-10-26 00:07:04

2017-07-28 10:55:49

AITayAlexa

2015-12-25 11:34:25

2023-11-27 15:03:26

2012-11-13 10:32:22

2011-03-16 10:00:46

2015-06-08 09:46:04

2014-09-24 09:59:23

微信企业号开发
点赞
收藏

51CTO技术栈公众号