最近在处理业务上的某些事情时,会发现这样的问题:为了调试某个程序,会 dump 出一堆中间文件的 log,去查找哪些地方发生了异常。这种文件都是 tsv 文件,即使用 TAB 分割的列表文件。一种最简单的做法就是在文本编辑器中打开这些文件,然后通过观察去查看异常,可以配合编辑器的搜索功能。在这方面 sublime text 最为适合,因为只有这个能打开大文件。但是,这样每一次打开都需要很长的时间,而且我去搜索某个字符串的时候,每输入一个字符编辑器就会停顿很长时间。

于是我希望能有这么一种东西,对文本文件这种没有被某种结构化工具存储的、没有明确定义 schema 的东西进行快速的查找、计算。简单地借用 SQL 中的术语,我希望能在不导入数据的情况下进行 SELECTWHEREORDERLIMITJOIN 等基本功能。幸运的是,最近发现了以前打印出来一直都没看的讲义,一些最基本的 Unix command 就基本上涵盖了我所希望的对文本文件处理的功能。本文就按照这些 SQL 功能语句把这些命令进行梳理。

本文进行处理的数据对象是在 2 月用 Bangumi_Spider 爬取的 Bangumi Data,这包括用户、条目和收藏记录:

1
2
3
head user-2017-02-17T12_26_12-2017-02-19T06_06_44.tsv -n 5
head record-2017-02-20T14_03_27-2017-02-24T10_57_16.tsv -n 5
head subject-2017-02-26T00_28_51-2017-02-27T02_15_34.tsv -n 5
uid    name    nickname    joindate    activedate
7    7    lorien.    2008-07-14    2010-06-05
2    2    陈永仁    2008-07-14    2017-02-17
8    8    堂堂    2008-07-14    2008-07-14
9    9    lxl711    2008-07-14    2008-07-14
name    iid    typ    state    adddate    rate    tags
2    189708    real    dropped    2016-10-06        
2    76371    real    dropped    2015-11-07        
2    119224    real    dropped    2015-03-04        
2    100734    real    dropped    2014-10-09        
subjectid    authenticid    subjectname    subjecttype    rank    date    votenum    favnum    tags
1    1    第一次的親密接觸    book    1069    1999-11-01    57    [7, 84, 0, 3, 2]    小説:1;NN:1;1999:1;国:1;台湾:4;网络:2;三次元:5;轻舞飞扬:9;国产:2;爱情:9;经典:5;少女系:1;蔡智恒:8;小说:5;痞子蔡:20;书籍:1
2    2    坟场    music    272        421    [108, 538, 50, 18, 20]    陈老师:1;银魂:1;冷泉夜月:1;中配:1;银魂中配:1;治愈系:1;银他妈:1;神还原:1;恶搞:1;陈绮贞:9
4    4    合金弹头7    game    2396    2008-07-17    120    [14, 164, 6, 3, 2]    STG:1;结束:1;暴力:1;动作:1;SNK:10;汉化:1;2008:1;六星:1;合金弹头:26;ACT:10;NDS:38;Metal_Slug_7:6;诚意不足:2;移植:2
6    6    军团要塞2    game    895    2007-10-10    107    [15, 108, 23, 9, 7]    抓好社会主义精神文明建设:3;团队要塞:3;帽子:5;出门杀:1;半条命2:5;Valve:31;PC:13;军团要塞:7;军团要塞2:24;FPS:26;经典:6;tf:1;枪枪枪:4;2007:2;STEAM:25;TF2:15

由于爬虫的性质,这些数据有以下缺陷:

  1. 非实时。我所说的“实时”并不是今天是 4 月 16 日而数据只是 2 月的,而是我无法保证数据是在某一个时间点上的快照。对于用户数据,由于爬取一次需要两天的时间,在这两天的时间里,可能用户修改了他们的昵称和用户名而在爬取的数据上未反映出来。更为严重的问题是,对于收藏数据,可能会出现在爬取数据的时候用户进行了收藏的操作,导致爬取的数据出现重复或缺失。而且由于用户数据和收藏数据是分开爬取的,我无法保证通过用户名能把两个 table 一一对应地 join 起来。
  2. 非顺序。可以从预览的数据中看到。
  3. 爬虫本身缺陷。由于我对于 Bangumi 出现 500 错误没有在处理上体现出来,所以会导致某些数据有所缺失。

在下面的文章里,我们将一边使用 Unix Command 对数据进行类似于 SQL 语句的操作,一边阐述 Bangumi_Spider 产生的 data 的各种特点和后续处理需要注意的问题。

1. SELECT … WHERE … ORDER BY …

筛选 2017 冬季番组

现在我们有了条目数据, 而条目数据是记录了标签信息的,我们可以从标签信息中抽取出 2017 年冬季番组。这个标签是“2017 年 1 月”。我们可以用一个 grep 语句取出这些番组:

1
grep "2017年1月" subject-2017-02-26T00_28_51-2017-02-27T02_15_34.tsv | grep "anime" | wc -l
90

然而这个抽取方式有很大的缺陷。我们没有指定应该在数据的哪一列上查找“anime”或“2017 年 1 月”!如果有一部音乐条目的名字里面就有 anime 这个词,而又被打上了 2017 年 1 月的标签呢?这显然不是我们希望得到的。实际上,需要指定列的话,最好的方式就是使用 awk

1
2
3
awk -F "\t" '$9 ~ /[;\t]2017年1月:/ && $4=="anime"' subject-2017-02-26T00_28_51-2017-02-27T02_15_34.tsv > anime_selection.tsv
wc -l anime_selection.tsv
head anime_selection.tsv -n 5
85 anime_selection.tsv
122772    122772    六心公主    anime        2016-12-30    26    [19, 41, 1, 1, 4]    17冬:1;原创:1;PONCOTAN:4;2016年:2;广桥凉:1;TVSP:1;池赖宏:1;原优子:1;mebae:1;TV:4;日本动画:1;片山慎三:1;Studio:1;STUDIOPONCOTAN:4;2016:5;TVA:1;短片:2;上田繁:1;搞笑:4;中川大地:2;岛津裕之:2;种崎敦美:1;2017年1月:1;テレビアニメ:1;オリジナル:1;SP:1;6HP:2;村上隆:10;未确定:1
125900    125900    锁链战记~赫克瑟塔斯之光~    anime    3065    2017-01-07    88    [66, 24, 216, 20, 60]    山下大辉:3;17冬:1;原创:1;游戏改:47;CC:1;花泽香菜:7;TV:22;未确定:2;グラフィニカ:2;佐仓绫音:4;2017年1月:61;锁链战记:1;2017:10;锁链战记~Haecceitas的闪光~:15;热血:2;チェインクロ:1;石田彰:22;声优:2;2017年:4;Telecom_Animation_Film:1;十文字:1;柳田淳一:1;战斗:2;内田真礼:2;剧场版:1;奇幻:17;2017·01·07:1;工藤昌史:3;2015年10月:1;TelecomAnimationFilm:9
126185    126185    POPIN Q    anime        2016-12-23    10    [134, 11, 3, 3, 0]    荒井修子:1;黒星紅白:4;原创:3;黑星红白:1;2016年:5;_Q:1;日本动画:1;2016年12月:2;未确定:1;小泽亚李:1;2017:2;2016:5;动画电影:1;2017年:5;Q:3;东映动画:1;种崎敦美:1;2017年1月:1;宫原直树:1;POPIN:6;東映アニメーション:12;剧场版:24;东映:4;萌系画风:1;濑户麻沙美:5
129805    129805    混沌子    anime    2910    2017-01-11    197    [264, 24, 764, 60, 67]    上坂すみれ:3;ブリドカットセーラ恵美:2;季番:2;黑暗推理向慎入:2;2016年:3;CHAOS:5;游戏改:67;SILVER_LINK.:2;悬疑:5;游戏:9;TV:73;未确定:9;伪:4;GAL改:106;2017:28;科幻:2;志倉千代丸:4;SILVERLINK.:34;SLIVERLINK.:70;2017年:10;志仓千代丸:4;2017年1月:170;5pb.:112;混沌子:3;反乌托邦:16;剧透禁止:27;极权主义世界:8;松冈祯丞:3;神保昌登:6;CHILD:5
131901    131901    神怒之日    anime        2017-10-01    0    [79, 1, 0, 3, 1]    GENCO:3;2017年10月:2;TV:4;未确定:2;2017年:2;GAL改:4;游戏改:4;LIGHT:2;2017:3;エロゲ改:3;2017年1月:1

