【问题标题】:Web scraping using multithreading使用多线程进行网页抓取
【发布时间】:2020-07-21 19:55:30
【问题描述】:

我编写了一个代码来查找 IMDB 上的一些电影名称,但如果我正在搜索“哈利波特”,我会找到不止一部电影。我想使用多线程,但我对这方面了解不多。

我正在使用策略设计模式在更多网站之间进行搜索,例如在我拥有此代码的方法之一中

            for (Element element : elements) {
            String searchedUrl = element.select("a").attr("href");
            String movieName = element.select("h2").text();
            if (movieName.matches(patternMatcher)) {
                Result result = new Result();
                result.setName(movieName);
                result.setLink(searchedUrl);
                result.setTitleProp(super.imdbConnection(movieName));
                System.out.println(movieName + " " + searchedUrl);
                resultList.add(result);
            }
        }

对于每个元素(即电影名称),它将在 IMDB 上创建一个新连接,以在 super.imdbConnection(movieName) 行上查找收视率和其他内容。

问题是,我想同时拥有所有的连接,因为在找到 5-6 部电影时,这个过程会比预期的要长得多。

我不是要代码,我想要一些想法。我想过创建一个实现 Runnable 的内部类并使用它,但我没有找到任何意义。

如何重写该循环以使用多线程?

我正在使用 Jsoup 进行解析,Element 和 Elements 来自该库。

【问题讨论】:

  • Element 来自哪个库?另外,我们应该如何知道super.imdbConnection(...) 做了什么?
  • 元素来自 Jsoup,抱歉。此外,imdbConnection 是另一种方法,它建立与 IMDB 的连接,并查找评级、描述、预告片和类型。我没有任何东西可以在那里更快:(。我将编辑我的问题并指定,对不起

标签: java multithreading web-scraping


【解决方案1】:

最简单的方法是parallelStream()

List<Result> resultList = elements.parallelStream()
                                  .map(e -> {
            
                                      String searchedUrl = element.select("a").attr("href");
                                      String movieName = element.select("h2").text();

                                      if(movieName.matches(patternMatcher)){

                                          Result result = new Result();
                                          result.setName(movieName);
                                          result.setLink(searchedUrl);
                                          result.setTitleProp(super.imdbConnection(movieName));
                                          
                                          System.out.println(movieName + " " + searchedUrl);

                                          return result;

                                      }else{
                                          return null;
                                      }

                                  }).filter(Objects::nonNull)
                                  .collect(Collectors.toList());

如果你不喜欢 parallelStream() 并想使用 Threads,你可以这样做:

List<Element> elements = new ArrayList<>();

//create a function which returns an implementation of `Callable`
//input: Element
//output: Callable<Result>
Function<Element, Callable<Result>> scrapFunction = (element) -> new Callable<Result>() {

    @Override
    public Result call() throws Exception{

        String searchedUrl = element.select("a").attr("href");
        String movieName = element.select("h2").text();
        if(movieName.matches(patternMatcher)){

            Result result = new Result();
            result.setName(movieName);
            result.setLink(searchedUrl);
            result.setTitleProp(super.imdbConnection(movieName));
            
            System.out.println(movieName + " " + searchedUrl);

            return result;

        }else{
            return null;
        }

    }

};

//create a fixed pool of threads
ExecutorService executor = Executors.newFixedThreadPool(elements.size());

//submit a Callable<Result> for every Element
//by using scrapFunction.apply(...)
List<Future<Result>> futures = elements.stream()
                                        .map(e -> executor.submit(scrapFunction.apply(e)))
                                        .collect(Collectors.toList());

//collect all results from Callable<Result>
List<Result> resultList = futures.stream()
                                .map(e -> {
                                    try{
                                        return e.get();
                                    }catch(Exception ignored){
                                        return null;
                                    }
                                }).filter(Objects::nonNull)
                                .collect(Collectors.toList());

【讨论】:

  • 它就像一个魅力:)。我想我会开始学习流和更多的多线程!非常感谢!
  • @scrapplez,我更新了我的答案。我添加了使用线程的方式。此外,使用线程可以有多种方式。
  • 再次感谢您!另外,感谢您对第二个的 cmets。
  • @scrapplez,欢迎您。如果您难以理解,请随时提问。
猜你喜欢
  • 1970-01-01
  • 2020-11-07
  • 1970-01-01
  • 1970-01-01
  • 2018-10-15
  • 1970-01-01
  • 2017-03-28
  • 1970-01-01
  • 2020-11-07
相关资源
最近更新 更多