从 Wiki 中挖掘国与国之间的联系

Wiki是一个众包性质的在线百科全书网站。志愿者们可以在上面编辑自己擅长领域的知识,它的宗旨是"中立的观点"。从某种程度上说,Wiki 和 OpenStreetMap 项目十分类似,只不过OpenStreetMap 项目只是专注于对空间数据的创建和编辑。

正是由于Wiki的众包特点,每个人都可以编辑页面,并且我们可以在页面之间创建链接。在Wiki里,可以通过一个特殊的URL区获得连接到某个页面的所有页面列表,比如,https://en.wikipedia.org/wiki/Special:WhatLinksHere/China 这个页面显示了所有可以链接到中国页面的链接列表。(由于中文维基已经被墙,这里访问的是英文维基)。

能否通过这个特殊的URL来挖掘国与国之间的联系呢?

通常,Wiki中一个国家的页面存在链接到另一国家的页面,说明这两个国家肯定有着一定的联系,比如可能是相邻关系、历史上有渊源、政治、经济或者是军事方面有联系等等。我们可以假设,如果某个国家被链接的次数越多,那么就有越多的国家和它有关系,也说明这个国家也就越重要。当然,这只是笼统地这么一说,国家的重要性是个很宽泛的话题,但是我们不妨先把国与国之间在Wiki上的链接关系找出来并可视化再说。

首先,我们要找到一个全世界所有国家在的列表,然后在Wiki里通过上述的URL去挖掘它们各自的联系。在Wiki里有一个国家ISO代码的页面,我们就从爬取这个页面的所有国家开始。下面的c++代码显示了如何用正则匹配去获得所有国家和地区的ISO代码和名称以及它们在Wiki中的链接地址(完整代码托管在oschina上):

std::string content;
if (!curl_get("https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2#IN", content)) {
    return false;
}
// regex search
std::regex expression("<td id=\"[A-Z]{2}\"><span style=\"[^\"]*\">[A-Z]{2}</span></td>\n<td><a href=\"[^\"]*\" title=\"[^\"]*\">.*</a></td>");

std::sregex_iterator iter(content.cbegin(), content.cend(), expression);
std::sregex_iterator iter_end;
int count = 0;
for (; iter != iter_end; ++iter) {
    std::string searched_string = (*iter)[0].str();
    std::string code, url, name;
    // country code
    code = searched_string.substr(8, 2);
    if (!std::isupper(code[0]) || !std::isupper(code[1])) {
        std::cout << "code: " << code << " is invalid!\n";
    }
    // country url
    std::regex url_expresion("href=\"[^\"]*\" ");
    std::smatch match;
    if (std::regex_search (searched_string, match, url_expresion)) {
        std::string s = match[0].str();
        url = s.substr(6, s.length() - 8);
    }
    // country name
    std::regex name_expresion("title=\"[^\"]*\"");
    if (std::regex_search (searched_string, match, name_expresion)) {
        std::string s = match[0].str();
        name = s.substr(7, s.length() - 8);
    }
    // construct country
    Country country(code, name, url);
    this->countries_.push_back(country);
    this->url_countries_.emplace(std::make_pair(url, count++));
}

获得国家的列表后,便可以依次寻找每个国家被链接的页面列表:

for (size_t i = 0; i < countries_.size(); ++i) {
    Country& country = countries_[i];

    // get page with maximal links list
    std::string links_url = "https://en.wikipedia.org/wiki/Special:WhatLinksHere/" + country.get_url().substr(5);
    std::string links_page;
    if (!curl_get(links_url, links_page)) {
        continue;
    }
        
    // regex search country page
    std::regex expression("<li><a href=\"[^\"]*\" title=\"[^\"]*\">.*</a>  ‎ <span class=");
    std::sregex_iterator iter(links_page.cbegin(), links_page.cend(), expression);
    std::sregex_iterator iter_end;
    for (; iter != iter_end; ++iter) {
        std::string searched_string = (*iter)[0].str();
        std::regex url_expresion("href=\"[^\"]*\" ");
        std::smatch match;
        if (std::regex_search(searched_string, match, url_expresion)) {
            std::string s = match[0].str();
            std::string href = s.substr(6, s.length() - 8);
            int index = this->Search(href, URL);
            if (index != -1) {
                count++;
                country.AddLink(index);
            }
        }
    }
}

如果您对程序感兴趣,下载oschina上的代码编译运行,可以得到包含国与国之间联系的shapefile格式的文件,如果您不想运行程序,可以从百度云盘直接下载程序的输出文件。最后,借助 GeoHey 的数据上图,我们可以做出下面的地图出来:

单独看看在Wiki上可以链接到中国的国家:

从上图可以看出,除了周边的国家外,能够链接到中国的其他国家的地理分布还是比较均匀的,从这里可以体现出我国外交所作出的努力。另外,从太平洋的一系列能链接到中国的岛国的位置,依稀可见中国走出太平洋的地理路线。

为了更直观地看每个国家的链接数,采用气泡图的可视化方式:

从图中不难发觉,北美洲、西欧以及一些地理位置优越的岛国被链接次数较多。虽然可以链接到中国的国家不少(123个),但是这个数字基本属于中等偏上一点点。连接数前四甲分别是法国(197),加拿大(193),美国(189)以及英国(183)。

分析到这里,大家可能会怀疑中国的被链接次数为何会如此低。这里面有一个原因是我们访问的是英文的wiki,这些页面基本上都是英语国家创建和编辑的,如果是中文的wiki的话可能会有更多的链接指向中国。但是从另一方面来说,这能使我们摒除自己的偏见,更客观地认识中国与世界的其它国家的联系。

欢迎从GeoHey获取地理和位置相关的数据、知识、服务

访问网站 http://geohey.com

联系我们 contact@geohey.com

长按关注公众号