可以看到,我们提升了一些 precision。awk 的思想就是把一个文本文件按行处理,然后在单引号里面对行进行编程。你可以看到,我们使用内部变量 \$9 指定第 9 列的内容(你可以看到,是从 1 开始标号的),这一列是标签。我们使用正则表达式对 \$9 进行匹配,正则表达式需要用斜杠包围。同时,我们指定第四列的内容是 anime。这样就可以将满足我们需求的条目筛选出来。顺便说一句,\$0 是整行的内容。

按照在看人数排序

我们在条目中用一个数组记录了想看、看过、在看、搁置和抛弃的人数。这个数组用方括号所包围。我现在希望对我刚才抽取的冬季番组按照在看人数从大到小排序。这需要我们抽取出第八列,然后从该列中抽取出在看人数。使用以下命令可以达到我的目的:

1
awk -F "\t" '{match($8, /\[([0-9]+), ([0-9]+), ([0-9]+), ([0-9]+), ([0-9]+)\]/, m); printf("%d\t%s\t%s\t%d\t%d\t%d\t%d\t%d\n", $1, $3, $4, m[1], m[2], m[3], m[4], m[5])}' < anime_selection.tsv | sort -t$'\t' -k6,6 -nr | sed 5q
179949    小林家的龙女仆    anime    157    84    2065    19    46
185792    小魔女学园    anime    245    51    1471    30    29
174043    为美好的世界献上祝福! 第二季    anime    297    63    1431    21    28
174143    人渣的本愿    anime    271    51    1381    60    133
188091    珈百璃的堕落    anime    126    46    1366    22    73

按照管道切割,第一个语句还是使用 awk,但是执行的功能不是筛选,而是构造新表。在这个构造新表的过程中,观看人数的抽取使用了 match 函数。被抽取的对象存储在变量 m 里,是 match 的第三个变量。第一个变量是目标匹配字符串,第二个变量是正则表达式。注意需要将匹配的对象用圆括号括起来。

awk 的函数有很多,具体的可以直接参考 awk 手册(我估计没人会看的)或是这里。很多都是 C 语言的内置函数。

对于排序功能我们需要调用 sort 命令。首先需要指定输入文件的分隔符(注意这里有点 hacky,必须是 -t$'\t')。由于我们希望对在看人数从大到小排序,我们必须指定按照第 6 列排序,即 -k6,6。同时我们指定 -nr 表明视第六列为数字并倒序。

结果显示了在二月某个时刻收看番组的前五个。Very reasonable。

抽取标签列表

既然我们已经爬取了标签,能不能对爬取的标签列表进行展开?这时候要使用 awksplit 函数和 array 类型了:

1
awk -F "\t" '{split($9, tags, ";");for(i in tags){ split(tags[i], itm, ":"); printf("%d\t%s\t%s\t%d\n", $1, $3, itm[1], itm[2]);};}' < anime_selection.tsv | sort -t$'\t' -k1,1n -k4,4nr | head -n 20
122772    六心公主    村上隆    10
122772    六心公主    2016    5
122772    六心公主    PONCOTAN    4
122772    六心公主    STUDIOPONCOTAN    4
122772    六心公主    TV    4
122772    六心公主    搞笑    4
122772    六心公主    2016年    2
122772    六心公主    6HP    2
122772    六心公主    中川大地    2
122772    六心公主    岛津裕之    2
122772    六心公主    短片    2
122772    六心公主    テレビアニメ    1
122772    六心公主    オリジナル    1
122772    六心公主    17冬    1
122772    六心公主    2017年1月    1
122772    六心公主    mebae    1
122772    六心公主    SP    1
122772    六心公主    Studio    1
122772    六心公主    TVA    1
122772    六心公主    TVSP    1
sort: write failed: standard output: Broken pipe
sort: write error

由于标签列表是按照分号分隔的,我们首先使用 split($9, tags, ";") 把分割后的字符串存储在 array tags 里。接着在 for(i in tags) 里,i 实际上是 index,对于每一个 tag 我们再次使用 split,得到具体的 tag 和 tag 人数。可以看到,可以使用 C 语言的方式写 awk 的行处理逻辑。在写的时候是可以隔行的,虽然我都写在了一行。

在此之后,我们首先对条目的 id 排序,再在条目中对 tag 的标记人数排序。这里 sort 需要使用两个 -k 选项指定排序顺序。同时我们把 -nr 的条件写在每个排序列的后面,这样可以对列按照不同的排序逻辑排序。

