锁定老帖子 主题:网站实时简繁转换解决方案
精华帖 (0) :: 良好帖 (6) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2009-04-01
最后修改:2009-04-02
最近由于工作需要做了一个将网站转成繁体呈现给用户的功能,后来参看了许多成功案例,发现思路大体都是相同的,我现在将我具体的实现拿出来讨论一下,因为它不可能是一个百分百覆盖的功能,所以希望大家能多提意见。 http://www.mydomain.com?methodname=big5&url=http://tieba.baidu.com
在实际操作中,在样的链接出现在浏览器地址栏会对以后的转换造成很多不便,这些不便在下面会我提及,得做Urlrewirte <rule> <from>^/big5/(.*)</from> <to>/big.do?methodname=big5&url=$1</to> </rule> 最终我们URL地址会变成 http://www.mydomain.com/big5/http://tieba.baidu.com
当确定目标网站后,使用htmlparser遍历它的所有结节,即所有node URL url = new URL(strurl); HttpURLConnection httpUrl = (HttpURLConnection) url.openConnection(); Node node; String contentType = httpUrl.getContentType(); String charSet = getCharset(contentType); Lexer lexer = null; if (charSet == null) charset = "GBK"; else charset = charSet; lexer = new Lexer(new Page(new UnicodeInputStream(httpUrl .getInputStream(), charset), charset)); PrototypicalNodeFactory factory = new PrototypicalNodeFactory(); lexer.setNodeFactory(factory); while (null != (node = lexer.nextNode())) { if((node instanceof RemarkNode)) {…} else if((node instanceof TextNode)) {…} else if((node instanceof LinkTag)) {…} …… } 大体按这样的流程走就可以了。
1:设置读取目标网站的编码 String charSet = getCharset(contentType); 如果此时的charSet为空,我会为它设置一个默认值“GBK”,默认用GBK来遍历目标网站,当遍历到某个叫”Meta”的结点时,我会查找它有没有属性中包含类似content="text/html; charset=utf-8"的东西,如果没有,继续遍历,如果有,取得该编码值,将它与当前遍历网站的编码进行比较,如果一致则继续遍历,如果不一致,说明从一开始遍历时设置的编码就是错误的,这个时候需要使用新的编码重新遍历。 public String getCharset(String content) { final String CHARSET_STRING = "charset"; int index; String ret; ret = null; if (null != content) { index = content.indexOf(CHARSET_STRING); if (index != -1) { content = content.substring(index + CHARSET_STRING.length()) .trim(); if (content.startsWith("=")) { content = content.substring(1).trim(); index = content.indexOf(";"); if (index != -1) content = content.substring(0, index); if (content.startsWith("\"") && content.endsWith("\"") && (1 < content.length())) content = content.substring(1, content.length() - 1); if (content.startsWith("'") && content.endsWith("'") && (1 < content.length())) content = content.substring(1, content.length() - 1); ret = findCharset(content, ret); } } } return ret; } public String findCharset(String name, String _default) { String ret; try { Class<java.nio.charset.Charset> cls; Method method; Object object; cls = java.nio.charset.Charset.class; method = cls.getMethod("forName", new Class[] { String.class }); object = method.invoke(null, new Object[] { name }); method = cls.getMethod("name", new Class[] {}); object = method.invoke(object, new Object[] {}); ret = (String) object; } catch (NoSuchMethodException nsme) { ret = name; } catch (IllegalAccessException ia) { ret = name; } catch (InvocationTargetException ita) { ret = _default; System.out .println("unable to determine cannonical charset name for " + name + " - using " + _default); } if (ret.equalsIgnoreCase("gb2312")) ret = "GBK"; return ret; } 在这里我想特别说一下,最后一句: if (ret.equalsIgnoreCase("gb2312")) ret = "GBK"; 如果你实现的是简繁转换功能的话,请加上这句,因为如果网站是gb2312编码,有些繁体字显示转换出来会是乱码 ‘?’.
2:处理相对路径。
3:处理普通图片、背景图片、样式表图片 <body style="background-image:pic.gif">
这样的写法,我们无法准确找到pic.gif字符串并转换它。 String REGEXP_HREF = "[\\w|\\/|\\.]+(http)?(s)?(\\:\\/\\/)?[\\:|\\.|\\/|\\w|\\d|_|\\'|+|-]+\\.(gif|jpg|png|bmp) 当读取可能出现背景图片的标签时,将此标签.toHtml()代码用正则表达式处理替换。这样所有的相对路径图片才能替换成绝对路径。
4:处理JS,VBS <script src="/top.js"></script> 此时我们先将相对路径转为绝对路径 <script src="http://tieba.baidu.com/top.js"></script> 但是,单纯将JS的路径补齐是没有用的,它并不像图片那样显示完以后就没事了,JS代码也会包含简体中文,包含相对路径,这也是需要转换成繁体中文和绝对路径的,所以这个链接我们还得在它前面加上一串,变成 <script src="http://www.mydomain.com/big5/http://tieba.baidu.com/top.js"></script> 还有问题,编码问题,script标签其实有个encoding属性,默认值是当前页面的编码,如果有值就必须用encoding里值的编码解析JS。 <script src="http://www.mydomain.com/big5js/GBK_http://tieba.baidu.com/top.js"></script> 由于是一行一行读取,无法精确取得链接地址,和图片地址
5:处理XML BufferedReader br = new BufferedReader(new UnicodeReader(newURL .openStream(), "UTF-8")); 解析完XML以后,不能像HTML或JS一样,推送到一个空白页,可以将它放在PrintWirte里,这样页面才能调取到。
6:处理链接 <a href="/index.htm">link</a> 用户在繁体页上点击这个链接后,出来的页面也应该是繁体,所以需要将链接也转换一下,最终的写法是: <a href="http://www.mydomian.com/big/http://tieba.baidu.com/index.htm">link</a> 其实这就已经完事了,我之所以单独把处理链接拿出来说,是因为有时写法并不是这样。 <a href="javascript:goUrl('/index.htm')">link</a> <a href="#" onclick=”goUrl('/index.htm')">link</a> 这个时候我们按正常的流程也没办法取得准确的链接字符串,有两个办法,一是将这个LinkTag.toHtml()用正则表达式替换一次,二是将可能出现URL的属性替换一次,第二种方法虽然显得麻烦,但碰到大多数正常的A标签效率会很快。
7:处理文本
8:设置繁体网页的编码
9:处理表单 StringBuilder requestParameters = new StringBuilder(); Enumeration parameterNames = request.getParameterNames(); while (parameterNames.hasMoreElements()) { String name = (String) parameterNames.nextElement(); //过滤掉重复的和不需要的参数 if(name.equals("url")||name.equals("methodname")) continue; String value = request.getParameter(name); String[] values = null; if (value == null) { // 判断是否是数组类型参数 values = request.getParameterValues(name); } if (value != null) { //System.out.println("name:"+name+"value:"+value); value = ChineseToBig5.convert_tosimple(value); requestParameters// .append("%26"+URLEncoder.encode(name, encoding))// .append("=")// .append(URLEncoder.encode(value, encoding))// ; } else if (values != null) { for (String s : values) { s = ChineseToBig5.convert_tosimple(s); requestParameters// .append("%26"+URLEncoder.encode(name, encoding))// .append("=")// .append(URLEncoder.encode(s, encoding))// ; } } } 接着将参数和参数值拼接好,发送到原form的action中.
10:设置过滤 <MyTag><a href="http://tieba.baidu.com">简体版</a></MyTag> Htmlparser有自己的增加和处理自定义标签的方法,逻辑可以这样,当程序遍历时,发现MyTag标签,里面的内容不转换。
11:提高效率
差不多就是这样了,以上内容是我一下午凭记忆打出来的,不知道有没描述清楚,可能有很多遗漏和没有提及的,大家将就着看,我在最开头的时候说这种即时的转换不能涵盖所有的情况,因为HTML的写法千奇百怪,有些JS的写法更是出奇的怪异,百分之百的功能还原和网页还原是不现实的,很多网页的简转繁都有这样那样的涵盖不了的问题,希望大家能提宝贵意见,可以尽量让算法更具兼容性。 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2009-04-02
没有人观注抓取网页这一块么。
|
|
返回顶楼 | |
发表时间:2009-04-03
shingo7 写道 没有人观注抓取网页这一块么。 繁简互转,几年前国内就有成熟产品了,从当初几万一套,卖到现在几百一套,LZ可以google一下。 繁简转换,最麻烦还是分析HTML,还有就是误字,繁体中有些字不是直接翻译简体就对了。 |
|
返回顶楼 | |
浏览 5129 次