java - 使用 Java 提取维基百科中的类别树

标签 java web-crawler deadlock wikipedia wikipedia-api

基本上,我打算使用维基百科 API 沙箱在根节点“经济学”下提取维基百科中的整个类别树。我不需要文章的内容,我只需要一些基本细节,如 pageid、标题、修订历史记录(在我工作的后期阶段)。到目前为止,我可以逐级提取它,但我想要的是一个递归/迭代函数来完成它。 每个类别包含一个类别和文章(就像每个根包含节点和叶子一样)。 我编写了一个代码将第一级提取到文件中。第一个文件包含文章,第二个文件夹包含类别名称(根的子级,可以进一步分割)。 然后我进入级别并使用类似的代码提取它们的类别、文章和子类别。 每种情况下的代码都保持相似,但具有可扩展性。我需要到达所有节点的最低叶子。所以我需要一个不断检查直到最后的递归。 我将包含类别的文件标记为“c_”,这样我就可以在提取不同级别时提供条件。 现在由于某种原因它陷入了僵局并且不断地一次又一次地添加相同的东西。我需要摆脱僵局的方法。

package wikiCrawl;
import java.awt.List;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Scanner;
import org.apache.commons.io.FileUtils;
import org.json.CDL;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;


public class SubCrawl 
{
public static void  main(String[] args)   throws IOException, InterruptedException, JSONException 
{   File file = new File("C:/Users/User/Desktop/Root/Economics_2.txt"); 
    crawlfile(file);    
}

public static void crawlfile(File food) throws JSONException, IOException ,InterruptedException
{           
    ArrayList<String> cat_list =new ArrayList <String>();
            Scanner scanner_cat = new Scanner(food);
            scanner_cat.useDelimiter("\n");
            while (scanner_cat.hasNext())
            {
                String scan_n = scanner_cat.next();
                if(scan_n.indexOf(":")>-1)
                    cat_list.add(scan_n.substring(scan_n.indexOf(":")+1));              
            }

            System.out.println(cat_list);

            //get the categories in different languages
            URL category_json; 
            for (int i_cat=0; i_cat<cat_list.size();i_cat++)
            {           
                category_json = new URL("https://en.wikipedia.org/w/api.php?action=query&format=json&list=categorymembers&cmtitle=Category%3A"+cat_list.get(i_cat).replaceAll(" ", "%20").trim()+"&cmlimit=500"); //.trim() removes trailing and following whitespaces
                System.out.println(category_json);
                HttpURLConnection urlConnection = (HttpURLConnection) category_json.openConnection(); //Opens the connection to the URL so clients can communicate with the resources.
                BufferedReader reader = new BufferedReader (new InputStreamReader(category_json.openStream()));

                String line;
                String diff = "";
                while ((line = reader.readLine()) != null) 
                {
                    System.out.println(line);
                    diff=diff+line; 
                }
                urlConnection.disconnect();
                reader.close();

                JSONArray jsonarray_cat = new JSONArray (diff.substring(diff.indexOf("[{\"pageid\"")));
                System.out.println(jsonarray_cat);
                //Loop categories
                for (int i_url = 0; i_url<jsonarray_cat.length();i_url++) //jSONarray is an array of json objects, we are looping through each object
                {

                    //Get the URL _part (Categorie isn't correct)
                    int pageid=Integer.parseInt(jsonarray_cat.getJSONObject(i_url).getString("pageid"));  //this can be written in a much better way
                    System.out.println(pageid);
                    String title=jsonarray_cat.getJSONObject(i_url).getString("title");
                    System.out.println(title);                      

                    File food_year= new File("C:/Users/User/Desktop/Root/"+cat_list.get(i_cat).replaceAll(" ", "_").trim()+".txt");
                    File food_year2= new File("C:/Users/User/Desktop/Root/c_"+cat_list.get(i_cat).replaceAll(" ", "_").trim()+".txt");
                    food_year.createNewFile();
                    food_year2.createNewFile();

                    BufferedWriter writer = new BufferedWriter (new OutputStreamWriter(new FileOutputStream(food_year, true)));
                    BufferedWriter writer2 = new BufferedWriter (new OutputStreamWriter(new FileOutputStream(food_year2, true)));               

                    if (title.contains("Category:"))
                    {
                        writer2.write(pageid+";"+title);
                        writer2.newLine();
                        writer2.flush();
                        crawlfile(food_year2);
                    }
                    else
                    {
                        writer.write(pageid+";"+title);
                        writer.newLine();
                        writer.flush();
                    }
                }
            }
        }

}

最佳答案

对于初学者来说,这对维基媒体服务器的要求可能太大了。有超过一百万个类别 ( 1 ),您需要阅读 Wikipedia:Database download - Why not just retrieve data from wikipedia.org at runtime 。您需要将您的使用限制在每秒 1 次左右,否则就有被阻止的风险。这意味着大约需要 11 天才能获得完整的树。

使用https://dumps.wikimedia.org/enwiki/处的标准转储会更好。这些将更容易阅读和处理,并且您不需要给服务器带来很大的负载。

更好的是获得 Wikimedia Labs帐户,它允许您对数据库服务器的复制或转储上的脚本运行查询,而无需下载一些非常大的文件。

<小时/>

要仅获取经济学类别,最简单的方法是通过 https://en.wikipedia.org/wiki/Wikipedia:WikiProject_Economics这有1242类别。您可能会发现使用那里的类别列表并从那里构建树更容易。

这比递归方法更好。维基百科分类系统的问题在于它并不是真正的树,有大量的循环。如果您继续关注类别,您最终将充分利用维基百科,我不会感到惊讶。

关于java - 使用 Java 提取维基百科中的类别树,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37729826/

相关文章:

java - 类初始化死锁机制解释

java - 我有已分配给某些地点的图像,我知道代码正在返回地点,但它没有将它们绘制在 map 上。

java - 在 Android 应用程序的标签中解析 HTML 中的 JSON

css - 搜索引擎如何对待:target?显示的内容

python - 我可以从Google Analytics(分析)API获取网站上所有网址的列表吗?

ruby-on-rails - 没有设置行锁或表锁,怎么会出现死锁呢? (由 rails ActiveRecord#touch 引起)

Java 原始类型提升困惑

java - Java 中通过键连接字符串值的最方便的习惯用法

web-crawler - 如何编写爬虫?

go - 关闭 chan 时出现死锁