免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 4920 | 回复: 9
打印 上一主题 下一主题

java开发一个搜索导航网站2  [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2007-11-28 23:31 |只看该作者 |倒序浏览
定向抓取网站频道
       如果我们要想定向抓取一个网站的某个特定频道,例如JAVA编程。就要分析这个频道在网页中所处的位置。一般来说某个
    频道的文章列表一定是在某个特定的TABLE,或者DIV中。所以定向分析页面的核心就在于定位这个频道的TABLE位置。我们可以用
    递归的方式搜索网页,给每个TABLE编号,然后列出每个TABLE或者DIV的编号和其中的内容就可以准确定位。一般来说每个网站都有
    自己的风格,不会每天都变,所以用这个方式定位频道文章列表是很可靠的,而且一旦网站改版后,TABLE或者DIV的数量会发生变化
    我们可以根据这个变化知道,然后重新定位就可以了。用这种方式配置抓取模版是种不错的尝试。

    下面的代码中,请大家重点关注public List extractHtml(Node nodeP)  函数,该函数利用递归的方法定位频道。
    该代码利用了HTMLPARSER的抓取分析功能。

  1. package com.newwatch.tools;

  2. import com.newwatch.toolkit.splitwords.SpiderConstant;
  3. import com.newwatch.toolkit.splitwords.SplitManager;

  4. import org.htmlparser.Node;
  5. import org.htmlparser.Parser;

  6. import org.htmlparser.nodes.TextNode;

  7. import org.htmlparser.tags.Div;
  8. import org.htmlparser.tags.Html;
  9. import org.htmlparser.tags.LinkTag;
  10. import org.htmlparser.tags.ScriptTag;
  11. import org.htmlparser.tags.SelectTag;
  12. import org.htmlparser.tags.StyleTag;
  13. import org.htmlparser.tags.TableTag;

  14. import org.htmlparser.util.NodeIterator;
  15. import org.htmlparser.util.NodeList;

  16. import java.util.ArrayList;
  17. import java.util.Iterator;
  18. import java.util.List;
  19. import java.util.regex.Matcher;
  20. import java.util.regex.Pattern;


  21. public class TestTabDivSerial {
  22.     /**
  23.     * A newline.
  24.     */
  25.     private static final String NEWLINE = System.getProperty("line.separator");

  26.     /**
  27.      * The length of the NEWLINE.
  28.      */
  29.     private static final int NEWLINE_SIZE = NEWLINE.length();
  30.     private String url;
  31.     private final String oriEncode = "gb2312,utf-8,gbk,iso-8859-1";
  32.     private ArrayList htmlContext = new ArrayList();
  33.     private String urlEncode;
  34.     private int tableNumber;
  35.     private int channelNumber;
  36.     private int totalNumber;

  37.     //url正则表达
  38.     private String domain;
  39.     private String urlDomaiPattern;
  40.     private String urlPattern;
  41.     private Pattern pattern;
  42.     private Pattern patternPost;

  43.     public void channelParseProcess() {
  44.         /**提取本站信息的正则表达式**/
  45.         urlDomaiPattern = "(http://[^/]*?" + domain + "/)(.*?)";
  46.         urlPattern = "(http://[^/]*?" + domain +
  47.             "/[^.]*?).(shtml|html|htm|shtm|php|asp#|asp|cgi|jsp|aspx)";

  48.         pattern = Pattern.compile(urlDomaiPattern,
  49.                 Pattern.CASE_INSENSITIVE + Pattern.DOTALL);
  50.         patternPost = Pattern.compile(urlPattern,
  51.                 Pattern.CASE_INSENSITIVE + Pattern.DOTALL);

  52.         /**收集表单集合**/
  53.         SplitManager splitManager = (SplitManager) ExtractLinkConsole.context.getBean(
  54.                 "splitManager");
  55.         urlEncode = dectedEncode(url);

  56.         if (urlEncode == null) {
  57.             return;
  58.         }

  59.         singContext(url);

  60.         Iterator hi = htmlContext.iterator();

  61.         if (htmlContext.size() == 0) {
  62.             return;
  63.         }

  64.         totalNumber = htmlContext.size();

  65.         //分析表单集合
  66.         while (hi.hasNext()) {
  67.             TableContext tc = (TableContext) hi.next();

  68.             this.totalNumber = tc.getTableRow();

  69.             if ((tc.getTableRow() == this.channelNumber) ||
  70.                     (this.channelNumber == -1)) {
  71.                 System.out.println("*********************表单" +
  72.                     tc.getTableRow() + "****************");

  73.                 List linkList = tc.getLinkList();

  74.                 //如果没有任何连接
  75.                 if ((linkList == null) || (linkList.size() == 0)) {
  76.                     continue;
  77.                 }

  78.                 Iterator hl = linkList.iterator();

  79.                 /**分析单个表单**/
  80.                 while (hl.hasNext()) {
  81.                     LinkTag lt = (LinkTag) hl.next();

  82.                     //**过滤非法link*
  83.                     if (isValidLink(lt.getLink()) == SpiderConstant.OUTDOMAINLINKTYPE) {
  84.                         continue;
  85.                     }

  86.                     if (lt.getLinkText().length() < 8) {
  87.                         continue;
  88.                     }

  89.                     /**过滤无效link**/
  90.                     if (splitManager.isChannelLink(lt.getLinkText()) != SpiderConstant.COMMONCHANNEL) {
  91.                         continue;
  92.                     }

  93.                     /**生成link的hashcode**/
  94.                     System.out.println("URL:" + lt.getLinkText() + "   " +
  95.                         lt.getLink());
  96.                 }
  97.             }
  98.         }
  99.     }

  100.     /**
  101.     *判断是否有效连接
  102.     * @return
  103.     */
  104.     public int isValidLink(String link) {
  105.         Matcher matcher = pattern.matcher(link);

  106.         while (matcher.find()) {
  107.             int start = matcher.start(2);
  108.             int end = matcher.end(2);

  109.             String postUrl = link.substring(end).trim();

  110.             //如果是目录型连接
  111.             if ((postUrl.length() == 0) || (postUrl.indexOf(".") < 0)) {
  112.                 return SpiderConstant.CHANNELLINKTYPE;
  113.             } else {
  114.                 Matcher matcherPost = patternPost.matcher(link);

  115.                 if (matcherPost.find()) {
  116.                     return SpiderConstant.COMMONLINKTYPE;
  117.                 } else {
  118.                     return SpiderConstant.OUTDOMAINLINKTYPE;
  119.                 }
  120.             }
  121.         }

  122.         return SpiderConstant.OUTDOMAINLINKTYPE;
  123.     }

  124.     /**
  125.      * 收集HTML页面信息
  126.      * @param url
  127.      * @param urlEncode
  128.      */
  129.     public void singContext(String url) {
  130.         try {
  131.             Parser parser = new Parser(url);
  132.             parser.setEncoding(urlEncode);
  133.             tableNumber = 0;

  134.             for (NodeIterator e = parser.elements(); e.hasMoreNodes();) {
  135.                 Node node = (Node) e.nextNode();

  136.                 if (node instanceof Html) {
  137.                     extractHtml(node);
  138.                 }
  139.             }
  140.         } catch (Exception e) {
  141.         }
  142.     }

  143.     /**
  144.     * 递归钻取信息
  145.     * @param nodeP
  146.     * @return
  147.     */
  148.     public List extractHtml(Node nodeP) {
  149.         NodeList nodeList = nodeP.getChildren();
  150.         boolean bl = false;

  151.         if ((nodeList == null) || (nodeList.size() == 0)) {
  152.             return null;
  153.         }

  154.         if ((nodeP instanceof TableTag) || (nodeP instanceof Div)) {
  155.             bl = true;
  156.         }

  157.         ArrayList tableList = new ArrayList();

  158.         try {
  159.             for (NodeIterator e = nodeList.elements(); e.hasMoreNodes();) {
  160.                 Node node = (Node) e.nextNode();

  161.                 if (node instanceof LinkTag) {
  162.                     tableList.add(node);
  163.                 } else if (node instanceof ScriptTag ||
  164.                         node instanceof StyleTag || node instanceof SelectTag) {
  165.                 } else if (node instanceof TextNode) {
  166.                     if (node.getText().trim().length() > 0) {
  167.                         tableList.add(node);
  168.                     }
  169.                 } else {
  170.                     List tempList = extractHtml(node);

  171.                     if ((tempList != null) && (tempList.size() > 0)) {
  172.                         Iterator ti = tempList.iterator();

  173.                         while (ti.hasNext()) {
  174.                             tableList.add(ti.next());
  175.                         }
  176.                     }
  177.                 }
  178.             }
  179.         } catch (Exception e) {
  180.         }

  181.         if ((tableList != null) && (tableList.size() > 0)) {
  182.             if (bl) {
  183.                 TableContext tc = new TableContext();
  184.                 tc.setLinkList(new ArrayList());
  185.                 tc.setTextBuffer(new StringBuffer());
  186.                 tableNumber++;
  187.                 tc.setTableRow(tableNumber);

  188.                 Iterator ti = tableList.iterator();

  189.                 while (ti.hasNext()) {
  190.                     Node node = (Node) ti.next();

  191.                     if (node instanceof LinkTag) {
  192.                         tc.getLinkList().add(node);
  193.                     } else {
  194.                         tc.getTextBuffer()
  195.                           .append(collapse(node.getText()
  196.                                                .replaceAll(" ", "")));
  197.                     }
  198.                 }

  199.                 htmlContext.add(tc);

  200.                 return null;
  201.             } else {
  202.                 return tableList;
  203.             }
  204.         }

  205.         return null;
  206.     }

  207.     /**
  208.      * 去除无效字符
  209.      * @param string
  210.      * @return
  211.      */
  212.     protected String collapse(String string) {
  213.         int chars;
  214.         int length;
  215.         int state;
  216.         char character;
  217.         StringBuffer buffer = new StringBuffer();
  218.         chars = string.length();

  219.         if (0 != chars) {
  220.             length = buffer.length();
  221.             state = ((0 == length) || (buffer.charAt(length - 1) == ' ') ||
  222.                 ((NEWLINE_SIZE <= length) &&
  223.                 buffer.substring(length - NEWLINE_SIZE, length).equals(NEWLINE)))
  224.                 ? 0 : 1;

  225.             for (int i = 0; i < chars; i++) {
  226.                 character = string.charAt(i);

  227.                 switch (character) {
  228.                 case '\u0020':
  229.                 case '\u0009':
  230.                 case '\u000C':
  231.                 case '\u200B':
  232.                 case '\u00a0':
  233.                 case '\r':
  234.                 case '\n':

  235.                     if (0 != state) {
  236.                         state = 1;
  237.                     }

  238.                     break;

  239.                 default:

  240.                     if (1 == state) {
  241.                         buffer.append(' ');
  242.                     }

  243.                     state = 2;
  244.                     buffer.append(character);
  245.                 }
  246.             }
  247.         }

  248.         return buffer.toString();
  249.     }

  250.     /**
  251.      * 检测字符级
  252.      * @param url
  253.      * @return
  254.      */
  255.     private String dectedEncode(String url) {
  256.         String[] encodes = oriEncode.split(",");

  257.         for (int i = 0; i < encodes.length; i++) {
  258.             if (dectedCode(url, encodes[i])) {
  259.                 return encodes[i];
  260.             }
  261.         }

  262.         return null;
  263.     }

  264.     public boolean dectedCode(String url, String encode) {
  265.         try {
  266.             Parser parser = new Parser(url);
  267.             parser.setEncoding(encode);

  268.             for (NodeIterator e = parser.elements(); e.hasMoreNodes();) {
  269.                 Node node = (Node) e.nextNode();

  270.                 if (node instanceof Html) {
  271.                     return true;
  272.                 }
  273.             }
  274.         } catch (Exception e) {
  275.         }

  276.         return false;
  277.     }

  278.     public String getDomain() {
  279.         return domain;
  280.     }

  281.     public void setDomain(String domain) {
  282.         this.domain = domain;
  283.     }

  284.     public Pattern getPattern() {
  285.         return pattern;
  286.     }

  287.     public void setPattern(Pattern pattern) {
  288.         this.pattern = pattern;
  289.     }

  290.     public Pattern getPatternPost() {
  291.         return patternPost;
  292.     }

  293.     public void setPatternPost(Pattern patternPost) {
  294.         this.patternPost = patternPost;
  295.     }

  296.     public String getUrlDomaiPattern() {
  297.         return urlDomaiPattern;
  298.     }

  299.     public void setUrlDomaiPattern(String urlDomaiPattern) {
  300.         this.urlDomaiPattern = urlDomaiPattern;
  301.     }

  302.     public String getUrlPattern() {
  303.         return urlPattern;
  304.     }

  305.     public void setUrlPattern(String urlPattern) {
  306.         this.urlPattern = urlPattern;
  307.     }

  308.     public int getChannelNumber() {
  309.         return channelNumber;
  310.     }

  311.     public void setChannelNumber(int channelNumber) {
  312.         this.channelNumber = channelNumber;
  313.     }

  314.     public int getTotalNumber() {
  315.         return totalNumber;
  316.     }

  317.     public void setTotalNumber(int totalNumber) {
  318.         this.totalNumber = totalNumber;
  319.     }

  320.     public String getUrlEncode() {
  321.         return urlEncode;
  322.     }

  323.     public void setUrlEncode(String urlEncode) {
  324.         this.urlEncode = urlEncode;
  325.     }

  326.     public String getUrl() {
  327.         return url;
  328.     }

  329.     public void setUrl(String url) {
  330.         this.url = url;
  331.     }
  332. }
复制代码

[ 本帖最后由 sakulagi 于 2007-12-5 16:16 编辑 ]

论坛徽章:
0
2 [报告]
发表于 2007-12-02 20:40 |只看该作者
很好,顶一个

论坛徽章:
0
3 [报告]
发表于 2007-12-05 22:44 |只看该作者
哈哈 同行 htmlparser 非常好用

论坛徽章:
0
4 [报告]
发表于 2007-12-06 12:55 |只看该作者
解析确实是最烦的事,
我想用模板来解析,
但发现制作模板是个非常痛苦的事,
之前都是自己写的,
正好试试用htmlparser来帮我做模板!

论坛徽章:
0
5 [报告]
发表于 2007-12-06 15:28 |只看该作者
改版很多的,,我们这里几万个。。。每天都有

论坛徽章:
0
6 [报告]
发表于 2007-12-07 11:30 |只看该作者
那你也贡献一下吧

论坛徽章:
0
7 [报告]
发表于 2007-12-07 21:48 |只看该作者
解析这一块一直没有好的方法,所以代码就不帖了,和大家交流下我的想法吧,欢迎指正!

我想做一个解析是做定制的解析,
不只是提取页面上所有的A连接值什么的!

而是想按照一定的特征码来提取连接,文字....等,
例如:
提取具有一定特征码的 table 标签下 第2个height=300 的 tr  标签下的 第3个 width=20 的 td 的文本值,
而且有可能有多个具备特征码的 table.

所以解析的类大致上要支持:
1:根据主模板循环查找有特征码的节点(例如:得到table)
2:对得到的节点根据子模板进行子解析得到子节点(例如:得到tr)
3:对子节点可以实现再解析(例如:得到td)
如上3步通过递归调用多个模板可以实现,
思路好懂,
模板是难点,
要定位到有特征码的特定标签上是个及其困难的事,
因为主子模板间是存在依存关系的,
一错皆错,
精力都费在这上面,
感觉实用性就卡在这里了!

[ 本帖最后由 tangchaodong 于 2007-12-7 21:50 编辑 ]

论坛徽章:
0
8 [报告]
发表于 2007-12-07 22:35 |只看该作者
分析的好啊:wink: :wink: :wink: :wink:

论坛徽章:
0
9 [报告]
发表于 2007-12-11 13:58 |只看该作者
用递归方式 对于抓取小信息量还好,但多了就会出问题的。

小弟我最近也做做相关的东西,也写了个spider,每天抓500万以上的网页没什么问题。

如果想抓取大量的信息,建议将线程控制独立到单独的调度器里面去实现,蜘蛛线程就负责自己的单独工作就可以了。
个人觉得这样做比较好,至少我是这样做的。

论坛徽章:
0
10 [报告]
发表于 2007-12-15 23:31 |只看该作者
恩,破碎细胞说的非常的对,
我也是这样做的,
我上面说的也只是这整个过程的其中关于解析的部分,
而且这对于编程来说也是个良好的习惯,
规划好各自要做的事,
多余的事让别的"同事"来做,
一个人都把事做完了,
别人不就只是摆设了吗?
显然你是老板你也不愿意看到这样的事情!

[ 本帖最后由 tangchaodong 于 2007-12-15 23:33 编辑 ]
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP