【问题标题】:Java jsoup using threads not working [closed]Java jsoup使用线程不起作用[关闭]
【发布时间】:2012-10-06 12:10:45
【问题描述】:

我喜欢这样的页面:

www.foo1.bar
www.foo2.bar
www.foo3.bar
.
.
www.foo100.bar

我正在使用库 jsoup 并使用 Thread 同时连接到每个页面:

Thread matchThread = new Thread(task);
matchThread.start();

每个任务,像这样连接到页面,并解析 HTML:

Jsoup.connect("www.fooX.bar").timeout(0).get();

获取大量这些异常:

java.net.ConnectException: Connection timed out: connect
at java.net.PlainSocketImpl.socketConnect(Native Method)
at java.net.PlainSocketImpl.doConnect(PlainSocketImpl.java:351)
at java.net.PlainSocketImpl.connectToAddress(PlainSocketImpl.java:213)
at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:200)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:366)
at java.net.Socket.connect(Socket.java:529)
at sun.net.NetworkClient.doConnect(NetworkClient.java:158)
at sun.net.www.http.HttpClient.openServer(HttpClient.java:388)
at sun.net.www.http.HttpClient.openServer(HttpClient.java:523)
at sun.net.www.http.HttpClient.<init>(HttpClient.java:227)
at sun.net.www.http.HttpClient.New(HttpClient.java:300)
at sun.net.www.http.HttpClient.New(HttpClient.java:317)
at sun.net.www.protocol.http.HttpURLConnection.getNewHttpClient(HttpURLConnection.java:970)
at sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:911)
at sun.net.www.protocol.http.HttpURLConnection.connect(HttpURLConnection.java:836)
at org.jsoup.helper.HttpConnection$Response.execute(HttpConnection.java:404)
at org.jsoup.helper.HttpConnection$Response.execute(HttpConnection.java:391)
at org.jsoup.helper.HttpConnection.execute(HttpConnection.java:157)
at org.jsoup.helper.HttpConnection.get(HttpConnection.java:146)

jsoup 是否同时只允许 1 个线程?或者我做错了什么? 关于如何更快地连接到我的页面的任何建议,因为逐一进行需要很长时间。

编辑:

所有 700 个线程都使用这种方法,也许这是问题所在。这个方法能处理这么多线程还是单例?

private static Document connect(String url) {
    Document doc = null;
    try {
        doc = Jsoup.connect(url).timeout(0).get();
    } catch (IOException e) {
        System.out.println(url);
    }
    return doc; 
}

编辑:整个线程代码

public class MatchWorker implements Callable<Match>{

private Element element;

public MatchWorker(Element element) {
    this.element = element;
}

@Override
public Match call() throws Exception {
    Match match = null;
            Util.connectAndDoStuff();
    return match;
}

}