2. Aggregation

计数

我们更为关心的是收藏数据中的一些统计数据,如平均分、活跃人数等。最基本的统计数据形式就是计数。虽然可以使用 awk 完成,但是这个比较特殊,有更为简单的方法。

比如说,我们想在上文抽取出来的冬季番组标签中统计每个番组会有多少标签。鉴于我们已经把标签对每个动画展开了,我们就可以对每个动画出现的次数进行统计,就可以得到该动画对应的标签数量:

1
cut -f1,2 < anime_taglist.tsv| uniq -c | sed 10q
29 122772    六心公主
30 125900    锁链战记~赫克瑟塔斯之光~
25 126185    POPIN Q
30 129805    混沌子
11 131901    神怒之日
30 143205    南镰仓高校女子自行车社
29 146732    碧蓝幻想
30 148037    伤物语III 冷血篇
30 148099    刀剑神域:序列之争
30 148181    飙速宅男 新世代

其中,cut 命令对我们抽取出来的标签列表的第一列和第二列单独取出,然后送给 uniquniq 会对每行进行 de-duplication 的操作,并且加上 -c 的选项会给出每行出现的个数。在输出的内容中,第一列就是每个动画对应的标签数量。

需要重点强调的是,uniq 只能对已经排序的输入有效。

可以看到,在一个条目页面,标签数量最多只有 30 个。

最大、最小值

有时候我们会对列表的某几项求取最大、最小值。下面显示了如何求取每一部动画标记人数最多的 tag。当然,我们也是用 awk 完成的。

1
awk -F "\t" '$1!=prev {print $0; prev=$1}' <anime_taglist.tsv | sed 10q
122772    六心公主    村上隆    10
125900    锁链战记~赫克瑟塔斯之光~    2017年1月    61
126185    POPIN Q    剧场版    24
129805    混沌子    2017年1月    170
131901    神怒之日    GAL改    4
143205    南镰仓高校女子自行车社    2017年1月    34
146732    碧蓝幻想    A-1Pictures    38
148037    伤物语III 冷血篇    剧场版    80
148099    刀剑神域:序列之争    剧场版    87
148181    飙速宅男 新世代    2017年1月    36

请注意,受了 uniq 的启发,这里的处理也是需要输入已经排好序。

平均值

既然有了 awk,我们就可以做更多的复杂计算。这里我们用平均值举一个例子:我们希望对爬取的收藏记录挑出动画和有评分的,计算其平均值。我还是希望能在有序输入上计算,于是先生成有序输入:

1
2
sed 1d record-2017-02-20T14_03_27-2017-02-24T10_57_16.tsv | awk -F "\t" '$6 && $3=="anime"' | sort -k2,2n | cut -f7 --complement > anime_record.tsv
head anime_record.tsv
103122    2    anime    do    2012-10-18    9
103909    2    anime    collect    2012-11-06    8
104394    2    anime    collect    2012-12-28    9
110320    2    anime    collect    2012-12-11    10
112414    2    anime    collect    2012-12-26    7
118090    2    anime    collect    2013-01-31    7
125406    2    anime    collect    2013-03-04    8
165363    2    anime    collect    2013-10-10    6
183190    2    anime    collect    2014-02-01    8
207963    2    anime    collect    2014-07-21    8

然后按照下式计算每个动画的平均值,并把平均值从高到低排序。

1
awk -F "\t" '{cnt[$2]+=1; cum[$2]+=$6}; END {for(i in cnt){printf("%d\t%f\t%d\n", i, cum[i]/cnt[i], cnt[i]);}}' < anime_record.tsv | sort -t$'\t' -k2,2nr -k3,3nr | head
202419    10.000000    4
186960    10.000000    3
143694    10.000000    2
158396    10.000000    2
193619    10.000000    2
11121    10.000000    1
127124    10.000000    1
127125    10.000000    1
127597    10.000000    1
137405    10.000000    1
sort: write failed: standard output: Broken pipe
sort: write error

需要注意的是,这里又使用了 awk 里面的字典数据结构(associative array)。你可以看作是一个 python 里面的 dict。我们使用了两个变量:cntcum 存储每一个动画 id 的评分人数和评分总和。在最后,我们在 END 包围的代码块里面生成最后的结果。这时候 END 里面的语句是在文件遍历之后执行的。

3. Union, intersection and except

在用户记录中丢失的用户

