我用Java编写了一个程序来查找网站的所有页面,从起始页面的URL开始(使用Jsoup作为webcrawler)。小型网站可以,但是对于200页或更多页面的网站来说太慢了:
public class SiteInspector {
private ObservableSet<String> allUrlsOfDomain; // all URLS found for site
private Set<String> toVisit; // pages that were found but not visited yet
private Set<String> visited; // URLS that were visited
private List<String> invalid; // broken URLs
public SiteInspector() {...}
public void getAllWebPagesOfSite(String entry) //entry must be startpage of a site
{
toVisit.add(entry);
allUrlsOfDomain.add(entry);
while(!toVisit.isEmpty())
{
String next = popElement(toVisit);
getAllLinksOfPage(next); //expensive
toVisit.remove(next);
}
}
public void getAllLinksOfPage(String pageURL) {
try {
if (urlIsValid(pageURL)) {
visited.add(pageURL);
Document document = Jsoup.connect(pageURL).get(); //connect to pageURL (expensive network operation)
Elements links = document.select("a"); //get all links from page
for(Element link : links)
{
String nextUrl = link.attr("abs:href"); // "http://..."
if(nextUrl.contains(new URL(pageURL).getHost())) //ignore URLs to external hosts
{
if(!isForbiddenForCrawlers(nextUrl)) // URLS forbidden by robots.txt
{
if(!visited.contains(nextUrl))
{
toVisit.add(nextUrl);
}
}
allUrlsOfDomain.add(nextUrl);
}
}
}
else
{
invalid.add(pageURL); //URL-validation fails
}
}
catch (IOException e) {
e.printStackTrace();
}
}
private boolean isForbiddenForCrawlers(String url){...}
private boolean urlIsValid(String url) {...}
public String popElement(Set<String> set) {...}
我知道我必须在额外的线程中运行昂贵的网络操作。
Document document = Jsoup.connect(pageURL).get(); //connect to pageURL
我的问题是我不知道如何在保持集合一致的同时正确地外包此操作(如何同步?)。如果可能的话,我想使用ThreadPoolExecutor来控制该过程中正在启动的线程数量。你们有解决办法的想法吗?预先感谢。
答案 0 :(得分:2)
要使用线程并使集合保持一致,只需要创建一个线程即可接收要添加到集合中但创建为空的变量,因此线程完成后将其填充,然后将其添加到集合中。
一个简单的例子可能是:
Main.class
for (String link : links) {
String validUrl = null;
taskThread = new Thread( new WebDownloadThreadHanlder(link, validUrl, barrier));
taskThread.start();
if (validUrl != null) {
allUrlsOfDomain.add(validUrl);
}
}
barrier.acquireUninterruptibly(links.size());
WebDownloadThreadHandler.class
public class WebDownloadThreadHandler implements Runnable {
private String link;
private String validUrl;
private Semaphore barrier;
public ScopusThreadHandler(String link, String validUrl, Semaphore barrier) {
this.link = link;
this.validUrl = null;
this.barrier = barrier;
}
public void run () {
try {
Document document = Jsoup.connect(this.link).userAgent("Mozilla/5.0");
Elements elements = document.select(YOUR CSS QUERY);
/*
YOUR JSOUP CODE GOES HERE, AND STORE THE VALID URL IN: this.validUrl = THE VALUE YOU GET;
*/
} catch (IOException) {
e.printStackTrace();
}
this.barrier.release();
}
}
您在这里要做的是为要从中获取所有链接的每个网站创建一个线程,并将其存储到变量中,如果要从每个页面中检索多个无效链接,则可以使用设置并将其添加到全局集(附加)。关键是要使代码保持一致,您需要将检索到的值存储在使用 THIS 关键字作为参数传递线程的变量中。
希望有帮助!如果您还有其他需要,随时问我!