我的所有 700 元素:

    Collection<Match> matches = new ArrayList<Match>();
    Collection<Future<Match>> results = new ArrayList<Future<Match>>();

 for (Element element : elements) {
        MatchWorker matchWorker = new MatchWorker(element);
        FutureTask<Match> task = new FutureTask<Match>(matchWorker);
        results.add(task);

        Thread matchThread = new Thread(task);
        matchThread.start();
    }
    for(Future<Match> match : results) {
        try {
            matches.add(match.get());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

【问题讨论】:

  • 您是否仔细检查过 www.fooX.bar 是否在线 - 例如浏览器可以访问它还是你可以在端口 80 上远程登录它?
  • 是的,我可以访问它,它基本上只是一个和其他网站一样的网站。
  • @Jaanus:我也经常遇到这个问题。您能找到解决方案吗?

标签: java multithreading jsoup runnable callable


【解决方案1】:

我试过这个:

    ExecutorService executorService = Executors.newFixedThreadPool(5);
    List<Future<Void>> handles = new ArrayList<Future<Void>>();
    Future<Void> handle;
    for (int i=0;i < 12; i++) {
        handle = executorService.submit(new Callable<Void>() {

            public Void call() throws Exception {
                Document d = Jsoup.connect("http://www.google.hr").timeout(0).get();
                System.out.println(d.title());
                return null;
            }
        });
        handles.add(handle);
    }

    for (Future<Void> h : handles) {
        try {
            h.get();
        } 
        catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    executorService.shutdownNow();

它几乎立即完成并打印正确的标题。也许您有防火墙问题? (“连接超时”表示根本无法访问服务器)

编辑:

我使用 JSoup 1.7.1

编辑^2:

AFAIK,这应该证明关系 JSoup - Thread 没有问题,因为它最终使用线程..

编辑^3:

另外,如果您使用代理,here 是您可以设置代理设置的方法。

编辑^4:

public static Document connect(String url) {
    Document doc = null;
    try {
        doc = Jsoup.connect(url).timeout(0).get();
    } catch (IOException ex) {
        ex.printStackTrace();
    }
    return doc;
}

并重写了调用函数:

public Void call() throws Exception {                    
    System.out.println(App.connect("http://www.google.hr").title());
    return null;
}

给出相同的结果。我唯一能想到的是一些隐式静态同步,但这没有多大意义,因为有一个超时异常:/请发布线程代码

编辑:

必须离开几个小时。这里我所有的三个类都重写了

仍然工作,速度较慢,但​​工作。我肯定会推荐使用固定线程池来提高性能。

但是,我认为这一定是网络问题。祝你好运:)

编辑:

连接超时意味着根本无法访问目标服务器。 AFAIK,这意味着(服务器从未发送/客户端从未收到)TCP SYN+ACK message

可以断定的第一件事是目标服务器不在线,但是有more possible causes to this problem,一个可能是目标服务器的请求超载(极端情况下是(D)DoS attack)。

目前,您尝试了并行方法 - 每个请求都在自己的线程中:

1) 在 700 个线程中发出 700 个请求(实际上不是 700 个,而是您的操作系统可以接受的数量)

2) 通过 n

首先你可以尝试在每个请求中放置一个随机的睡眠形式 0 - 10 s

Thread.currentThread.sleep(new Random().nextInt(10000)) 

但鉴于目前的结果,这可能行不通。接下来的事情是用您在评论中提到的顺序方法替换并行 - 每个请求都从单个主线程的 for 循环内部一个接一个地运行。您也可以尝试将随机睡眠。

这是您可以采用的最温和(最慢)的方式,如果这不起作用,我不知道如何解决:(

编辑:

通过使用 5 个线程的线程池,我成功下载了 1141 场足球比赛的标题。

这样的网站保护他们的数据是合乎逻辑的,所以当你在开发和测试时(使用尽可能多的线程重复运行)他们的系统最有可能识别出你( r IP)作为一个想要他们所有数据的爬虫,他们显然不喜欢也不想要那个,所以他们禁止你。他们只是决定甚至不拒绝您的请求,而是装死 - 因此连接超时。现在这是有道理的。呼:)

如果这是正确的,您应该能够通过代理获取数据,但礼貌并使用

【讨论】:

  • 不,我有最新的 jsoup 而不是代理,我正好有 765 个站点要浏览:每个站点都不同。我的线程使用相同的静态方法进行连接,这可能会导致问题,请参阅第一页的更新。
  • 你能发布整个工作线程代码吗?我尝试了静态方法,很快看到编辑
  • 我把所有线程代码放到第一个帖子...
  • 好吧,这样会更好,当减慢线程速度,而不是创建 700 个线程,但在某些情况下仍会得到 java.net.ConnectException: Connection timed out: connect
  • 我会尝试使用类似 wireshark 的东西来嗅探流量:/
【解决方案2】:

Jsoup 只是调用 HTTPUrlConnection 进行连接。它对该连接是否失败没有影响。可能影响它的是防火墙和 DDOS 预防设备。网站拒绝大量同时连接并不奇怪。

【讨论】:

  • 但是,他不是应该先搞定,然后连接被拒绝而不是连接超时吗?
  • 好吧,我可以想象一个非常聪明的谷歌在看到 syn 是否是一群人的一部分之前不会响应,并拒绝它们。
  • 是的,我意识到这一点,但告诉我这是正确的:当第一个 SYN 被 refused 抛出的异常不会是 Connection 拒绝?是否永远不会收到 SYN+ACK,那么抛出的连接应该是 Connection Timeout
  • 大部分情况下,我已经阅读了 jsoup 的源代码,它没有可能产生这些异常的心灵感应影响。
猜你喜欢
  • 1970-01-01
  • 2019-03-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-17
  • 2018-07-26
  • 1970-01-01
相关资源
最近更新 更多