在前文我们讲过,用户数据可能会因爬虫爬取时出现 500 错误而丢失,但是条目记录可能保留了这部分数据。而且,由于用户数据爬取的时间先于收藏数据,会出现用户在其间改了用户名、还有新的注册用户加入 Bangumi 等问题。这里我们试着查看有多少这类在用户数据中丢失的用户。

首先我们用用户数据生成用户 id 和 username 的列表:

1
2
sed 1d user-2017-02-17T12_26_12-2017-02-19T06_06_44.tsv| cut -f1,2 | sort -t$'\t' -k1,1n > user_list.tsv
tail user_list.tsv
321167    321167
321168    321168
321169    321169
321170    321170
321171    321171
321172    321172
321173    321173
321174    321174
321175    321175
321176    321176

还有条目数据中的用户列表:

1
2
sed 1d record-2017-02-20T14_03_27-2017-02-24T10_57_16.tsv | cut -f1 | sort -t$'\t' -k1,1 | uniq > record_user.tsv
head record_user.tsv
100004
100017
100018
100026
100039
100049
100051
100054
10006
100060

这实际上就是求 record_user 对于 user_list 中 user 的差集。如果是这样的话,我们可以使用 comm 命令:

1
2
cut -f2 user_list.tsv| sort | comm -13 - record_user.tsv > user_failure.tsv
head user_failure.tsv
12372
128259
1465
15000
174374
17601
210506
223507
257848
273783

comm 命令对每个文件的行操作,同时它的先验要求是文件已经排过序。它可以给出三列数据:仅在第一个文件中出现的行;仅在第二个文件中出现的行;在两个文件中同时出现的行。可以看出,这个就是求差集和并集的操作。通过指定 -13,我们指定只输出仅在第二个文件中出现的行,也就是在 user_list.tsv 中没有爬到的用户。

4. Join

获得收藏数据中的用户 id

在收藏数据中,我们记录下了用户的用户名,却没有记录用户 id 和用户昵称!这个是爬虫在设计时候的缺陷。这时候只能通过和用户数据 join 来弥补了。可是怎么在文本文件中进行 join 的操作呢?

首先,我们抽取两组数据:

1
2
sed 1d record-2017-02-20T14_03_27-2017-02-24T10_57_16.tsv| sort -t$'\t' -k1,1 > record.sorted.tsv
head record.sorted.tsv
100004    10380    anime    collect    2012-09-30    10    标签:;中二病;花泽香菜;嘟嘟噜;牧瀬紅莉栖;Steins
100004    10440    anime    collect    2012-10-02    9    标签:;あの日見た花の名前を僕達はまだ知らない
100004    10639    anime    collect    2012-10-02        标签:;虚渊玄;奈绪蘑菇;梶浦
100004    16235    anime    collect    2012-10-02    8    标签:;未来日记;我妻由乃
100004    18635    anime    collect    2012-10-02    7    标签:;罪恶王冠;泽野弘之
100004    23684    anime    collect    2012-09-30    9    标签:;黑子的篮球;基
100004    23686    anime    do    2012-10-01        标签:;刀剑神域;2012年7月;SAO;川原砾;梶浦由纪
100004    2453    anime    collect    2012-10-02    7    标签:;BRS;Black★RockShooter
100004    2463    anime    collect    2012-10-02    8    标签:;デュラララ!!
100004    265    anime    collect    2012-09-30    10    标签:;EVA;庵野秀明;神作;Evangelion;补完
1
2
sed 1d user-2017-02-17T12_26_12-2017-02-19T06_06_44.tsv| cut -f1,2,3 | sort -t$'\t' -k2,2 > user_list.sorted.tsv
head user_list.sorted.tsv
10    10    MoMo.
100    100    Jacob
10000    10000    漠漠
100000    100000    natalie_1204
100001    100001    七堂伽蓝
100002    100002    宇
100003    100003    二的二次方
100004    100004    从不卖萌的K
100005    100005    Astrid
100006    100006    tsiaben

需要注意的是,我们希望对用户的用户名进行 join,其前提是我们的列表需要对被 join 的列进行排序。我们已经在上面进行了对用户名列的排序操作。

接着,我们可以使用 join 了。我们可以首先进行 INNER JOIN:

