微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

从维基百科页面的类别中获取更一般的类别 代码示例结果可能的改进

如何解决从维基百科页面的类别中获取更一般的类别 代码示例结果可能的改进

我正在使用 Python wikipedia library 获取页面类别列表。我看到它是 MediaWiki API 的包装器。

无论如何,我想知道如何将类别概括为 ma​​rco 类别,例如这些 Main topic classifications

例如,如果我搜索页面 Hamburger,有一个名为 German-American cousine 的类别,但我想获得它的超级类别,如 Food and Drink。我该怎么做?

import wikipedia
page = wikipedia.page("Hamburger")
print(page.categories)
# how to filter only imortant categories?
>>>['All articles with specifically marked weasel-worded phrases','All articles with unsourced statements','American sandwiches','Articles with hAudio microformats','Articles with short description','Articles with specifically marked weasel-worded phrases from May 2015','Articles with unsourced statements from May 2017','CS1: Julian–Gregorian uncertainty','Commons category link is on Wikidata','Culture in Hamburg','Fast food','German-American cuisine','German cuisine','German sandwiches','Hamburgers (food)','Hot sandwiches','National dishes','Short description is different from Wikidata','Spoken articles','Use mdy dates from October 2020','Webarchive template wayback links','Wikipedia articles with BNF identifiers','Wikipedia articles with GND identifiers','Wikipedia articles with LCCN identifiers','Wikipedia articles with Nara identifiers','Wikipedia indefinitely move-protected pages','Wikipedia pages semi-protected against vandalism']

我没有找到用于浏览维基百科类别层次树的 api。

我接受 Python 和 API 请求解决方案。谢谢

编辑: 我找到了 api categorytree,它似乎做了一些与我需要的类似的事情。

enter image description here

无论如何,我找不到文档中所说的插入 options 参数的方法。我认为这些选项可以是这个 link 中表达的那些,比如 mode=parents,但我找不到在 HTTP url 中插入这个参数的方法,因为它必须是一个 JSON 对象,如文档中说。我正在尝试这个 https://en.wikipedia.org/w/api.php?action=categorytree&category=Category:Biscuits&format=json。如何插入 options 字段?

解决方法

您可以做的一些不同的事情是使用 machine-predicted article topic 之类的查询获取 https://ores.wikimedia.org/v3/scores/enwiki/?models=articletopic&revids=1000459607

,

这是一项非常艰巨的任务,因为维基百科的类别图一团糟(从技术上讲:-))。实际上,在树中,您希望以对数时间到达根节点。但这不是树,因为任何节点都可以有多个父节点!

此外,我认为它不能仅使用类别来完成,因为正如您在示例中看到的那样,您很可能会得到意想不到的结果。无论如何,我试图重现类似于您所问的内容。

以下代码说明:

  • 从源页面开始(硬编码的页面是“汉堡包”);
  • 返回递归访问所有父类别;
  • 缓存所有遇到的类别,以避免两次访问同一类别(这也解决了循环问题);
  • 找到目标类别就切掉当前分支;
  • 当积压为空时停止。

从给定的页面开始,您可能会获得多个目标类别,因此我将结果组织成一个字典,告诉您某个目标类别遇到了多少次。

正如您想象的那样,响应不是立即的,因此该算法应该在离线模式下实现。它可以通过多种方式改进(见下文)。

代码

import requests
import time
import wikipedia

def get_categories(title) :
    try : return set(wikipedia.page(title,auto_suggest=False).categories)
    except requests.exceptions.ConnectionError :
        time.sleep(10)
        return get_categories(title)

start_page = "Hamburger"
target_categories = {"Academic disciplines","Business","Concepts","Culture","Economy","Education","Energy","Engineering","Entertainment","Entities","Ethics","Events","Food and drink","Geography","Government","Health","History","Human nature","Humanities","Knowledge","Language","Law","Life","Mass media","Mathematics","Military","Music","Nature","Objects","Organizations","People","Philosophy","Policy","Politics","Religion","Science and technology","Society","Sports","Universe","World"}
result_categories = {c:0 for c in target_categories}    # dictionary target category -> number of paths
cached_categories = set()       # monotonically encreasing
backlog = get_categories(start_page)
cached_categories.update(backlog)
while (len(backlog) != 0) :
    print("\nBacklog size: %d" % len(backlog))
    cat = backlog.pop()         # pick a category removing it from backlog
    print("Visiting category: " + cat)
    try:
        for parent in get_categories("Category:" + cat) :
            if parent in target_categories :
                print("Found target category: " + parent)
                result_categories[parent] += 1
            elif parent not in cached_categories :
                backlog.add(parent)
                cached_categories.add(parent)
    except KeyError: pass       # current cat may not have "categories" attribute
result_categories = {k:v for (k,v) in result_categories.items() if v>0} # filter not-found categories
print("\nVisited categories: %d" % len(cached_categories))
print("Result: " + str(result_categories))

示例结果

在您的示例中,脚本将访问 12176 个类别 (!) 并返回以下结果:

{'Education': 21,'Society': 40,'Knowledge': 17,'Entities': 4,'People': 21,'Health': 25,'Mass media': 25,'Philosophy': 17,'Events': 17,'Music': 18,'History': 21,'Sports': 6,'Geography': 18,'Life': 13,'Government': 36,'Food and drink': 12,'Organizations': 16,'Religion': 23,'Language': 15,'Engineering': 7,'Law': 25,'World': 13,'Military': 18,'Science and technology': 8,'Politics': 24,'Business': 15,'Objects': 3,'Entertainment': 15,'Nature': 12,'Ethics': 12,'Culture': 29,'Human nature': 3,'Energy': 13,'Concepts': 7,'Universe': 2,'Academic disciplines': 23,'Humanities': 25,'Policy': 14,'Economy': 17,'Mathematics': 10}

您可能已经注意到,“食品和饮料”类别仅被访问了 12 次,而例如“社会”类别已被访问了 40 次。这告诉我们很多关于维基百科类别图的奇怪之处。

可能的改进

优化或近似此算法有很多改进。我想到的第一个:

  • 考虑跟踪路径长度并假设具有最短路径的目标类别是最相关的。
  • 减少执行时间
    • 您可以通过在第一个目标类别出现后(或在第 N 个出现时)停止脚本来减少步骤数。
    • 如果您从多篇文章开始执行此算法,您可以将最终目标类别与您遇到的每个类别相关联的信息保存在内存中。例如,运行“汉堡包”后,您会知道从“类别:快餐”开始,您将到达“类别:经济”,这可能是一个宝贵的信息。这会占用大量空间,但最终会帮助您减少执行时间。
  • 仅将目标更频繁的类别用作标签。例如。如果您的结果是 {"Food and drinks" : 37,"Economy" : 4},您可能只想保留“食品和饮料”作为标签。为此,您可以:
    • 取 N 个最常出现的目标类别;
    • 取最相关的分数(例如前半部分、第三部分或第四部分);
    • 选取至少出现 N% 次的类别 w.r.t.最常见的;
    • 使用更复杂的统计检验来分析频率的统计显着性。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。