用Java编程实现“网络蜘蛛”

读取并解析HTML

Java同时支持访问URL内容及解析HTML,而这正是“processURL”方法要做的。在Java中读取URL内容相对还比较简单,下面就是“processURL”方法实现此功能的代码:

URLConnection connection = url.openConnection(); if ( (connection.getContentType()!=null) && !connection.getContentType().toLowerCase() .startsWith("text/") ) { getWorkloadWaiting().remove(url); getWorkloadProcessed().add(url); log("Not processing because content type is: " + connection.getContentType() ); return; }

首先,为每个传递进来的变量url中存储的URL构造一个“URLConnection”对象,因为网站上会有多种类型的文档,而“蜘蛛”只对那些包含HTML,尤其是基于文本的文档感兴趣。前述代码是为了确保文档内容以“text/”打头,如果文档类型为非文本,会从等待区移除此URL,并把它添加到已处理区,这也是为了保证不会再次访问此URL。

在对特定URL建立连接之后,接下来就要解析其内容了。下面的代码打开了URL连接,并读取内容:

InputStream is = connection.getInputStream(); Reader r = new InputStreamReader(is);

现在,我们有了一个Reader对象,可以用它来读取此URL的内容,对本文中的“蜘蛛”来说,只需简单地把其内容传递给HTML解析器就可以了。本例中使用的HTML解析器为Swing HTML解析器,其由Java内置,但由于Java对HTML解析的支持力度不够,所以必须重载一个类来实现对HTML解析器的访问,这就是为什么我们要调用“HTMLEditorKit”类中的“getParser”方法。但不幸的是,Sun公司把这个方法置为protected,唯一的解决办法就是创建自己的类并重载“getParser”方法,并把它置为public,这由“HTMLParse”类来实现,请看例4:

import javax.swing.text.html.*; public class HTMLParse extends HTMLEditorKit { public HTMLEditorKit.Parser getParser() { return super.getParser(); } }

这个类用在Spider类的“processURL”方法中,我们也会看到,Reader对象会用于读取传递到“HTMLEditorKit.Parser”中网页的内容:

HTMLEditorKit.Parser parse = new HTMLParse().getParser(); parse.parse(r,new Parser(url),true);

请留意,这里又构造了一个新的Parser类,这个Parser类是一个Spider类中的内嵌类,而且还是一个回调类,它包含了对应于每种HTML tag将要调用的特定方法。在本文中,我们只需关心两类回调函数,它们分别对应一个简单tag(即不带结束tag的tag,如<br>)和一个开始tag,这两类回调函数名为“handleSimpleTag”和“handleStartTag”。因为每种的处理过程都是一样的,所以“handleStartTag”方法仅是简单地调用“handleSimpleTag”,而“handleSimpleTag”则会负责从文档中取出超链接,这些超链接将会用于定位“蜘蛛”要访问的其他页面。在当前tag被解析时,“handleSimpleTag”会检查是否存在一个“href”或超文本引用:

String href = (String)a.getAttribute(HTML.Attribute.HREF); if( (href==null) && (t==HTML.Tag.FRAME) ) href = (String)a.getAttribute(HTML.Attribute.SRC); if ( href==null ) return;

如果不存在“href”属性,会继续检查当前tag是否为一个Frame,Frame会使用一个“src”属性指向其他页面,一个典型的超链接通常为以下形式:

<a href="linkedpage.html">Click Here</a>

上面链接中的“href”属性指向其链接到的页面,但是“linkedpage.html”不是一个地址,它只是指定了这个Web服务器上一个页面上的某处,这称为相对URL,相对URL必须被解析为绝对URL,而这由以下代码完成:

这又会构造一个URL,str为相对URL,base为这个URL上的页面,这种形式的URL类构造函数可构造一个绝对URL。在URL变为正确的绝对形式之后,通过检查它是否在等待区,来确认此URL是否已经被处理过。如果此URL没有被处理过,它会添加到等待区,之后,它会像其他URL一样被处理。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的 Java 实现纸牌游戏 24 点的示例代码: ```java import java.util.ArrayList; import java.util.List; import java.util.Scanner; public class SpiderCardGame { private static final int TARGET_NUMBER = 24; private static final double EPSILON = 1e-6; public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.out.print("请输入 4 张牌(用空格隔开):"); List<Double> cards = new ArrayList<>(); for (int i = 0; i < 4; i++) { cards.add(scanner.nextDouble()); } if (canMake24(cards)) { System.out.println("可以凑出 24 点!"); } else { System.out.println("无法凑出 24 点!"); } } private static boolean canMake24(List<Double> cards) { if (cards.size() == 1) { return Math.abs(cards.get(0) - TARGET_NUMBER) < EPSILON; } for (int i = 0; i < cards.size(); i++) { for (int j = i + 1; j < cards.size(); j++) { double a = cards.get(i), b = cards.get(j); List<Double> remainingCards = new ArrayList<>(cards); remainingCards.remove(j); remainingCards.remove(i); if (canMake24(remainingCards.subList(0, remainingCards.size()), a + b)) { return true; } if (canMake24(remainingCards.subList(0, remainingCards.size()), a - b)) { return true; } if (canMake24(remainingCards.subList(0, remainingCards.size()), b - a)) { return true; } if (canMake24(remainingCards.subList(0, remainingCards.size()), a * b)) { return true; } if (b != 0 && canMake24(remainingCards.subList(0, remainingCards.size()), a / b)) { return true; } if (a != 0 && canMake24(remainingCards.subList(0, remainingCards.size()), b / a)) { return true; } } } return false; } private static boolean canMake24(List<Double> cards, double current) { cards.add(current); return canMake24(cards); } } ``` 该程序使用递归实现了对 4 张牌能否凑出 24 点的判断。其中,`canMake24(cards)` 方法用于判断某个列表中的牌是否能凑出 24 点,`canMake24(cards, current)` 方法用于在某个列表中加入当前计算结果后再进行判断。具体实现中,我们使用了两层循环来枚举所有可能的两张牌,然后对这两张牌进行加、减、乘、除等操作,再将得到的结果递归传入下一层判断,直到判断出是否能凑出 24 点。注意在除法操作时要注意被除数是否为 0,以及浮点数比较时要使用一个很小的误差范围来避免精度问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值