1
2
# inner join
join -t$'\t' -1 2 -2 1 user_list.sorted.tsv record.sorted.tsv | wc -l
6483106
1
join -t$'\t' -1 2 -2 1 user_list.sorted.tsv record.sorted.tsv | sed 5q
100004    100004    从不卖萌的K    10380    anime    collect    2012-09-30    10    标签:;中二病;花泽香菜;嘟嘟噜;牧瀬紅莉栖;Steins
100004    100004    从不卖萌的K    10440    anime    collect    2012-10-02    9    标签:;あの日見た花の名前を僕達はまだ知らない
100004    100004    从不卖萌的K    10639    anime    collect    2012-10-02        标签:;虚渊玄;奈绪蘑菇;梶浦
100004    100004    从不卖萌的K    16235    anime    collect    2012-10-02    8    标签:;未来日记;我妻由乃
100004    100004    从不卖萌的K    18635    anime    collect    2012-10-02    7    标签:;罪恶王冠;泽野弘之
join: write error: Broken pipe

可以看到我们已经 join 了一些有价值的东西。被 join 的那个字段总是在第一列。

如果使用 LEFT OUTER JOIN 或是 RIGHT OUTER JOIN,我们需要用 -a 指定哪个文件需要全部输出:

1
2
# left outer join
join -t$'\t' -1 2 -2 1 -a 1 user_list.sorted.tsv record.sorted.tsv | wc -l
6718272
1
join -t$'\t' -1 2 -2 1 -a 1 user_list.sorted.tsv record.sorted.tsv | head
10    10    MoMo.
100    100    Jacob
10000    10000    漠漠
100000    100000    natalie_1204
100001    100001    七堂伽蓝
100002    100002    宇
100003    100003    二的二次方
100004    100004    从不卖萌的K    10380    anime    collect    2012-09-30    10    标签:;中二病;花泽香菜;嘟嘟噜;牧瀬紅莉栖;Steins
100004    100004    从不卖萌的K    10440    anime    collect    2012-10-02    9    标签:;あの日見た花の名前を僕達はまだ知らない
100004    100004    从不卖萌的K    10639    anime    collect    2012-10-02        标签:;虚渊玄;奈绪蘑菇;梶浦
join: write error: Broken pipe
1
2
# right outer join
join -t$'\t' -1 2 -2 1 -a 2 user_list.sorted.tsv record.sorted.tsv | wc -l
6492074

这里我们可以看到使用 RIGHT OUTER JOIN 的时候,对于没有被 join 上的用户就是在 user_list 中没有出现的用户。这时候没有被 join 上用户名和用户昵称的记录应该和 user_failure.tsv 的内容是一样的。

1
2
join -t$'\t' -1 2 -2 1 -a 2 user_list.sorted.tsv record.sorted.tsv | sort > record.complete.tsv
join -t$'\t' -1 2 -2 1 user_list.sorted.tsv record.sorted.tsv | sort > record.inner.tsv
1
comm -13 record.inner.tsv record.complete.tsv| cut -f1 | uniq | diff - user_failure.tsv
140d139
< sdf4
141a141
> sdf4

可以看到两个文件在实际内容上,除了顺序有所差别,并没有本质上的变动。这一定程度上证明了 JOIN 的正确性。

当然,我们既然有了 join,就会有人问怎么进行多个 column 的 join?遗憾的是,目前 join 只支持一列的 join。对于多列的 join 可以参考这个

5. Conclusion and future plan for Bangumi Spider

依据现有的技术,我们可以通过配合 awksort 进行文本文件操作从而组合成各种我们想要的类似于 SQL 语句的功能,这个给我们线下调试带来了很大的方便。同时我们可以使用 catcomm 达到交集、并集和差集的功能,使用 join 可以把两个文件通过指定的列 join 起来。在懒得使用 pandas 或数据库的时候,这些命令给我们带来了很大的方便。

同时,我觉得目前 Bangumi Spider 问题太多。我计划从一下方面入手将 Bangumi Spider 升级:

  1. 废除 user spider,更新 record spider 使之记录 user spider 目前所记录的所有信息,在爬虫结束任务之后,通过 raw record spider 生成 record 记录和 user 记录。后期处理还要力求 record spider 不能重复爬取条目。
  2. 修改 Bangumi Spider 对 Bangumi 主站 500 错误的处理。
  3. 保留 subject spider 的部分功能,特别是 infobox 那边的功能,去掉 tag 信息。 subject 要和 record 相 join 解决条目重定位的问题。

留言