python - 从维基百科页面的类别中获取更一般的类别

标签 python mediawiki wikipedia wikipedia-api mediawiki-api

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

无论如何,我想知道如何将类别归纳为宏类别,例如这些Main topic classifications .

例如,如果我搜索页面 Hamburger有一个类别叫做 German-American cousine , 但我想得到它的 super 类别 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字段?

最佳答案

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

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

下面代码的解释:

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

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

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

代码

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},您可能只想保留“Food and drinks”作为标签。为此,您可以:
    • 选取 N 个最常出现的目标类别;
    • 取最相关的分数(例如上半部分、第三部分或第四部分);
    • 选择至少出现 N% 次的类别 w.r.t.最常见的;
    • 使用更复杂的统计测试来分析频率的统计显着性。

关于python - 从维基百科页面的类别中获取更一般的类别,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65791801/

相关文章:

php - MediaWiki 的 session 设置在哪里?

ssh - SSH key 的错误密码(MediaWiki 的 Gerrit 教程)

r - 可以从 knitr 生成 MediaWiki 标记吗?

php - 维基百科使用哪种维基标记解析器?

objective-c - 将维基百科页面部分转换为 NSString Objective-C

css - 为维基百科制作侧边栏

python - 如何使用 beautifulsoup 在 span 标签之间进行抓取

Python:cTurtle 模块未加载

python - ROS:变压器总是空的

python - 线程化 Python 应用程序对 Ctrl+C 没有反应