我们把信息放在无法取出的地方。
import wtf from 'wtf_wikipedia'
let doc = await wtf.fetch('Toronto Raptors')
let coach = doc.infobox().get('coach')
coach.text() //'Darko Rajaković'
.text()
获取清洁的纯文本:
let str = `[[Greater_Boston|Boston]]'s [[Fenway_Park|baseball field]] has a {{convert|37|ft}} wall. <ref>Field of our Fathers: By Richard Johnson</ref>`
wtf(str).text()
// "Boston's baseball field has a 37ft wall."
let doc = await wtf.fetch('Glastonbury', 'en')
doc.sentences()[0].text()
// 'Glastonbury是英格兰萨默塞特郡的一个小镇和民政教区,位于一个干燥点...'
.json()
获取页面的所有数据:
let doc = await wtf.fetch('Whistling')
doc.json()
// { categories: ['口头交流', '声音技巧'], sections: [{ title: '技巧' }], ...}
默认的 .json() 输出是_非常详细的_,但你可以像这样挑选数据:
// 只获取链接:
doc.links().map((link) => link.json())
//[{ page: '戏剧迷信', text: '迷信' }]
// 只获取图片:
doc.images()[0].json()
// { file: 'Image:Duveneck Whistling Boy.jpg', url: 'https://commons.wiki...' }
// 特定章节的json:
doc.section('另见').links()[0].json()
// { page: '滑哨' }
在客户端运行:
<script src="https://unpkg.com/wtf_wikipedia"></script>
<script>
wtf.fetch('Radiohead', { 'Api-User-Agent': '在此处命名你的脚本' }, function (err, doc) {
let members = doc.infobox().get('current members')
members.links().map((l) => l.page())
//['Thom Yorke', 'Jonny Greenwood', 'Colin Greenwood'...]
})
</script>
或在服务器端:
import wtf from 'wtf_wikipedia'
// 或者,
const wtf = require('wtf_wikipedia')
完整的维基百科转储
使用这个库,结合 dumpster-dive,你可以在一个下午内解析整个英文维基百科。
npm install -g dumpster-dive
好的,首先 🛀
维基文本 并非小事。
考虑以下几点:
- 内联 CSS 的部分实现
- 嵌套元素不遵守其他元素的作用域
- 该语言没有错误
- 相似语法模板的深度递归
- 埃及象形文字语法
- 'Birth_date_and_age' 对比 'Birth-date_and_age'
- 图像路径中未解释的哈希方案
- 空格和标点符号的自定义编码
- 从左到右模板中的从右到左值
- 基于 PEG 的解析器难以处理维基文本的回溯/前瞻
- 英文维基百科中有 634,755 个模板(截至 2018 年 11 月)
- 有大量页面在维基百科或其应用程序上无法正确渲染..
这个库支持许多递归技巧、已弃用和晦涩的模板变体,以及非法的维基简写。
它能做什么:
- 检测并解析重定向和消歧义页面
- 将信息框解析为格式化的键值对象
- 处理递归模板和链接 - 如 [[.. [[...]] ]]
- 逐句纯文本和链接解析
- 解析和格式化内部链接
- 从 File:XYZ.png 文件名创建图像缩略图 URL
- 正确解析动态模板,如 {{CURRENTMONTH}} 和 {{CONVERT ..}}
- 解析图像、标题和分类
- 将"DMS 格式"的(59°12'7.7"N)地理坐标转换为经纬度
- 解析并合并引用和参考元数据
- 消除 xml、latex、css 和表格排序垃圾内容
它不能做什么:
- 外部"嵌入"页面数据 [1]
- AST 输出
- 信息框或图库中 HTML 的智能(或"美化")格式化 [1]
- 维护完美的页面顺序 [1]
- 逐句参考(而是按"章节"元素)
- 维护模板或信息框的 CSS 样式
- 跨不同章节的大型表格 [1]
它的构建尽可能灵活。在所有情况下,都尝试以周到的方式处理失败。
HTML 抓取如何?
维基媒体的官方解析器将维基文本转换为 HTML。
如果你更喜欢这种屏幕抓取工作流程,你可以像这样提取页面的各个部分。
这很酷!
但通过这种方式获取结构化数据仍然是一个复杂、奇怪的过程。 手动_探索_ HTML 有时与扫描维基文本本身一样棘手和容易出错。
这个库的贡献者们已经得出这个结论,正如许多其他人一样。
这个库对 Parsoid 的贡献者表示感谢。
好的,
将你的维基文本转换为 Doc 对象
import wtf from 'wtf_wikipedia'
let txt = `
==木材在流行文化中==
* 哈利·波特的魔杖
* 辛普森一家的栅栏
`
wtf(txt)
// Document {text(), json(), lists()...}
doc.links()
let txt = `口哨在许多电视节目中都有出现,例如 [[Lassie (1954 TV series)|''莱西'']],以及《[[The X-Files|X档案]]》的片头主题。`
wtf(txt)
.links()
.map((l) => l.page())
// [ 'Lassie (1954 TV series)', 'The X-Files' ]
doc.text()
返回文章的纯文本格式
let txt =
"[[Greater_Boston|波士顿]]的[[Fenway_Park|棒球场]]有一面 {{convert|37|ft}} 高的墙。<ref>{{cite web|blah}}</ref>"
wtf(txt).text()
//"波士顿的棒球场有一面 37 英尺高的墙。"
doc.sections():
章节是指 '==像这样==' 的标题
wtf(page).sections()[1].children() // 遍历嵌套的章节
wtf(page).section('see also').remove() // 删除一个章节
doc.sentences()
let s = wtf(page).sentences()[4]
s.links()
s.bolds()
s.italics()
s.text()
s.wikitext()
doc.categories()
await wtf.fetch('Whistling').categories()
//['口头交流', '声乐', '声音技巧']
doc.images()
let img = wtf(page).images()[0]
img.url() // 完整大小的维基媒体托管的URL
img.thumbnail() // 默认300px
img.format() // jpg, png, ..
获取
你可以从任何维基API抓取和解析文章。这包括任何语言、任何维基项目,以及大多数第三方维基。
// 第三方维基
let doc = await wtf.fetch('https://muppet.fandom.com/wiki/Miss_Piggy')
// 法语维基百科
doc = await wtf.fetch('Tony Hawk', 'fr')
doc.sentence().text() // 'Tony Hawk是一名职业滑板运动员和演员...'
// 接受数组或维基媒体页面ID
let docs = wtf.fetch(['Whistling', 2983], { follow_redirects: false })
// 德语维基导游中的文章
wtf.fetch('Toronto', { lang: 'de', wiki: 'wikivoyage' }).then((doc) => {
console.log(doc.sentences()[0].text()) // 'Toronto是安大略省的首府'
})
你也可以使用维基百科页面ID作为参数,而不是页面标题:
let doc = await wtf.fetch(64646, 'de')
fetch方法会跟随重定向。
API插件
wtf.getCategoryPages(title, [options])
获取属于给定分类的所有页面和子分类:
wtf.extend(require('wtf-plugin-api'))
let result = await wtf.getCategoryPages('Category:Politicians_from_Paris')
/*
{
[
{"pageid":52502362,"ns":0,"title":"William Abitbol"},
{"pageid":50101413,"ns":0,"title":"Marie-Joseph Charles des Acres de L'Aigle"}
...
{"pageid":62721979,"ns":14,"title":"Category:Councillors of Paris"},
{"pageid":856891,"ns":14,"title":"Category:Mayors of Paris"}
]
}
*/
wtf.random([options])
从给定语言或域名获取随机的维基百科文章
wtf.extend(require('wtf-plugin-api'))
wtf.random().then((doc) => {
console.log(doc.title(), doc.categories())
//'Whistling' ['口头交流', '声音技巧']
})
教程
插件
这些插件添加了各种新功能:
wtf.extend(require('wtf-plugin-classify'))
await wtf.fetch('Toronto Raptors').classify()
// 'Organization/SportsTeam'
wtf.extend(require('wtf-plugin-summary'))
await wtf.fetch('Pulp Fiction').summary()
// '一部1994年美国犯罪片'
wtf.extend(require('wtf-plugin-person'))
await wtf.fetch('David Bowie').birthDate()
// {year:1947, date:8, month:1}
wtf.extend(require('wtf-plugin-i18n'))
await wtf.fetch('Ziggy Stardust', 'fr').infobox().json()
// {nom:{text:"Ziggy Stardust"}, oeuvre:{text:"The Rise and Fall of Ziggy Stardust"}}
插件 | |
---|---|
classify | 人/地点/事物 |
summary | 简短描述文本 |
person | 出生/死亡信息 |
api | 从API获取更多数据 |
i18n | 改进多语言模板覆盖 |
wtf-mlb | 获取棒球数据 |
wtf-nhl | 获取冰球数据 |
nsfw | 标记色情/图形/成人文章 |
image | .images() 的附加方法 |
html | 输出html |
wikitext | 输出wikitext |
markdown | 输出markdown |
latex | 输出latex |
良好实践:
维基百科API相当欢迎使用,但如果你要大量使用,建议遵循以下三点 -
- 传递一个
Api-User-Agent
作为某种标识,以便他们可以轻松限制不良脚本 - 将多个页面打包成一个请求(比如,每组5个?)
- 串行运行,或至少,缓慢运行。
wtf
.fetch(['Royal Cinema', 'Aldous Huxley'], {
lang: 'en',
'Api-User-Agent': 'spencermountain@gmail.com',
})
.then((docList) => {
let links = docList.map((doc) => doc.links())
console.log(links)
})
完整API
- .title() - 获取/设置页面的标题(从第一句话中获取)
- .pageID() - 获取/设置页面的维基媒体ID(如果有的话)
- .wikidata() - 获取/设置页面的维基数据ID(如果有的话)
- .domain() - 获取/设置我们所在的维基域名(如果有的话)
- .url() - (尝试)生成当前文章的URL
- .lang() - 获取/设置当前语言(用于URL方法)
- .namespace() - 获取/设置页面的维基媒体命名空间(如果有的话)
- .isRedirect() - 判断页面是否只是重定向到另一个页面
- .redirectTo() - 此页面重定向到的目标页面
- .isDisambiguation() - 判断是否为引导你到多个可能页面之一的占位页面
- .isStub() - 判断页面是否被标记为不完整
- .categories() - 返回文档的所有分类
- .sections() - 返回文档的章节列表
- .paragraphs() - 返回所有章节中的段落列表
- .sentences() - 返回文档中的所有句子列表
- .images() - 返回文档中找到的所有图片
- .links() - 返回文档所有部分中的所有链接列表
- .lists() - 页面中每行以项目符号开头的章节
- .tables() - 返回文档中所有结构化表格的列表
- .templates() - 任何类型的结构化数据元素,通常包裹在{{这样}}中
- .infoboxes() - 特定类型的模板,通常出现在页面右上角
- .references() - 返回文档中"引用"的列表
- .coordinates() - 页面上出现的地理位置
- .text() - 页面的纯文本、人类可读输出
- .json() - 页面主要数据的"可字符串化"输出
- .wikitext() - 原始维基标记
- .description() - 获取/设置页面的简短描述(如果有的话)
- .pageImage() - 获取/设置页面的代表性图片(如果有的话)
- .revisionID() - 获取/设置页面的最新编辑ID(如果有的话)
- .timestamp() - 获取/设置页面最近编辑的时间(如果有的话)
章节
- .title() - 章节名称,位于==这些标签==之间
- .index() - 此章节在整个文档中的序号
- .indentation() - 在目录中的深度层级
- .sentences() - 返回此章节中的句子列表
- .paragraphs() - 返回此章节中的段落列表
- .links() - 所有段落和模板中的所有链接列表
- .tables() - 所有HTML表格列表
- .templates() - 此章节中的所有模板列表
- .infoboxes() - 此章节中找到的所有信息框列表
- .coordinates() - 此章节中找到的所有坐标模板列表
- .lists() - 此章节中的所有列表
- .interwiki() - 指向其他语言维基的任何链接
- .images() - 返回此章节中任何图片的列表
- .references() - 返回此章节中"引用"的列表
- .remove() - 从文档中移除当前章节
- .nextSibling() - 当前父级下的下一个章节:例如,1920年代 → 1930年代
- .lastSibling() - 当前父级下的上一个章节:例如,1930年代 → 1920年代
- .children() - 比当前章节更具体的任何章节:例如,历史 → [史前时期, 1920年代, 1930年代]
- .parent() - 比当前章节更广泛的章节:例如,1920年代 → 历史
- .text() - 此章节的可读纯文本
- .json() - 返回所有章节数据
- .wikitext() - 原始维基标记
段落
- .sentences() - 返回此段落中的句子对象列表
- .references() - 所有句子中的任何引用或参考
- .lists() - 此段落中找到的任何列表
- .images() - 此段落中找到的任何图片
- .links() - 所有句子中的所有链接列表
- .interwiki() - 指向其他语言维基的任何链接
- .text() - 生成此段落的可读纯文本
- .json() - 以JSON格式生成此段落的一些通用数据
- .wikitext() - 原始维基标记
句子
- .links() - 所有链接列表
- .bolds() - 所有粗体文本列表
- .italics() - 所有斜体格式文本列表
- .text() - 生成可读纯文本
- .json() - 返回所有句子数据
- .wikitext() - 原始维基标记
图片
- .url() - 返回全尺寸图片的URL
- .thumbnail() - 返回缩略图URL(传入
size
参数可自定义大小) - .links() - 来自图片说明的任何链接(如果存在)
- .format() - 获取文件格式(例如
jpg
) - .text() - 无操作
- .json() - 返回此图片的一些通用元数据
- .wikitext() - 原始维基标记
模板
- .text() - 此模板是否生成任何可读的纯文本?
- .json() - 获取此模板的所有数据
- .wikitext() - 原始维基标记
信息框
- .links() - 此信息框中的任何内部或外部链接
- .keyValue() - 从此信息框生成简单的键值对字符串
- .image() - 从此信息框获取主图片
- .get() - 通过键查找属性
- .template() - 使用的信息框类型,例如"Infobox Person"
- .text() - 为此信息框生成可读纯文本
- .json() - 为此信息框生成一些通用的"可字符串化"数据
- .wikitext() - 原始维基标记
列表
- .lines() - 获取列表中每个成员的数组
- .links() - 获取此列表中提到的所有链接
- .text() - 为此列表生成可读纯文本
- .json() - 为此列表生成一些通用的易解析数据
- .wikitext() - 原始维基标记
参考
- .title() - 为此参考生成面向人类的文本
- .links() - 获取此参考中提到的任何链接
- .text() - 不返回任何内容
- .json() - 为此参考生成一些通用元数据
- .wikitext() - 原始维基标记
表格
- .links() - 获取此表格中提到的任何链接
- .keyValue() - 为此表格生成简单的键值对对象列表
- .text() - 不返回任何内容
- .json() - 为此表格生成一些有用的元数据
- .wikitext() - 原始维基标记
配置
添加新方法:
你可以使用wtf.extend()
向库中的任何类添加新方法
wtf.extend((models) => {
// 将此方法添加进去...
models.Doc.prototype.isPerson = function () {
return this.categories().find((cat) => cat.match(/people/))
}
})
await wtf.fetch('Stephen Harper').isPerson()
添加新模板:
你的维基使用{{foo}}
模板吗?为它添加一个自定义解析器:
wtf.extend((models, templates) => {
// 创建一个自定义解析函数
templates.foo = (tmpl, list, parse) => {
let obj = parse(tmpl) //或者使用自定义正则表达式
list.push(obj)
return 'new-text'
}
// 数组语法允许轻松标记参数
templates.foo = ['a', 'b', 'c']
// 数字语法用于按参数编号返回 '{{name|zero|one|two}}'
templates.baz = 0
// 用字符串替换模板 '{{asterisk}}' -> '*'
templates.asterisk = '*'
})
默认情况下,如果没有模板的解析器,它将被忽略并生成一个空字符串。 但是,可以配置一个后备解析函数来处理这些模板:
wtf('some {{weird_template}} here', {
templateFallbackFn: (tmpl, list, parse) => {
let obj = parse(tmpl) //或者使用自定义正则表达式
list.push(obj)
return '[unsupported template]' // 或返回null以忽略此模板
},
})
你可以使用第三个参数确定哪些模板被理解为"信息框":
wtf.extend((models, templates, infoboxes) => {
Object.assign(infoboxes, { person: true, place: true, thing: true })
})
注意事项:
第三方维基
默认情况下,已安装的MediaWiki应用程序提供公共API。 这意味着大多数维基都有一个开放的API,即使他们没有意识到这一点。一些维基可能会关闭这个功能。
通常可以通过访问http://mywiki.com/api.php
找到它
要从第三方维基获取页面:
wtf.fetch('Kermit', { domain: 'muppet.fandom.com' }).then((doc) => {
console.log(doc.text())
})
一些维基会将其API的路径从./api.php
更改为其他位置。如果你的API有不同的路径,你可以这样设置:
wtf.fetch('2016-06-04_-_J.Fernandes_@_FIL,_Lisbon', { domain: 'www.mixesdb.com', path: 'db/api.php' }).then((doc) => {
console.log(doc.template('player').json())
})
为了使图像URL正常工作,维基还应启用Special:Redirect
。
一些维基(如Wikia)有意禁用了这个功能。
国际化和多语言:
wikitext(令人惊讶地)在所有语言、维基,甚至在从右到左的语言中都被使用。 这个解析器在处理这方面也做得相当不错。
库中包含了Wikipedia I18n语言信息,涵盖_重定向、信息框、分类和图像_,覆盖范围相当广泛。
要改善I18n模板的覆盖范围,请使用wtf-plugin-i18n
如果你发现你的语言缺少某些内容,请提交PR。
构建:
该库提供独立的客户端和服务器端构建,以保持文件大小。
-
./wtf_wikipedia-client.mjs - 作为ES模块(或Deno)
-
./wtf_wikipedia-client.min.js - 用于生产环境
-
./wtf_wikipedia.cjs - Node CommonJS构建
-
./wtf_wikipedia.mjs - Node/Deno/TypeScript ESM构建
浏览器版本使用fetch()
,而服务器版本使用require('https')
。
性能:
它不是最快的解析器,也不太可能在速度上超过C或Java中的单程解析器。
使用dumpster-dive,这个库可以在MacBook上用约4小时解析完整的英文Wikipedia。
这大约是每线程每秒100页。
另请参阅:
其他替代JavaScript解析器:
以及更多!
MIT