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