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