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