|
本帖最后由 bt4baidu 于 2015-11-22 10:08 编辑 * x b, v# l% v. [% w8 j3 N/ c. K
! D/ ~, c1 O5 X; ]这篇文章主要是给计算机小白和初学者扫盲。1 a( B) [1 @" ]4 K2 C
本人尽量写得浅显一点, 希望完全没接触过计算机编程的文科生也可以看懂。3 q/ H1 [2 S, @! o: Y# k; v; k
只讲原理和思路, 具体的编程语言、语法之类的问题自己找资料解决,网上到处都是。
; M5 k1 m3 G+ V0 a; ~+ O1 O
9 l' v" t8 q; u& J一、计算机的两个终极哲学问题
y: {* U* W2 k" k4 Z1936年,图灵在他的重要论文《论可计算数及其在判定问题上的应用》里,提出著名的“图灵机”的设想。
% @8 X5 G) v* x4 n( D) Y+ e/ z; q图灵机被公认为现代计算机的原型,它的工作原理简单来说是这样的:% q' R2 J( T' ~
设想一条无限长的纸带,上面分成了一个个的小方格,方格有两种:空白的和上面有“—”的;
2 W6 |3 o$ T( U2 }# r( O机器读入纸带上的方格信息,根据这些信息, 结合自己的内部状态查找程序表,输出计算结果。0 d- U3 A# M7 P
方格信息代表了解决某一问题所需要的步骤,这样进行下去,就可以模拟人类的任何计算过程。- z5 L, X8 a, h/ q2 Z& W% J
“纸带方格”代表外部输入,“内部状态查找程序表”代表算法——也就是程序,要靠人来写的。1 O5 M* _6 v( p1 k/ J7 Y
* v7 E. v: Z- |0 T
那么要写出程序,立即就会发现不得不解决两个问题:: j* e i* i5 w1 l) j) i
1、怎么知道机器当前读入的是哪个方格?怎么读到具体某一个方格?也就是寻址。7 L. z# V, V+ _9 b/ y
2、怎么把两种方格区分开?也就是特征识别。
% F$ U$ h# g9 X这两个问题,就是计算机的终极哲学问题。
* }, J' U% i& F& p) m理论上,所有的计算机程序问题都可以逐步分解下去,最终分解为这两个基本问题。+ _' I* \# F+ s7 A _$ S1 r
下面的讲解也会以这两个问题为核心展开。( ~* }7 m) V4 Z4 l2 F
' F8 J8 ]4 k |3 q0 o( I, G) mBTW, 如果你能想通这两个问题,遇到编程问题都可以这样子分解一下,把自己想象成一台图灵机,4 q9 T' h6 u+ v# V* i
——所谓“采用程序化思维”,也就相当于打通了任督二脉,立即具有了至少10年的编程内功。
F1 {4 @# m* y8 E) h ^9 i3 d4 }所谓编程,本质上就是一种读取、存放、组织、区分数据,然后按照具体业务计算出结果的活动。$ B% G z, e1 k
前者是核心,“我强烈建议围绕着数据来设计代码,而不是反其道而行之...坏程序员总是担心他们的代码,
+ g8 A1 u+ m7 i! l而优秀的程序员则会担心数据结构和它们之间的关系。”——Linus曰。
; Z+ n3 B( T7 v, A( O3 v" k具体的招式,也就是某种具体编程语言的语法,花个半天功夫就能学会的。% N/ ?. r A5 _
8 J7 U6 N, k" a, @
不要觉得自己上学时学的不是这个,or文科生,就不行。
5 r6 Z! Q1 M; C1 n江民杀毒软件大家想必都听说过。
( Y9 K2 o. C0 L+ I X( U8 l+ W创始人王江民同志,初中毕业,38岁才开始自学计算机,不出几年,就成为中国最早的反病毒专家。1 p5 c. w+ w7 r+ s4 a
咱不奢望成为专家,写写程序总还是可以的吧?% R! M! |4 o* @0 ^/ M4 c( @1 e! c
, ~: n- w5 m9 n6 b& e K二、采用何种编程语言
- v ^' [& }5 x9 A+ T' t. C上面已经说过,存放、读取、组织、区分数据是编程的核心问题。
4 `. Y; f' q: M显然,能够方便的进行上述四种活动的编程语言就是最好用、最易上手的编程语言。
: c n* _& I R' m: c) z* [4 _( B抓网站,恐怕没有哪种语言比Python更方便。! ~* y5 j& u: g6 Z# L" A
当然,你要愿意,用C语言也不是不可以,不过可不是那么容易上手,
. T9 C! i$ k1 x; ]% z3 K: r3 q& d计算机专业的学生,有些到了大四毕业居然还搞不清何为指针、何为引用...这些家伙还是趁早转行,( [1 j) m; D6 R" b3 k1 @, f
没有慧根就别吃这碗饭。3 W! }* U7 w8 ~: f
! F8 U" j5 D9 T5 Y, O
三、网站抓取技术, m1 B2 c, E5 ~7 A) H6 D
1、下载某一个网页,提取其内容
! |6 X5 z" M2 K P3 ?7 V2 I" Z% S以前写过一篇,就不重复了。参考:# U6 D9 c! [! ]2 o7 F
用一个简单的例子讲讲怎样从网站上扒数据$ @2 J7 D( P" }9 [7 o4 ?; ~/ C
9 m* N" Y5 {5 p/ \2、寻址问题
3 N& i8 p& c8 p下载网页,自然首先要知道网址,也就是东西放在哪儿。
. F- C5 G- { B; R- j8 X如果事先有个单词总表,那是最简单不过,立即就可以根据网站的网址规则拼出来。
1 `3 F, c6 b9 d3 e$ @6 o" U* T) I但是大部分在线词典,到底收了多少单词,事先是完全不知道的,
& C* a% o& U2 o- _: z要把单词弄全,就要想办法得到每个单词的网址。- j+ u$ `. t% \6 ~% W( Y
总结各主流词典网站,大概可以分为这么几类:1 H! r+ B4 r6 ]( O
I. 事先有单词总表& l- A( ], M. E8 Y
比如http://www.vocabulary.com就是这种类型。8 g' k6 P6 M0 P
它是在wordnet3.0的基础上编纂的,直接用wordnet3.0的词汇表拼网址就可以。
5 D) J* ?' i6 I- D- n
* N! A" J. |. KII. 网站有索引页面
+ `9 f9 D2 N5 [; R如:
5 G0 G2 J& G, w" u9 N! e9 Q1 sOALD(http://www.oxfordlearnersdictionaries.com/)
* X$ J4 A5 ?$ ?2 Q4 f' I它的索引页在 http://www.oxfordlearnersdictionaries.com/browse/english/
h. b" c5 z9 a0 GLDOCE(http://global.longmandictionaries.com/)
* K% D6 ^% Z) U2 h; a9 V采用框架结构,左侧边栏就是索引页
2 k `7 ~5 t) |: y! jMWC(http://www.merriam-webster.com)2 ^+ O: \( p6 o; q# k
索引页在 http://www.merriam-webster.com/browse/dictionary/
9 I! R1 p: z# _' x* {/ O4 b. s6 U2 a等等
6 Y1 u: P# N6 {% d这类也容易搞,思路是先从索引页得到单词表和网址,再一个个下载。
3 v1 q% h. _. o
5 |, L( K( k! t! z' v4 t- urls = []
8 i- b3 y c9 E0 n T a6 I/ z1 B - for someindex in indexs: # 循环所有索引页
3 @& C7 F0 g, n+ \ - browseurl = ''.join(['http://somewebsite.com/', someindex]). Q& ^: d8 q/ @8 X0 T
- browsepage = getpage(browseurl) # 下载索引页面 b* \; u3 a6 w3 {: ~+ ~$ [' A. l4 W
- target = SoupStrainer('sometag', class_='target') # 抠出放单词链接的区域
, j5 y, _8 | A' p - bs = BeautifulSoup(browsepage, parse_only=target)
n, D+ H" y ^ - if bs:: U3 c+ p, K& |4 }' j# w
- for a in bs.find_all('a'):: ]8 B" R( P( B/ t4 Y+ V
- urls.append((a.string, a['href'])) # 取得该索引页上全部单词及链接
6 L% ]) @$ K. H/ ? - 然后:0 q1 @4 y4 B/ S0 \7 {! r- A( W( H( b
- for url in urls: # 循环所有单词1 r9 I2 {2 w) F; ` }
- wordpage = getpage(url) # 下载单词页面
/ a! \( p, |9 C6 Z) S! ~) i
复制代码
. A5 H4 X# f) x% G; \
( o% r1 V" p& o' ?* P* Q! IIII. 索引页和单词释义一体型网站6 d/ e* |6 {# n% A& s) Q9 y
如:Online Etymology(http://www.etymonline.com/)5 W# m+ G: l9 U7 K; X: }$ ~. k, R
和上述II.处理方式相同,在循环索引页的过程中把单词抠出来即可
9 ?$ c1 r9 d3 C) ]4 j2 \
' o- y" w/ Y% Y% M0 c- for someindex in indexs: # 循环所有索引页
0 t+ C) }# s6 V' {, y - browseurl = ''.join(['http://somewebsite.com/', someindex])
5 Q% F: k; W+ ]7 J, Y' a - page = getpage(browseurl) # 下载页面
" S6 `$ k J& @" ~1 W - target = SoupStrainer('sometag', class_='target') # 抠出放单词的区域
6 h% w9 z e, L9 Q8 `5 {" ? - bs = BeautifulSoup(page, parse_only=target)
( x) B; i; i) W' u4 |3 Q2 _- u - for tag in bs.find_all(target): # 循环抠出单词
: |- C' ]) j9 R4 b) C - worddefine = getworddefine(tag)
4 j x# d9 h+ O# z/ C* D
复制代码
3 p0 ?/ S; H" S9 k( p% [. m; b' `* S, M2 p* k' t- ]
IV. 片断索引型网站
5 g- |. _; `' m/ {如:: T9 k0 V: R1 @- ?9 E
ODE(http://www.oxforddictionaries.com/)
$ Q. c$ {+ M$ p5 ^每查一个单词,右侧边栏有个Nearby words9 G- |5 Z# d3 A
RHD(http://dictionary.reference.com/)0 N- u- u% W! `; \! h+ Q
右侧边栏有Nearby words# U" ?* w) S C7 d. W0 o8 u
CALD(http://dictionary.cambridge.org/)
- ^7 I' g, K" B8 B) x在页面的最下面有个Browse栏,给出前后相邻的单词) J, H% m1 @) {+ R+ c. O
这类网站没有总索引,只好利用它的Nearby栏。7 [1 |. J9 ^4 L
思路是从第一个单词(一般为‘a’或者符号数字之类的)开始抓,& @5 r8 V* R! Z" `6 `1 d. i
每抓一个单词,同时得到下一个单词的网址,直到最后一个单词结束(一般为‘zzz’什么的)
; s3 E9 t4 {) s* y) B* g- / [# _8 A0 }2 s: z z
- cur = 'a'8 c6 A6 V T$ V( S4 Z2 E& E
- end = 'z'3 K% p0 W$ j6 j4 n3 k( k
- nexturl = ''.join(['http://somewebsite.com/', cur])1 w1 l4 v F- `7 _- s0 }1 }- S, ?
- while cur!=end and nexturl:
2 y* |" q- R6 k - page = getpage(nexturl) # 下载单词页面
% V9 `5 ^' j8 a0 p7 y5 ] - worddefine, cur, nexturl = getword(page) # 得到本单词释义、下一个单词及其链接
9 K( U, v: L( U' v; `
复制代码
6 t" v6 e7 O2 [6 K
' H; Q! Z b: _3 |; O3 k- ~V. 完全没有任何索引,那就没法子了
3 D/ h) D& ^% @0 \1 Y- O当然穷举算是一个办法,自己搞一个庞大的单词表,然后按I.的步骤处理
' J+ X3 b. T& W6 p7 V( [理论上也是可以的,就是效率差一点;
4 r. n3 V+ N2 y1 X另外各家新词收录情况不一,有些词组、短语拼法也不太一致,单词表准备得不充分就没法完全网罗。. l, f! N. G& `
2 `% l4 d6 k/ K, u( h3、提高下载效率
! w+ }- z7 V* fI. 多进程
2 R q* |- T4 ]& m- m上面写的都是伪代码,也就是简单示范一下处理流程,直接写个循环了事。
, ?* Q, c; v2 \& z# z实际抓网站时,这么做效率显然是非常低的。/ G: [: m u8 T
假如有十万个单词,每个单词需要1秒,循环十万次就是差不多28小时,要花掉一天,
: L& L; Z* ~! A) ^# T7 ?1 k有些网站还经常抽风,半分钟下载不了一个单词,那就更慢。0 x. H9 o* g# K% U
假如在这段时间内,你家猫咪把电源插头给挠掉,或者键盘被女秘书不小心坐到了呢?
) P2 |- {/ p1 y* R- E* Z# U要速战速决,就得开多进程。
. L7 }$ v/ Q6 Q1 f5 ]7 N/ h同样十万个单词,分成25个进程下,也就是28/25=1个多小时。
) Q, M6 v2 ~% l+ [4 N8 R+ G再开多一点呢?岂不更快。。。那样硬盘就转不动了,所以也是有极限的,要看PC的配置。; O+ L; c% E4 W. q
在Python里开多进程,同样十分简单,
0 N+ R+ O1 T( H. C9 i- " V* Y8 e) I) [. I; i! B( ?; R
- from multiprocessing import Pool* U9 g3 {7 U+ U; J
- pool = Pool(25) # 开25个进程
% w9 q' A6 @$ `) U3 y4 O2 D* K; F - pool.map(downloadloop, args) # downloadloop是下载函数,args是其参数$ x3 r S6 _$ w: o4 G4 Q+ H2 D# _
复制代码
* Y9 ]( S. d% e( @- [; I: E( G这就搞定了。
) P; v3 H, s7 L. `) J& q) J( N- A- h1 u& a8 Y
对于上述I.~III.,包括V.,分块比较容易,无非是把一个大数组分成25份,, m4 w3 E) D; o6 A/ e
关于IV.,事先没有单词总表,就只好采用区间的概念,: ?/ _7 O0 C. u3 Y. a. F& F: q- S
比如('a', 'b'), ('b', 'c')。。。这样划分若干个区间下载5 F! }2 y6 m7 v
5 `1 i' `7 `/ U+ X- z. V$ f; L- |初学编程的人,一碰到进程、线程,常常有种畏惧感,
+ P! F$ ]* g, O8 ]+ _看到同步锁、共享内存、信号量什么的顿时觉得头大。
- t" C, r) O, T% y其实没什么好怕的,这是个寻址问题,关键是搞清楚它的内存空间构造是怎样的,
$ s( A; R* j9 H其中涉及到一点操作系统、读写互斥、原子操作的概念,找相关的书了解一下即可,没有特别难以理解的。
6 y7 E/ {! }* [3 b2 _' l/ L3 }/ I, |7 u- G/ a- J! X6 L
II. 断点续传
. c; b+ \3 x1 Y3 e' H) P事情永远不是那么完美的,网络连接随时有可能中断,网站可能存在瑕疵、死链。
, ~4 {5 k! D2 o. S% ]3 w所以下载程序也要有点容错的功能,最好莫过于可以自行从中断处恢复,完全无需人工干预;9 k8 u8 e5 Q) ? N8 r- Z% v
即便无法自行恢复,也得容易手工处理,不然可有的烦了。* }9 H f+ U! Z5 N
这也是个寻址问题:关键是搞清楚从什么地方断的,把它标记下来;循环检测没下完的区块,从中断处接着下,
( G# H: _+ ?' O! f# o% `9 D& i2 u直到所有区块下完。 U3 I' f2 g1 z# b* e* ` z! z+ ?1 C
- # E3 f. _4 ^" c' e
- def fetch_a_word(part, word, url, data): # 下载一个单词4 d) U. o0 R ?/ g5 {: |
- word_define, failed = get_a_word_from_website(word, url)
' R7 r' L' M* |; k* ~; T - if failed:0 G; { z, j" E' z }7 ~" X
- dump_failed_word(part) # 输出下载失败的单词及网址0 p4 h7 c* D3 [# N# F7 q
- return False
( D# q1 t' C2 T+ ] - else:
' a, F9 P, c& V/ E - data.append(word_define) # 保存下载成功的单词数据
3 z2 B5 T+ n$ L- c - return True! M" x0 D9 r- `: s- k; r) b
w& f9 ~4 O" j) n- def download(part): # 下载一个区块6 t) {$ E6 v8 O* G
- words = getwordlist(part) # 读取单词总表; g# O. w5 ?: r2 F+ g
- if hasfailed(part):% G, v3 @+ g# J5 r* F
- word, url = read_failed_word(part) # 读取前次下载失败的单词及网址
, e6 Y& w5 P' O% }. E - else:
# W5 V5 d9 j& J* d - word, url = words[0] # 首次从头下载
4 M& {4 e P8 l4 D, ` - data = [] # 用来存放单词定义
. S8 G4 Z6 M. H5 t, L - while not_end(words): # 循环下载3 T0 V+ H% N2 ]- P; O$ X$ @; V4 g
- if not fetch_a_word(part, word, url, data):
8 _: G( v8 N1 O - failed = True
/ j0 f" n$ J) h6 M4 |: Z9 v6 v Z- P - break H% U' g0 a, {3 x! o) I- T
- else:+ b) S& b( d9 v
- word, url = get_next_word(words) # 准备下一个单词及其网址
& o9 Z' R! ]7 f# {8 \ - if failed:- K6 X7 M _& z( k, { t+ t( h
- suffix = '.part'
9 X- \# o" f5 N* E* t6 y" \* d$ k - dump_data(data, suffix) # 输出下载成功的单词,若因下载失败中断,文件名加后缀'.part'
; l$ d v, h) D0 W7 ?& z
v5 b1 p" K+ i Q/ c- def isfinished(part) : # 判断某区块是否下载完成8 W% h3 D' Y1 `$ F" t& I- [
- if is_data_file_exists(''.join([path, part])): # 通过检查数据文件有没有生成来判断
5 V$ O U6 p. e! N - return True
" B. f2 T ^4 l - else:8 w# U7 R' T/ v
- return False
+ x6 _9 Z0 l6 I) A, \1 ^) P - " {; H: v. W$ x& [! \
- def downloadloop(): # 循环检测未下完的区块* O3 R; C) ?$ W+ k
- finished = 0
8 K- h0 Q9 u" \% c; r - while not finished:
" v6 _' I2 I/ M+ i+ T, a+ P - nf = [] # 没下完的区块
1 S8 }/ t1 _$ c" |( Y - for part in parts:
O. W& l$ @: o+ n4 K - if not isfinished(part):* T+ I9 W" M8 w+ F5 \, V l
- nf.append(part)
0 l! }' O/ y3 d: J" V( y: c! y+ T - finished = not nf, ^$ ~& S6 _/ I6 ~6 }
- for part in nf:' G( W p, r, \0 s' l+ `
- download(part)
: r2 e7 y- }! v" h) {. G. G
复制代码
7 c% j8 z, \. z5 }: F3 ^2 S+ I) T) _2 R0 {+ L5 I& H* M
III. 高速下载网页的小技巧. R: q! d3 c2 E5 `
Python里面有三个库都可以用来下载网页:urllib2、urllib3和requests。
; Y N0 g8 M4 P: m其中urllib2是Python原生的,urllib3和requests为第三方库。
) U* }" ]; M5 |(似乎Python3已经把urllib3收编为正规军了)5 B5 V0 s8 f7 @' M$ H) D
这三个库有什么区别呢?
. R$ L" m6 j$ T2 i形象点说,urllib2相当于去别人家“拿”东西,开门只拿一件,然后关上门,再开门拿下一件的家伙。" m' k& O- \& ^# J1 `
再笨的贼也没有这么干的,不停地开、关门太浪费时间,也容易招警察;同样,频繁连接也会网站被封IP,' V* ]' l6 g6 H
所以urllib3在urllib2基础上加了一个HTTP连接池,保持连接不中断,打开门以后一次拿个够,然后关门走人。
. D" H5 B, T7 R, [但是urllib3有个问题,它不支持cookie,所以无法保存用户信息,遇到需要登录的网站就没辙了。
# w1 _1 w- M$ }这时候就该轮到requests出场。requests在urllib3的基础上又进化了一步,它可以通过session来保存用户信息,2 @; s5 w* o4 U) a
通吃一切网站。
' m9 D3 Q7 V) z. c所以你完全可以只用requests,忽略另外两个。不过我一般习惯用urllib3,只在需要登录的时候才用requests。# |5 J- ]# E* c( o
这仨库的用法都非常简单, 两个第三方库都有齐全的文档可供随时参考,虽然大多数功能都用不到:
C: g& v7 I+ H# I1 i% ]5 @) [' m. I" ]http://urllib3.readthedocs.org/en/latest/4 a: ]' H' l8 m9 V# F
http://docs.python-requests.org/en/latest/:* Z; K8 p" Z: P) m4 m! D
( n+ G Q, J) v6 z8 Y, e! m" V- #urllib2; b% e6 E! e& T1 }
- import urllib2
* J6 O, T( C( p! z - def getpage(url):" ~# C( }: T6 _; ?
- req = urllib2.Request(url)
/ @6 p8 B/ W3 `' o1 w( r8 j4 o- e - response = urllib2.urlopen(req)
7 Y: O1 x, T) z( Q' w0 ?6 ~* e - page = response.read()# ]& E0 @( \$ E7 g7 O; _7 s J# }
. P6 j- ^8 V- N* d" P( g4 z- #urllib39 D; C. Z1 r5 w5 t
- from urllib3 import PoolManager+ Q7 |' d7 L' w
- http = PoolManager()/ C0 ]6 T! s; a( q5 j1 {
- def getpage(http, url):
1 u, G2 V+ R8 L+ i& } - r = http.request('GET', url)1 ]1 ~: J- i7 o1 r% ^; I4 O3 _! M
- if r.status == 200:
c' ^8 u8 l Y; F3 t- Y - return r.data
: c' B4 X! V% n$ S$ J2 Y) K - else: D4 k* `7 l3 V L- r
- return None3 A1 y8 g. y. ?
- * c7 k; a+ M# w- e8 t/ Q9 g$ |
- #requests( y" N5 ? l3 Q: ^6 g& H
- import requests
; P; H+ Q- ~1 ?- e% W - session = requests.Session()
2 z) Y$ j' t, Y( t& P1 q6 { - def getpage(session, url): G' y5 L2 t, n8 ^1 ~1 C l* X
- r = session.get(url, timeout=10)
4 v8 v; V7 t: E% T& t6 V - if r.status == 200:
7 J$ [8 w% W. t' _$ P9 v0 p - return r.content s. x8 ?' n9 [$ n# Y
- else:
9 D2 t; v7 Y4 p2 T; k - return None8 D6 ~5 S! F& Y& A! J% [1 K
复制代码 / P% L) t/ e+ y+ B/ o* [9 Z( f% J, x% O
四、后期制作
5 @$ k: f! } u1、文本处理,是个特征识别问题。
* s" O3 ?0 o* E* t7 Z3 l本质上是找到满足某种模式的一串数据,按一定的规则转换成另一种模式。+ |1 X7 L/ G1 R1 A& s5 U6 K7 n* x, w9 z
当前的大热门:生物识别(人脸、指纹、静脉、虹膜。。。)、语音/摄像头输入(智能家电、自动驾驶。。。)# o* Z# c5 H; f- r7 t
都涉及到特征识别问题。0 C& D q8 g/ t+ z; ^
相比这些高难度动作,文本处理算是比较简单、基础。
) @3 @( \% `- QPython里常用的文本处理技术:正则表达式、BeatifulSoup、lxml
4 x: B+ @8 @, t: E( l$ ?0 b7 t正则表达式非常强大,但没法处理递归嵌套的标签型数据
+ ~+ @% F- B; V9 t9 Z(如:<div>第1层<div>第2层<div>第3层</div></div>...</div>,这已经不属于正则文法的范畴);
* q' i* H; X* Y7 I, F* Q9 PBeatifulSoup/lxml可以处理嵌套的标签型数据,普通文本则无法处理。8 `' W8 w8 m! K( v0 V& K
所以常常要结合使用。( M; x: `* a' l6 ?8 |2 m/ k
这些难度都不大,关键是胆大心细、思维缜密,不厌其烦,慢工出细活。
. R3 w5 B5 V% W+ C; j! f( `, j$ p1 ~% Y9 T/ }) Q9 D
2、排版
9 h T9 S2 E6 b1 A( {+ XHTML、CSS的基础知识:. M+ ^# Q+ g0 v
http://www.w3school.com.cn/html/index.asp
. c5 z, t# b& @$ k9 W2 E4 ]3 ]http://www.w3school.com.cn/css/index.asp
. B. Y/ n9 x/ Q0 ohttp://www.w3school.com.cn/css3/index.asp
. W4 @4 }) J- ^) m" o; ^非常系统、非常全面。
( m* B& q7 Q7 t+ q排版词典需要用到的HTML/CSS知识并不太多,遇到问题参考上述网站即可。
* M: |: T1 j3 \. D @" H
0 @$ u: a6 }9 m9 i% T五、结语
+ r* `2 Q& w4 j# |7 ]) c花点时间写个科普文,主要是考虑到确实有些文科同学or计算机小白想制作词典,但没有思路,无从下手。4 c! b! S8 Q; f* F1 e9 h9 C
所谓术业有专攻,为学有先后,总结一点经验分享出来,也算是对社会做点贡献——
7 W, L6 E0 H0 g2 N大家有个切实可行的方法参照,不至于绕太多弯路,浪费太多时间,从而节约了社会成本。9 X$ V. J; y' F9 d8 b/ H# @: p
7 A: L9 v/ y- G1 ~
打算做万年伸手党的同学,本人也没想过要鼓动你们,继续做伸手党好了,热心人还是挺多的,时常有不错的新作发布。
$ j7 |" B9 u) I2 p, z
- A5 z5 H. O+ ]只是拜托不要打扰别人,真想要就自己动手。
& J7 W7 R9 Y7 N8 |尤其不要抱着“你手熟,水平高,做得比我快”的想法,觉得找别人做词典就特别理直气壮、理所当然。
0 R, Y+ i6 y9 ^3 ^水平再高也要花时间;同时,水平高也意味着其单位时间的价值要超过水平低的人。5 k" ]& _, }! q. X" o1 }
虽然每个人都觉得自己至高无上,应当受到别人重视,
' [- k4 R1 \$ \, m( s2 v8 P5 f其实“在你做出惊天动地的大事、拥有巨大名声之前,你在别人眼里就是个屁”——Bill Gates曰
" l5 [9 n$ m; g" ^/ `. O9 R8 z8 @7 M; r0 i1 }5 o' b% ~
0 S, g+ L8 j& i& p- R========
$ ^& @' U3 h" v* m六、拾遗
2 g% ~8 g+ D! V$ U' \' n3 `4 t关于上述IV. 片断索引型网站,有坛友指出ODE、RHU等都有索引页,因此可以归到第II.类
k+ b' h6 o: d( y确实如此
1 q" }0 u7 r4 S9 H9 w不过这里只是举例而已,不用太较真啦 4 i. ?( Q6 z) h, ~7 _7 _" p
实际操作过程中,由于网站可能存在索引不全、死链、交叉跳转及数据瑕疵,往往要将II.和IV. 的方法结合起来用,否则不是抓重就是抓漏
* v f8 b6 K8 ^' R" N1 x0 R这种综合性的抓取方法,本人称之为单词表密集轰炸+广度扩展法。. |. x0 q4 q# E6 ^) H
即,# B$ e) O5 r& l
第一轮:先从索引页面获取全部的词头(集合A),再用这些词头去网站下载单词,同时提取每个单词页面里指向其它单词的链接(集合B)" Q- y- \% A1 L$ @: n
第二轮:从集合B里删除已在集合A里的词头,得集合C;再用这个集合C里的词头去下载单词,并提取每个页面里的链接(集合D)
! v4 M8 a6 A- _7 W1 ^3 g0 V7 N第三轮:从集合D里删除已在集合A和B的并集里的词头,然后重复第二轮的后几步动作
5 n( C" X8 u3 c。。。
, Z) U6 n1 G- q" I+ P. [& M直到无法提取到新链接时,结束下载。大部分时候,第二轮结束时就已经提取不到新链接(即集合D为空): j `/ j7 p1 S2 g6 @
最近新做的RHU、CED、WBD,均是采用这种方法,速度快,容错性强,效果极好。2 w' _0 {- z( \7 ]& N1 y
形象点说,这种方法相当于草原上先有若干个着火点,这些着火点会把其周围的草地点着,最终烧光整片草原。5 `6 ~; T$ R! X8 o" M* c9 I4 ?$ m
因为一开始着火点就已经比较多(索引页的大篇单词表),所以会烧得非常快、非常完整。5 n1 w' b* U% e% ]- p8 W. M$ Z
|
评分
-
4
查看全部评分
-
|