* a8 @$ a' N) O- F7 D# A% r 1 |; ^* Z: q( G. R& b. ?( Z4 j, \pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple beautifulsoup4 ' I. l$ u' e) b( |9 F' D8 L) _1 3 c1 y2 X% u+ q) [( b9 s导入即可9 W) Q- Q/ j9 ~& \" \
! E# S R( ]* Y4 E% j# J
R3 F% z9 M/ S/ A
from bs4 import BeautifulSoup * k* @$ o+ T0 C9 k16 T$ C2 L- S/ J) e3 S
html_doc = """7 Y1 \! h. I3 {4 q6 K7 l
<html><head><title>The Dormouse's story</title></head> ! e* J, V8 |4 E- d<body> 7 q% W+ I4 u/ i" A: ^% ^<p class="title"><b>The Dormouse's story</b></p> G- @* \- w Q+ N 4 m$ |' Y6 N6 ^4 Q & J2 i. ~2 b0 A, E<p class="story">Once upon a time there were three little sisters; and their names were + M. k+ \" }1 v" f6 K& [<a class="sister" id="link1">Elsie</a>, 8 I; X# T7 a' T5 m# n# a* a1 |<a class="sister" id="link2">Lacie</a> and 8 Y r/ r7 U. j- e2 S<a class="sister" id="link3">Tillie</a>;/ _: Q; E9 T0 n+ b% @
and they lived at the bottom of a well.</p> 1 @7 ]4 P$ W# }( Q& h8 Q* c- U 8 s! Q( p: r$ g( t 1 R S% m- S6 V<p class="story">...</p> 0 I; t$ f( q* d3 c \/ l"""2 B+ K+ W8 k+ B0 Y
1 ; D( l: B. R* _6 `' j$ B2 \( z! S" Y: n$ v* ~6 _% a6 e$ N3 B3 1 n: |% L3 {. [- b4* T/ n$ p( a3 F* t0 R7 I- `3 c) `* _
5 ' H. `# z9 K4 v3 P4 u* _0 \6- v" s2 y. g+ @* ^4 y
7 7 @4 p# K$ ?- V4 h5 d8 3 Z# c( D2 W* R2 w) v' d9 ) h4 c6 T7 h; Q4 x" h10 + b; w/ B7 T7 T1 F! T- k11 ) D# S8 W7 `. \, X: f4 x- | o# C12 $ z/ j3 R X$ K9 X3 R. t2 D137 G/ d; x* g7 R3 ~% w- _
soup = BeautifulSoup(html_doc,"lxml")- D2 K4 e* ?( J" [/ I6 S$ ]
1 - W8 n: O# O4 Z5 F" ^几个简单的浏览结构化数据的方法/ m' T2 H" R0 B
soup.title 9 f! D6 ~, c7 y2 q8 t& R' T" F1 8 e& n6 e# h3 i! f6 i* J, ]. U<title>The Dormouse's story</title> % Q, G. b# _# ^% U8 O12 i V4 @) J" Y2 e3 V' F
soup.title.name ! a4 G8 ^+ V. `6 e9 V7 g5 d& W1 . A+ [+ `. r( |* Y- y'title' 3 ^ |; T/ q- ]% P. \# Y4 D1# w- N3 E7 I- \8 h" h! r
soup.title.string - S+ `* d7 [8 b. b& q: r0 G( z8 D1 S; u! i) f8 ^4 Z! X( Y9 k"The Dormouse's story" % U; u# d [. e. t7 g11 A( f( _7 ^$ G, r7 |3 l8 f. l
soup.title.text2 p0 o9 v1 \3 L# q/ k: J/ A* F; S. r
1 ) i( m4 p. c/ N) N0 Y4 r; S* D( G0 U5 Z"The Dormouse's story" 2 }' y9 I6 Y3 E/ J% x1 B6 r: `0 F6 T# ~# Zsoup.title.parent.name* S/ G, }( N3 k1 o5 I
1% O6 G" h' @3 c
'head'0 _1 Q5 _ v4 c2 L& A
1 3 e1 E) a; a+ {- vsoup.p + x9 Q( \0 D0 E: F1) l6 _ h( s) ~; a6 [) B2 x& f8 G
<p class="title"><b>The Dormouse's story</b></p> $ J4 v0 U+ n$ J H7 B7 K1! K6 u# M" O& W, h4 f) n# h
soup.p.name; t8 ^$ V: S! f
14 Z2 k' L- w/ L, j: p3 V
'p'" }/ n$ E9 Y- A; q
18 j, Q, @ j5 i* N, f8 q
soup.p["class"] ! N! u* ~6 R+ }; U/ t1 5 b9 l1 Z/ r) \6 `( ~['title'] 5 M, \4 j' V- [8 p4 C# L$ O3 O: b1 q# a+ P* ]: c. |# F$ A
soup.a 9 x" a; x1 ]4 A+ {# l1 \; Z7 j17 q' a. w Y$ t7 k& s
<a class="sister" id="link1">Elsie</a>! ^* ~& H5 i1 G1 Y! E# R
1 : y7 [' q; v; T3 zsoup.find("a") 1 K5 h, f' G' K! m7 v1* K- q8 G! T j4 I9 o1 y
<a class="sister" id="link1">Elsie</a> 5 E; F2 O1 E3 r8 L+ H3 O1 c1 7 i0 | u3 {4 n* c/ P6 Hsoup.find_all("a"); G' u4 u$ i1 I2 a; e
1 : _* a/ Q: C1 @4 g4 r0 G3 v- j[<a class="sister" id="link1">Elsie</a>, & u" p& R: f: D4 [: X \$ n. d <a class="sister" id="link2">Lacie</a>,6 K' y( q# V2 F# j. H
<a class="sister" id="link3">Tillie</a>]* M$ [7 q; P+ h: W
1 3 @' Y$ o& X* ]1 {; i1 {- D2( g. s2 X6 u3 g9 U) E' ]* [, s
3/ h) E; V- s/ B. F* |+ h+ `9 s
从文档中找到所有的< a>标签的链接 - k9 n! z# L/ L) k4 D0 ?for link in soup.find_all("a"): ; N# l/ Q, a4 w- t. U( i+ s' O% O print(link.get("href"))4 S' o& x0 e8 P
1 5 x! p2 ^1 @' m) k2 ; j( @0 W7 a8 L6 J( lhttp://example.com/elsie 6 s! `4 d8 C$ z; ^http://example.com/lacie % h" H& @! C& rhttp://example.com/tillie ) Y7 U- r( B# Q# V1* \7 m0 w5 x- `! Q# P& p0 N$ e, A
2% |+ x I5 _* K! _
3 * H+ @# q6 s! m0 m& Z在文档中获取所有的文字内容 2 c1 ?, q1 K; b0 C) q! B6 Rprint(soup.get_text())$ d. N4 C5 s7 p0 w9 e; i; A
13 O: M9 _- O! q+ ?" k
The Dormouse's story% T- s5 g; A+ M$ N& B$ B
& g' F: _3 r" s% o ( L/ k( r, X, j! e7 Q8 b- [3 E# EThe Dormouse's story - y2 Z! _/ k, HOnce upon a time there were three little sisters; and their names were9 t4 u! R7 E: V3 a' i3 B
Elsie, 7 d5 S. W% H+ c) i! u' [Lacie and, ]" F( j# O0 d+ F$ t2 X
Tillie;/ Y! j3 y U/ h, l. j) X
and they lived at the bottom of a well. + i8 a( u/ _' x: s- L0 p" ~...8 `# `; p" j8 N4 ^& x! J
1( G5 [& X/ A9 B, C! k! q
25 j# C& t4 X) v I8 o8 K; f$ [' K
38 M7 @* F: ^: e0 G" L
4 0 l7 P2 T6 E8 ~, X0 u, E5: U, y6 z8 {; j( n t
6( r0 _6 Q- N! E" y4 T
7+ j% a* b6 K. q, S
8$ ]: F$ ?3 C- d( M
9/ V# t c; ^% g2 s3 R
5 n8 ?4 J8 }4 G; ^: J
0 [: J4 @- M: i& S/ L
: c" O* I7 `" {$ t q3 g8 e9 w+ L通过标签和属性获取 / r/ l3 [: r8 h7 i" R- sTag有很多方法和属性,在 遍历文档树 和 搜索文档树 中有详细解释.现在介绍一下tag中最重要的属性: name和attributes& Z) M! r2 P5 A X0 H
soup = BeautifulSoup('<b class="boldest">Extremely bold</b>') 7 X; I; D/ f9 u- Ctag = soup.b W/ q: `0 C: h* Itag& D i4 N7 G! {0 F
1 8 ?2 E4 V3 U& i2 7 q, ^! R8 T6 v/ h3 . U0 e0 U" w1 e" z7 K<b class="boldest">Extremely bold</b>8 g# N0 s& `: w1 y3 O( q
18 Q) a! O8 q. ]/ M
type(tag)+ q7 G# K; b6 a
1. U) c% s/ T. `5 f1 H) h
bs4.element.Tag + M7 z3 U+ b, O8 J, P3 g1# {9 J. n) ^% s k, ^# y% T
Name属性* K# }7 Q- m( K
每个tag都有自己的名字,通过 .name 来获取:' g7 R: w, p) S* B
tag.name 2 v2 m/ d% `5 p5 A/ [" k0 O1 . F' s& a$ g: S0 z3 Z'b' * [. }. d) u3 ~- y! f" b5 o1 ) q6 [+ ?5 I# S; K2 M. n如果改变了tag的name,那将影响所有通过当前Beautiful Soup对象生成的HTML文档/ K7 Y! b: G+ ^6 n. z5 F
tag.name = "blockquote"# _0 X: _% m% i5 v2 e9 i- [
tag 9 f. @% A$ h5 _( ~6 K* {1* ?5 E y v6 ~; o9 h A
2$ ]0 \8 R! V3 i' l
<blockquote class="boldest">Extremely bold</blockquote>8 {5 t0 c5 N7 F* T
1, b4 y% P+ U) o' b8 Q9 w7 t! y% w( T
多个属性" y0 `) F$ k* F5 X6 s
一个tag可能有很多个属性.tag 有一个 “class” 的属性,值为 “boldest” . tag的属性的操作方法与字典相同:) r6 l5 G9 D5 [; d% U/ d
tag["class"] ( v0 D$ @' r4 P- F' m( m' p1 ~1 C `) z2 K0 E* X['boldest'] ; g# l/ A/ c* ~! I$ M1 ]- o19 U3 D, v6 `* `2 `$ U8 e D
tag.attrs) }) B: H, c. _$ e/ B5 w) l
1 + [. s7 Z$ m: X7 x9 @' M- p{'class': ['boldest']}" o3 n( ]% ^% H4 R
1+ r+ F5 H: N: w; R" [. F% t7 u% j% S
tag的属性可以被添加,删除或修改. 再说一次, tag的属性操作方法与字典一样/ U- b9 G8 r# V" t* }' _7 g* P9 I
tag["class"] = "verybold"3 v. ?% L4 R! a% p
tag["id"] = 1 $ H1 C. [2 A3 ?0 d$ p9 vtag , g& M/ B0 r6 Z* {4 m) v: p7 z1 * `: r. t8 w# q: ~5 R$ }& j2 . X' M$ x2 p- {3 s! a3 , M% W: N+ C& J4 v% c3 v) B# Y<blockquote class="verybold" id="1">Extremely bold</blockquote> ( Z0 s" D7 q/ q; Z9 H19 U" Q- x2 V# q5 `! M
del tag["class"], d7 w& b' Y$ O6 W7 w4 s: d0 ~
tag 3 h/ `% \9 W% y8 t. Y. u1 _" V* t1 O& G; y. e: y1 b6 b8 V" n' I, [1 o2 9 ~2 }- N) M9 O5 P. k( z6 |( n. t<blockquote id="1">Extremely bold</blockquote>7 I& D- @" ^& G, z5 B0 g
1; O- _5 w! R( O+ F& U9 f
多值属性 9 P# |5 Q( o; H5 n6 |8 C. u1 qcss_soup = BeautifulSoup('<p class="body strikeout"></p>') ; t4 _+ F2 I% v$ W& ^css_soup.p['class']" z6 p% H$ C, o# D& m
1 ; i8 D, R- h# ?' ~; U! s5 `2 , }7 A- c: d* o7 W; c['body', 'strikeout']+ P/ V. E) K# n% [) }
14 Y s. h3 G6 G& E9 ]2 _. u d7 z* t
css_soup = BeautifulSoup('<p class="body"></p>') 6 E: q3 Z4 ?) |( ~css_soup.p['class']$ E8 o6 b4 _& v0 p
1; r7 c1 Y4 A2 c4 O! R$ G) e2 b
2 4 o: O. H2 A% k2 |5 R2 M['body']! n4 }% F2 h1 s& ]# J; r
1 " z- @& w6 a- w" x- ~5 { x9 o' N可以遍历的字符串/ s- I$ {8 |0 D8 j
字符串常被包含在tag内.Beautiful Soup用 NavigableString 类来包装tag中的字符串: ) [) J$ g' W% jtag.string5 l# f$ B+ e& q9 P# O, U) R$ e
1 # w' K0 U: X7 `4 N7 w2 m# Z'Extremely bold' 9 }0 ?/ L! W2 Z( p1 1 \7 B) \9 R; Y" {) Otype(tag.string) . S: B0 ~& p7 C1 ; p" x+ ^. j) D; Xbs4.element.NavigableString& j \4 c! V; \4 G# ?8 P4 v
1 * w# r2 v$ ?: {2 B一个 NavigableString 字符串与Python中的Unicode字符串相同, p2 n8 W& k- g" i
并且还支持包含在遍历文档树 和 搜索文档树 中的一些特性." e+ n9 S4 r! J# k6 c
通过 unicode() 方法可以直接将 NavigableString 对象转换成Unicode字符串:* P# D6 S1 ], p
' g2 }5 p/ b1 L; R5 k* W
S) J% H- A) I/ ` 5 `# w' q; R/ ]2 Y. n6 B$ {tag.string.replace_with("No longer bold") 4 ]" z+ N& U4 q: D( A9 Htag9 H: b3 R0 _+ B+ ~9 ~* V: E
1+ }* K: d8 R2 f4 e4 N% ^0 P
2 % v+ B6 s$ i; q% E9 n0 O# c8 R<blockquote id="1">No longer bold</blockquote> 5 C2 y: D8 g4 C1* @: v+ w% D* U0 q c+ m$ `4 H: e9 G
注释及特殊字符串 & J, _5 F* p- Q: ?: I文档的注释部分 7 T9 W& X# u# S( s# q* z$ Smarkup = "<b><!--Hey, buddy. Want to buy a used parser?--></b>" & G' m1 u6 \" W1 Nsoup = BeautifulSoup(markup). I' y% ]4 X9 r2 b. e" m5 V
comment = soup.b.string 5 ~/ D5 V; g: B" |' F: Scomment# h5 o& q% v- j* F
1* |$ _8 V1 m# B
2% [% B& A! f; [! ?' r
3# X. v# d7 I2 w6 ^6 p) W" C
4 E, U" k5 r& U0 v i7 ]
'Hey, buddy. Want to buy a used parser?' 2 |2 |0 {/ y9 ~* k; K9 Z& r Y- T1. x; k/ ^( ^, z( o* J% E
type(comment) : p+ N* @) B2 H% x% P' G, U; Z9 w1 1 E: ]/ l+ y, \# ?# }bs4.element.Comment* Y0 C5 R0 ]2 ~5 m5 Z
1 6 \9 c- b" [5 t! r7 g3 G1 n% _Comment 对象是一个特殊类型的 NavigableString 对象: % ?5 U2 X& _- M( ccomment # K( ?/ y! ~* z$ \! {1" r) I# l& n" ^8 P8 g
'Hey, buddy. Want to buy a used parser?'" |+ w+ f7 o+ Y7 [4 w" {1 H; ?4 |
1 + X/ j; j+ ~: y5 ^0 I7 w m" n6 k/ Z但是当它出现在HTML文档中时, Comment 对象会使用特殊的格式输出:9 m: ]; S2 k" @0 Q/ ?' }
. j8 }1 f1 F! K: k% f' y- i
F3 N- K* u9 O
print(soup.prettify())2 f' o5 z$ {0 `" N7 K: S
1 " U" S8 }, Q' I5 r4 v0 p<html> R/ ^) s% ~, F <body> 5 f. F7 M8 s; P& o8 J+ Y. \- e7 g <b>4 ?1 }6 E( r* `5 f) l3 v2 {
<!--Hey, buddy. Want to buy a used parser?-->* d* f6 T( ^) R
</b>+ Z3 ^) r7 Y) P \/ q
</body> . m w1 |$ Y5 s( z6 S</html>) f- S" y0 Y1 |' h' i
17 a0 S7 f c: N# f3 l6 H4 P0 M
2+ l9 x- Z/ J6 h& v
3* ^* E$ f* m3 [" D9 j( R5 {
4 ) v0 a* G% P. g5 7 }9 P% p& E" h( m8 v6# x1 C/ ?4 y) j9 M
7 3 y" [. N( ^4 j: O0 \/ i, R. kfrom bs4 import CData ) R$ \8 ]* p0 {# Fcdata = CData("A CDATA block")0 Z( i# M& p: N6 q
comment.replace_with(cdata) . i% Y- V2 c2 c) K) d; y+ Fprint(soup.b.prettify())( j- s- i4 N9 V5 q7 y% I0 R; y
1, J) S9 T C! ^) u
2 5 B k9 L7 Z2 C2 w3 7 t" ?. T" g- @" L5 {4) w m) l5 B$ c, i) k' m+ X, n
<b> 9 ~9 d# V2 g9 p; j' T0 W, B; f <![CDATA[A CDATA block]]> 6 l+ w/ G q2 l0 Q" w</b> 9 t; L) T$ d9 W0 e- P1 6 M p' p" R# E' n" `2 . X7 G$ L6 D+ o8 ]3 . D! ~4 N5 i2 F v6 J! b遍历文档树+ Y1 U) j/ g+ H- E8 [
html_doc = """ + @5 @8 T- K( L6 s5 b8 Z<html><head><title>The Dormouse's story</title></head>/ k% e4 G7 F( H5 s* }/ ~/ v
<body> " O9 t" j- q: D$ Y r+ s" J! i<p class="title"><b>The Dormouse's story</b></p>4 T% V ^& }8 X8 E1 f1 e
, s+ K/ K' f# s0 J- |) v
0 z. H( @ G8 G. n- w7 K<p class="story">Once upon a time there were three little sisters; and their names were% l% F/ _ c5 L6 a9 F# w
<a class="sister" id="link1">Elsie</a>, 4 K# Z; i% N3 o8 v<a class="sister" id="link2">Lacie</a> and w7 S% m, K( C1 C+ k: B<a class="sister" id="link3">Tillie</a>;3 a% g, I/ ]) p* k
and they lived at the bottom of a well.</p>- k2 s# R$ g/ J) y. c
, C8 h2 s2 R, f. b
6 R2 c3 `) t5 I. {) J- R, `6 a* p<p class="story">...</p>4 b n* }. `3 R- r
""" ( F. W3 Z: Q; ~1 l12 d# |2 H' X5 o' q
2. S4 |/ r. F3 w! w
3 . b$ W1 S9 A5 y! [& l# v: H9 @+ D4 6 b) H6 j% m- t$ t2 u. p! {: Y5 4 F) Q+ ]. _4 z) _* j* x6 * j! U9 B" I+ x8 O, O( H! [7 - S- }$ z% d) t) R0 b: r5 P: l8 L8! Z) u1 ]1 K$ r: p! a# \8 @( {
9 ( d" r0 U0 m5 K: O% J10 9 ~3 N6 W" E" N1 V5 O$ m11 " d1 A& s. S% C6 j& \! J12 : U6 u/ S( |9 W+ J' E$ \( Z4 M13 + k* \" W& R9 _from bs4 import BeautifulSoup2 X' W! I+ H+ y: e
1$ `' r1 g( t: e9 @" d7 o% d
soup = BeautifulSoup(html_doc,"html.parser") 9 L, V _" G* {/ u, b1 ^5 }+ u% T$ C) T2 n" [" C; f
子节点3 x6 ?$ J" o' y' z5 G
一个Tag可能包含多个字符串或其它的Tag,这些都是这个Tag的子节点.Beautiful Soup提供了许多操作和遍历子节点的属性.0 p0 s2 P: b/ [/ S4 c& v1 B" w. p
% V9 Z% g4 k4 @) S# Q7 G9 z* J- r! `- E7 G
# 如果driver添加了环境变量则不需要设置executable_path # W* g3 P$ D w. Bdriver = webdriver.Chrome()/ {- p; h- z6 h$ a) L! z
* A5 Y' R& f; R* l9 m) `1 Z! Y' M a$ }5 ^0 Y y$ S
# 向一个url发起请求6 i7 j ~: k# X
driver.get("http://www.itcast.cn/")* U @! }9 B3 e, H3 i
. W+ R7 x# R: X
' W' J5 \8 p) b1 k: X0 J' x+ b
# 把网页保存为图片,69版本以上的谷歌浏览器将无法使用截图功能 - M, j. @$ {. W' D8 T" v0 u) P# driver.save_screenshot("itcast.png")+ A' B' {% w' |, N+ |- |( |. H
' X1 r/ D, P2 W6 l) d# U# z0 D/ K7 g% T/ w4 H* Y
print(driver.title) # 打印页面的标题 1 L- e( J& t0 O. n3 b4 ]: s' { J( S6 |- t
$ T% n2 N' H0 G; _3 R: B
# 退出模拟浏览器" V2 \) p* K/ u
driver.quit() # 一定要退出!不退出会有残留进程! 9 ^3 G& G. t+ p0 G1 8 z4 K) ]( B ~; o& i2 8 Q8 B, E% }! Z2 V h37 K. w4 d; X& b) L3 `; E
4 ! f( C2 Z! x3 L/ j- l5 # W% A- s* {! r6 2 l# ^+ g1 R# Z' l9 F0 b0 @ R7 & Y% g9 H* y* I1 I9 X$ f89 E$ D9 a6 U: u- ]0 T, A$ S1 r; R
95 b% j& @9 M* H" ~5 b1 D
10! @ v4 P- w/ P- O5 Y
11 + u B4 ~% l6 b/ p! C$ T4 t12 6 k1 C- k5 Z" ~9 H6 c" F13 ' X3 b0 U4 c9 [4 w/ [141 h0 @; O9 Q) G3 V) W9 w! d' D$ K
15: V/ k- v" N7 G2 e h
168 [4 [7 Q0 d" B
17 7 u& D( p6 ?. A: g: L8 o& X18 - H3 ? U% d8 E, ^5 n2 pphantomjs无界面浏览器的运行效果% O) s- _0 T2 @5 r& C( i
PhantomJS 是一个基于Webkit的“无界面”(headless)浏览器,它会把网站加载到内存并执行页面上的 JavaScript。下载地址:http://phantomjs.org/download.html ! v1 p5 u4 p* @1 n( e+ O9 E" u - y5 ^+ _1 Y7 F / p2 |2 [4 r0 ~9 Tfrom selenium import webdriver - x2 E: J3 F2 y4 W4 }1 K; {9 c
& g+ U" n6 a: T5 j1 j# m
# z% i8 d2 c* p5 o8 k- N+ ]
# 指定driver的绝对路径8 V% _3 g8 s+ x
driver = webdriver.PhantomJS(executable_path='/home/worker/Desktop/driver/phantomjs') 6 [1 l6 l7 {$ ?# driver = webdriver.Chrome(executable_path='/home/worker/Desktop/driver/chromedriver') : W j u- n1 R3 B9 r# X * U/ U: O: b/ z4 H. Q* G! W$ L8 X% H' g
# 向一个url发起请求 1 I: } W; f$ L" W8 A- X4 v- t* Edriver.get("http://www.itcast.cn/") " U T( V* P# G8 i7 ]5 P7 L* y$ i8 _
) o4 n( I- N) k) z5 r( s( X
# 把网页保存为图片 , V& a+ H7 ?4 a, \driver.save_screenshot("itcast.png")6 @4 p$ y" `4 U4 F2 @. f
! [7 d% U6 D" a; P& _6 s
# t6 N) N4 ] t3 s0 X. F
# 退出模拟浏览器/ p# I" Q& a4 f/ x
driver.quit() # 一定要退出!不退出会有残留进程! , q& G0 R8 A [! t7 g$ ?/ F+ f& C1 9 ?3 w$ R% a+ E. I8 T2 % ^5 o5 \- x, J8 h3 a7 z3 ! x0 h. K; n: K: l- @4" o! s" V5 O) d3 J6 Y9 _3 Y6 a" ?* F
5 ; f. M0 P0 E8 D4 _6 : M7 m& n0 D7 j: \76 B# j% V8 y9 ]: D8 B4 {$ C7 s
8 ! P8 a- \' ]2 e9 8 E" m3 n+ k" F% [: g& E10 7 M Q* C/ _; {1 o+ E3 J( K11+ }8 q* ]/ Q, o# O; }7 V
121 U! c" S, h) j: R. r E
13 1 w7 ~8 E6 C8 `/ ^; Q/ V% _2 p' W14: l: `& T! g. X" Z+ I
无头浏览器与有头浏览器的使用场景$ i1 c! J' m) Z$ I