从
enwiki-latest-page.sql
中仅下载非重定向标题
文件enwiki-latest-all-titles-in-ns0
由svick提到包含了"实际页面"和重定向页面,例如:
存在,并且第一个重定向到第二个。
然而,可以在转储列表中找到的文件enwiki-latest-page.sql
似乎包含足够的信息来获取无需重定向的标题。
在Ubuntu 23.04上,我们可以使用类似以下方式将标题提取到titles.txt
文件中:
wget https://dumps.wikimedia.org/enwiki/latest/enwiki-latest-page.sql.gz
gunzip --keep enwiki-latest-page.sql.gz
sed -E '/^ (UNIQUE )?KEY/d;;/^ PRIMARY KEY/{s/,$//}' enwiki-latest-page.sql > enwiki-latest-page-noindex.sql
sudo apt update
sudo apt install mariadb-server
sudo mariadb -e 'create database enwiki'
sudo mariadb enwiki <enwiki-latest-page-noindex.sql
sudo mariadb enwiki -B -N -e 'select page_title from page where page_namespace = 0 and page_is_redirect = 0 order by page_title' > titles.txt
rm enwiki-latest-page.sql.gz enwiki-latest-page.sql enwiki-latest-page-noindex.sql
SQL执行速度非常慢,大约花了17分钟。
截至2023年10月,根据https://en.wikipedia.org/wiki/Wikipedia:Size_of_Wikipedia维基百科有约6.7百万篇文章,而titles.txt文件中也包含了相当数量的行,所以结果看起来是合理的。
我们可以看到enwiki-latest-page.sql
创建了这个表:
CREATE TABLE `page` (
`page_id` int(8) unsigned NOT NULL AUTO_INCREMENT,
`page_namespace` int(11) NOT NULL DEFAULT 0,
`page_title` varbinary(255) NOT NULL DEFAULT '',
`page_is_redirect` tinyint(1) unsigned NOT NULL DEFAULT 0,
`page_is_new` tinyint(1) unsigned NOT NULL DEFAULT 0,
`page_random` double unsigned NOT NULL DEFAULT 0,
`page_touched` binary(14) NOT NULL,
`page_links_updated` varbinary(14) DEFAULT NULL,
`page_latest` int(8) unsigned NOT NULL DEFAULT 0,
`page_len` int(8) unsigned NOT NULL DEFAULT 0,
`page_content_model` varbinary(32) DEFAULT NULL,
`page_lang` varbinary(35) DEFAULT NULL,
PRIMARY KEY (`page_id`),
UNIQUE KEY `page_name_title` (`page_namespace`,`page_title`),
KEY `page_random` (`page_random`),
KEY `page_len` (`page_len`),
KEY `page_redirect_namespace_len` (`page_is_redirect`,`page_namespace`,`page_len`)
) ENGINE=InnoDB AUTO_INCREMENT=74951970 DEFAULT CHARSET=binary ROW_FORMAT=COMPRESSED;
跟着一堆庞大的
INSERT
语句。
正如我们所看到的,这个包含了我们想要的
title
和
is_redirect
字段。这个表是来自MediaWiki软件的一个实际表,文档可以在这里找到:
https://www.mediawiki.org/wiki/Manual:Page_table
另一个令人烦恼的问题是,存在多个索引使得初始导入非常非常慢,所以我们通过使用
sed
编辑表格来删除除了主键索引之外的所有索引,大大加快了导入速度。在删除这些索引后,导入步骤在我的Lenovo ThinkPad P51上花了17分钟。使用这些语句后,5个小时后还没有完成,我放弃了。以下是相关语句:
;
根据我的系统上执行的情况来看,mysqldump生成的/*!xxxxxx语句*/是什么意思?,并且很可能是由mysqldump添加的,以加快插入速度,因为只在插入后更新索引一次会更快。所以看起来索引生成本身在最后非常慢?
关于没有重定向的标题的具体情况已经在这里提问过了:所有维基百科文章的标题没有重定向
一个相关的维基百科导入问题:将维基百科转储导入到MySql
只是再进行一个时间实验,如果我修改它不包含任何索引,甚至没有主键:
echo page
cat <<'EOF' | time mariadb enwiki
DROP TABLE IF EXISTS `page`;
;
;
CREATE TABLE `page` (
`page_id` int(8) unsigned NOT NULL,
`page_namespace` int(11) NOT NULL DEFAULT 0,
`page_title` varbinary(255) NOT NULL DEFAULT '',
`page_is_redirect` tinyint(1) unsigned NOT NULL DEFAULT 0,
`page_is_new` tinyint(1) unsigned NOT NULL DEFAULT 0,
`page_random` double unsigned NOT NULL DEFAULT 0,
`page_touched` binary(14) NOT NULL,
`page_links_updated` varbinary(14) DEFAULT NULL,
`page_latest` int(8) unsigned NOT NULL DEFAULT 0,
`page_len` int(8) unsigned NOT NULL DEFAULT 0,
`page_content_model` varbinary(32) DEFAULT NULL,
`page_lang` varbinary(35) DEFAULT NULL
) ENGINE=InnoDB AUTO_INCREMENT=74951970 DEFAULT CHARSET=binary ROW_FORMAT=COMPRESSED;
;
EOF
echo 'insert'
grep -a -e '^INSERT ' enwiki-latest-page.sql | time mariadb enwiki
echo 'index'
time mariadb enwiki -e 'ALTER TABLE `page` ADD PRIMARY KEY `page_pk` (`page_id`)'
time mariadb enwiki -e 'ALTER TABLE `page` ADD UNIQUE KEY `page_name_title` (`page_namespace`,`page_title`)'
#time mariadb enwiki -e 'ALTER TABLE `page` ADD KEY `page_random` (`page_random`)'
#time mariadb enwiki -e 'ALTER TABLE `page` ADD KEY `page_len` (`page_len`)'
#time mariadb enwiki -e 'ALTER TABLE `page` ADD KEY `page_redirect_namespace_len` (`page_is_redirect`,`page_namespace`,`page_len`)'
echo
给出:
- 插入需要28分钟
- 添加主键需要20分钟
page_name_title
需要7分钟
我对MySQL的速度失去了耐心。
我还尝试手动创建一个SQLite表,并运行未修改的INSERT
语句,但它不起作用,因为MySQL使用\'
SQL扩展来转义单引号,而不是''
。这是标准的...
好的,这里有一个家伙试图用自定义脚本来对抗慢速:使用'mysqldump'以CSV格式导出所有表,位于:https://github.com/jamesmishra/mysqldump-to-csv
sed
如果你真的想要过快节奏的生活,这里有一个尝试:
cat enwiki-latest-page.sql |
grep -E '^INSERT' |
sed 's/),(/\n/g' |
grep -E '^[^,]+,0,' |
perl -lane "s/^[^']+'//;s/(,[^,]+){8}$//; if (substr(\$_, -1) == '0') { print substr(\$_, 0, -3) }" |
sed 's/\\\\/\\/g;s/\\'"'"'/'"'"'/g;s/\\"/"/g' \
> titles-sed.txt
sort -o titles-sed.txt titles-sed.txt
这在我的联想ThinkPad P51上大约花了5分钟,生成了完全相同的文本文件,除了处理了极少数包含反斜杠
\
的标题,例如:
https://en.wikipedia.org/wiki/TBWA%5CChiat%5CDay 由于某种原因,MySQL输出包含双反斜杠。不确定它是硬编码在数据库本身中还是IO问题。查看
enwiki-latest-all-titles-in-ns0
,我发现它包含单个反斜杠的
TBWA/Chiat/Day
,所以这一定是一个简单的IO问题。
后来我了解到,可以通过
awk FPAT
来大大改进这个问题,具体方法如下:
What's the most robust way to efficiently parse CSV using awk?
这是一个关于
sed
的上传链接:
https://archive.org/details/enwiki-latest-all-titles-in-ns0-no-redirects
mysqldump-to-csv
https://dev59.com/5Gct5IYBdhLWcg3wgNnR#28168617指向了
https://github.com/jamesmishra/mysqldump-to-csv,它可以将转储文件半自动地转换为CSV格式,从而绕过MySQL的速度问题。
git clone https://github.com/jamesmishra/mysqldump-to-csv
cd mysqldump-to-csv
git checkout 24301dfa739c13025844ed3ff5a8abe093ced6cc
patch <<'EOF'
diff --git a/mysqldump_to_csv.py b/mysqldump_to_csv.py
index b49cfe7..8d5bb2a 100644
--- a/mysqldump_to_csv.py
+++ b/mysqldump_to_csv.py
@@ -101,7 +101,8 @@ def main():
try:
- for line in fileinput.input():
+ sys.stdin.reconfigure(errors='ignore')
+ for line in fileinput.input(encoding="utf-8", errors="ignore"):
if is_insert(line):
values = get_values(line)
EOF
然后我按照
如何提取CSV文件的一列中的方法添加了csvtool,并使用awk来过滤列:
time zcat enwiki-latest-page.sql.gz | python mysqldump-to-csv/mysqldump_to_csv.py | csvtool format '%(2) %(4) %(3)\n' - | awk '$1==0 && $2==0 {print $3}' > titles-csv.txt
sort -o titles-csv.txt titles-csv.txt
这在我的联想ThinkPad P51上花了5.5分钟,结果与sed方法完全相同,这让人放心。所以这个方法虽然快,但更加简洁,而且能够处理其他具有原始换行符的表格。
获取类别层次内的页面
我对这个相关主题进行了一些探索,参考链接:
Wikipedia Category Hierarchy from dumps*