数学建模社区-数学中国

标题: Python爬虫常用库总结 [打印本页]

作者: 杨利霞    时间: 2021-7-8 14:55
标题: Python爬虫常用库总结

( S0 O- M. s: Z1 A% C# m9 c; oPython爬虫常用库总结- b! W% t' U9 w1 C! X3 Y# ^( y
文章目录; t! g* g' a  z4 _
requests
/ ?7 R) r1 ^4 h  Crequests基础- E7 \- r5 M8 w
requests模块发送get请求
# _1 o5 |6 h4 h8 B0 V( B) p! i; a+ cresponse响应对象; L: c+ }! Y8 O$ F! a( V
response.text 和response.content的区别
0 V5 F5 o$ t" e) I+ S解决中文乱码
2 f  `' j! s2 m6 r1 presponse响应对象的其它常用属性或方法
5 d; _* H# B! `! z3 @requests实操1 {9 S! p$ T, j
requests模块发送请求
5 ?! P2 C% ~# o* X3 Z发送带参数的请求; Z$ F* X8 U" q* g* l" V0 K
超时参数timeout的使用* \2 c/ l; [$ \
requests发送post请求的方法- U2 z+ r) \% L3 y
BeautifulSoup% s' N. P  X- M8 g3 K8 M
常见解释器的优缺点
* N/ l/ z. ?4 s: g7 L8 w常用操作
" S4 S5 g# H: a; @! {- O) S几个简单的浏览结构化数据的方法
$ C3 |; W9 g9 h" ~2 D从文档中找到所有的< a>标签的链接' [/ P' m1 F$ b( x0 Y
在文档中获取所有的文字内容4 y) [- D" Y+ w9 L( L8 Z. E; ^
通过标签和属性获取  l* n& ?* P) e9 Q- m3 c( v, [
Name属性# ~$ N3 m9 b0 B
多个属性* T, z9 g; O! w$ g; Q
多值属性
3 G7 j2 L4 x5 m可以遍历的字符串0 }/ l* P7 r/ K; a; G5 q1 L+ @: h
注释及特殊字符串( ?7 K. g& Y* U1 y
遍历文档树6 w7 _$ p& c5 C
子节点
0 L$ L, O. P7 ~  v; `1 `# Bfind_all方法! n$ R( d7 T5 S% H0 ?- _& y
.contents和.children: v& n( N) K8 S! L& y+ N; c
selenium
7 U- m+ k8 `  o* a% V" d6 Dselenium介绍. e  d: y5 @, x' T6 B6 w
chrome浏览器的运行效果
0 Y) n1 U( M1 j: A& Z+ aphantomjs无界面浏览器的运行效果
6 C1 B! W( ]5 n+ Z4 T5 [selenium的作用和工作原理( Z& s  M7 m& o; }5 R' `
selenium的安装以及简单使用
6 n  f2 b/ _# C/ Q. n; A$ \/ Sselenium的简单使用3 ]- Q1 c+ k) ]0 \/ u; J0 N  w1 |
lxml2 @6 a4 _2 ]9 k
记得安装快速第三方库,Python经常需要安装第三方库,原始的下载速度很慢,使用国内的镜像就很快啦# t5 E; z. K( A4 D
# P4 `' v9 G. u) X% P
) F" ?1 Z2 V7 A' E
pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple 包名5 b, y" h( _# O' h
15 Y8 b# G) W+ z( Y! _
快速下载模块2 g6 p$ z0 S; `5 K
: n, p2 m2 L" i, h# N

- l; p9 O% R$ {" @( B官方网址:
) ^# o) _. p) }  V% ~# K1 O3 F$ u) d+ p4 d. e
4 a" P6 O5 M# Z
Requests: 让 HTTP 服务人类  x0 h) I& S& w0 h- O' g& ]% w# B
Beautiful Soup 4.4.0 文档4 h: c2 d" E& w* S" E" V; \" E5 h
Selenium官网
5 G' @; K5 a) l( A6 q* D0 ?lxml - XML and HTML with Python
% \! o" o2 R' d, {" ?, Zrequests
2 G- {+ U  [1 ?- M1 K0 f$ P# }requests官方文档 https://docs.python-requests.org/zh_CN/latest/
1 Z! l( ^/ r" e1 R
/ ]0 s( F. k* `% _6 C

% R: y$ O, l+ k" [' a0 R6 b3 X* J) M; i, b" A/ V
5 v' _; k6 N. |) q4 s+ V2 H/ x& a

" p9 {+ j# E* K7 u8 G' I/ C! J
, C0 w8 r9 p1 c7 s( z& t6 m4 z
进行爬虫,首先要对网址进行请求,这个时候就要用刀我们的requests模块了。requests是python的一个HTTP客户端库,跟urllib,urllib2类似。与urllib,urllib2相比,requests模块语法更加简单。正如他的官网所说:8 r, R3 \. E" `$ R3 `6 G

4 X& H9 ~3 O7 M2 m+ V% Y; L

. [" S7 H- i) v  D: y! x% \% ]+ r$ e! F' A/ B/ ]

2 ?) W4 F5 u* k, Qrequests模块介绍/ z$ s' ?  [3 D! Y

3 t; P8 m% h& W4 W% w

" ~" `5 a! _; Z发送http请求,获取响应数据
9 d7 X$ `7 ?0 R$ q1 y5 f" H& H* Y7 k1 M6 U# ^; d" q2 C
& a9 l* u. \& g
requests模块是一个第三方模块,需要在你的python(虚拟)环境中额外安装
, C; k8 S6 a0 X# T0 D$ F  q) P# E6 [5 U  m
' i  P2 H+ B/ f5 ~* P9 M
pip/pip3 install requests
, u+ z( D1 j% F9 {5 P( O+ U6 a% c7 t+ f+ m- S9 ~: ?' i

8 v  N3 }1 L, ]* prequests基础
/ D  q; k: ^" [! Z4 I* C$ b! Y2 w3 m1 Zrequests模块发送get请求: e$ H3 V3 l  T
#https://beishan.blog.csdn.net/& u* G) {. L6 _9 I1 U
import requests + k; e" @8 k1 g# ]
# 目标url3 T. S) E, E9 L6 Y+ U5 s" Q
url = 'https://www.baidu.com'
3 B; k1 x0 Z- A( X1 b0 ~# 向目标url发送get请求
- i9 L2 I2 i! ~2 ?# eresponse = requests.get(url)
7 v$ s9 N, L) @1 f( F# 打印响应内容3 k% j' g+ J4 `. M" f  G
print(response.text)
, V7 D* t0 b* x* x. C6 L) y1
* D% g& ?* ^0 B' I9 ]+ d0 g24 F! n/ N9 |9 O5 D. K; F# N. Z
33 }+ f) ?! n, l
4
. ~$ j5 E! G, z5
7 ~+ F) B9 n: p+ P% t/ H: |" u( W67 I4 T4 n$ E  }$ C6 l
7! i1 C; C$ V2 V# G  ?, {
8
* D. w1 k# ?& h. ]4 o: yresponse响应对象# L3 I- ^9 u( U/ b* X3 V" n" y/ H
观察上边代码运行结果发现,有好多乱码;这是因为编解码使用的字符集不同早造成的;我们尝试使用下边的办法来解决中文乱码问题
% Q1 l( Q# K3 z! P) @+ z3 j6 i! c5 l$ t+ ^# G4 o
' m9 H+ ^: X0 [* N% U% b
import requests
1 |. `) D* D. v7 vurl = 'https://www.baidu.com' % Y4 z3 p- D* t0 R
# 向目标url发送get请求
% ~# h& N! M+ @9 q9 Zresponse = requests.get(url)/ p+ ?# j% S4 O
# 打印响应内容
$ C- ^/ ~+ B  }* }( ?( F) [8 D0 T9 l# print(response.text)
8 d: w* J: \, N8 F& Cprint(response.content.decode()) # 注意这里!
. o& O0 j4 F. |17 N% X% F- v2 m) j$ @1 q3 e
2
! u( G/ ~6 n7 `( \/ |: d/ F7 Y3 e3
$ K9 h# J# x% n6 b4
1 M7 O7 a! z4 U& k$ W8 v56 e" W; }3 b* e7 O
6
' ]- W/ w: F3 m( H7. K2 I$ f) @2 t0 p) l
response.text是requests模块按照chardet模块推测出的编码字符集进行解码的结果
% }. Z( `3 j/ d% T/ T& ^- [网络传输的字符串都是bytes类型的,所以response.text = response.content.decode(‘推测出的编码字符集’)
3 Z+ ]9 N7 C, Q; k& G, ]我们可以在网页源码中搜索charset,尝试参考该编码字符集,注意存在不准确的情况3 A0 }; H; H2 I+ K7 H( _
response.text 和response.content的区别" s- [3 ^3 d5 z% Q3 Y+ M
response.text
! Z' g, l  p9 ]3 h3 f6 I类型:str; d  ~; V. @) E; X
解码类型: requests模块自动根据HTTP 头部对响应的编码作出有根据的推测,推测的文本编码( f, i6 g/ w7 S2 e6 P+ F
response.content
8 a. K" ^! M- @( y类型:bytes
" Y. R, l9 m+ ?, q! j解码类型: 没有指定
. j2 r* G2 I% k4 a0 C$ S解决中文乱码
0 ]! ?9 e" ?3 s通过对response.content进行decode,来解决中文乱码
* h4 i3 i' G  n# f8 w
. H7 k! |! K* w4 m+ p' |

9 V' q: Y: E2 k. _9 ^response.content.decode() 默认utf-8! @8 W/ Q% J) x, b) s
response.content.decode("GBK")
9 F. j' f% D7 z- H7 q5 \& a' W常见的编码字符集
; O0 X, X4 H3 V4 Q7 \! e& G: |utf-8
9 S( u& P: t" wgbk
% n- d2 A2 n0 q0 ~$ t- V. igb2312
1 t$ R- @' X- dascii (读音:阿斯克码)
5 T+ \/ N& m; i( D) _" jiso-8859-18 X: O" d8 ?. U9 Z, c) ]
response响应对象的其它常用属性或方法- z8 Q" V8 _8 s1 f! l, j
#https://beishan.blog.csdn.net/- a; P$ t& z  f$ I  N  P
# 1.2.3-response其它常用属性* s# ]  a! v8 ~. G
import requests
. g& e* o; e3 }5 T) A
* j$ E$ b  ]  u0 O  [
- M4 n. n. U/ Y% i$ |
# 目标url
& r5 T# V& K8 m% [* s; H+ |6 @url = 'https://www.baidu.com'* B2 H2 @% ?5 m
" Z: N+ T, L/ H6 N; W. @
6 F, N2 G' M4 k
# 向目标url发送get请求. B5 b$ s$ U8 b+ O$ Z
response = requests.get(url)5 x& z2 }2 P2 C
! W3 d& h8 M3 y/ O: \5 y0 ~# J% x
  T- [" i: ~7 J+ U: ~. |
# 打印响应内容
6 ~& A' {& d% f5 \  S. D; l# print(response.text)
! r7 {* ^* {* L8 V3 w1 v, U8 V% y: [# print(response.content.decode())                         # 注意这里!
& s& M+ d' z0 P/ uprint(response.url)                                                        # 打印响应的url- B0 u; I& K/ W3 Z4 |) ~( G
print(response.status_code)                                        # 打印响应的状态码9 Z" r4 ?! q, E4 l
print(response.request.headers)                                # 打印响应对象的请求头9 E# t& C) ?- j; K# J& L
print(response.headers)                                                # 打印响应头( z8 d. F: c2 E/ L) L- W
print(response.request._cookies)                        # 打印请求携带的cookies
7 ^1 ^9 f& j2 _; r! Aprint(response.cookies)                                                # 打印响应中携带的cookies& ]% P% P: r5 y4 T# F& h
17 }2 s, y; F$ L) X6 R( ~$ n
20 |' Z. h& [6 ]
3
9 Q  U) {7 e' y6 [9 h$ q; ?4/ q( U5 R! N* D% J! e
5! O5 Q( b6 l' N4 n
6
! R- R/ y4 h9 f5 R6 g7( T1 n, Z) G5 Y
8* W& @8 `2 a& t
94 {9 B& i) i9 b  q- E, V: m0 M
10
; h8 W" `& h+ m/ q11/ U  _- E4 m% ^, w; ?6 d4 g
120 Z" F+ W- R: t- X0 K. O
13
& d0 t  g2 {1 J! U" D+ ~8 Y14: g/ \/ v2 _- c& k# R/ t
15
. ?$ b+ Q. s" B/ v16
! `( n! m1 f: {175 O4 L8 _. `* m$ x- {* K
18; X( c( e% F0 N+ l2 T8 ]
19& n! h. C3 Z0 o  c" t8 _
requests实操
: |' p+ D% X' r3 W3 grequests模块发送请求
* o- d9 r  k; O/ }发送带header的请求6 O" B% n: A4 k) s  p+ a0 r
* [9 O+ w- d1 u7 Q, ~7 `' _
; C  Q9 R2 q" E
我们先写一个获取百度首页的代码
& g/ z* ?/ q) G" O! T; [' {& p
  D) r* L0 }# V

4 j: m* s/ Q. t: |- rimport requests
' r0 _  \: ]+ k$ _# I0 T9 purl = 'https://www.baidu.com'
! l' c6 g! Z* o' z4 P+ T" |4 Presponse = requests.get(url)
5 h0 `7 [& ?" ?print(response.content.decode())
+ I  s, x* _7 }+ ~# 打印响应对应请求的请求头信息" v7 ?9 n" H$ Z0 B/ {
print(response.request.headers)
* d  v8 Q- B$ a+ n( k3 O$ ^! v1
, I  z/ L- ^+ K4 N2; _" @4 `" b/ ]8 M" `  ^/ m+ i
39 a; C4 P* t5 t
4
2 G. q# [0 O- A, F; H5
5 K; E( _1 ^  D' }6
# C6 {: g6 F$ h1 T8 U  n" ?从浏览器中复制User-Agent,构造headers字典;完成下面的代码后,运行代码查看结果
! h& K$ S+ ?# K" k9 }0 w; Y2 Y. c* E
: S) W5 T0 s' v/ D( H' O

& z4 e, c. i4 j5 R2 {% Limport requests: {3 v; J, N, S7 ^
' k8 `# M" C3 R" l
! G; M  }2 P( m/ ?1 _
url = 'https://www.baidu.com'
6 _9 S" b# w' \( t! ^
' C, d# }4 p$ Q/ Q6 Y
( }! }( W* @$ B9 d1 d( H7 b
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"}  f- _$ `! N9 [1 ]8 x. R  X
( a' p$ R/ [7 X; W* g8 \

) M5 v7 \' h' T/ X% F2 N; v# 在请求头中带上User-Agent,模拟浏览器发送请求  O9 b+ e9 E6 b: q
response = requests.get(url, headers=headers)
+ s0 {9 c7 l2 Z9 M# X6 n& \- _5 L& F* k
) C! e5 l, w, d5 W
print(response.content)" Y; A  t* ]7 E3 B( M
* |; s- J( v" Z+ _
6 @% l7 b1 W: m# x
# 打印请求头信息0 S) u+ w' `+ Y2 B  [+ O8 |
print(response.request.headers)
0 `( I5 d; I$ k0 w# O1
# S" x) a5 p9 @* G8 ~+ V0 {/ e2
' \) i  K3 p  q1 J3 g5 ?2 O3
7 t  M/ W& L, X' `, C# ~) u7 l4
2 v8 C3 i3 ?+ Q) s1 Y6 [5
$ |/ w5 T1 F. _7 r5 b6: V4 P  V$ U. g8 N9 R
7! J! R6 o* ^9 m. ]7 i5 ]* V
8
" `6 m  f) c1 O: \! Q# q+ e97 }& ~# o1 r% k1 x
10
# H6 m" }1 Z' |% E11
3 P4 j/ B$ i9 N/ g* r12
- f) ?7 m! j5 C% [6 [9 ]132 j0 V0 S; j8 C
发送带参数的请求
9 \$ g3 J* s  Z7 i我们在使用百度搜索的时候经常发现url地址中会有一个 ?,那么该问号后边的就是请求参数,又叫做查询字符串
3 Q1 t" P7 l1 A, X, V3 Y& v/ R2 x* D; @5 z. y( H

, r6 g! I; k1 y3 e在url携带参数,直接对含有参数的url发起请求
/ i* Q  _* e8 ^- v/ h. f  h& t& j' C% d
1 o: J) }8 w  X4 i, D: H# @: b
" k1 @: i. D9 u3 W3 I
import requests
/ V9 \$ _# R& g" s* M) E+ C# e( ?% N& h" @

5 ~7 ~& C+ [: z+ ~4 ?headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"}% b1 t! X: {# ]% E

+ d4 T* V0 g6 x% S" r; B
# R0 i  `( K3 A. A5 d1 e
url = 'https://www.baidu.com/s?wd=python'
) g4 S+ W& e# l, {/ A
$ S. \+ N; h: h  m4 n& Y4 j; Z0 _

# s! L' _6 s1 P" k4 wresponse = requests.get(url, headers=headers)
& `" @- v/ N  Y
9 R0 a' K" r. O
! i6 N2 {- E# v1 g+ S
1
" D2 a8 i6 ~1 Z2 D6 z2
/ O. z$ q% @3 p  {, g32 K' V+ X3 m* e! |1 J* q( T
4
4 f. H  z' F4 t. L( q% w9 L5
5 d3 n7 a' n1 u( N: y7 n3 B6% x5 B8 A8 R, [. D3 M" k9 d9 W
7( @: _* o! b: _3 u' F
8
/ N6 E' j7 Y, @  l; Q通过params携带参数字典
, b" K' F3 p" d! P9 I
0 \/ N4 M* H0 N# ^" e

( u( `3 m: j( O8 N​ 1.构建请求参数字典
, V5 F4 v8 f- U
* C7 Y  O5 l8 l& {8 w# Q
3 e! }5 l6 f/ J8 ]. }4 M0 Q7 M7 d; y
​ 2.向接口发送请求的时候带上参数字典,参数字典设置给params. Z! W) E, X+ `4 e8 T+ s4 n3 T- Y

' T/ @5 o5 E$ t5 e$ o; _& K6 {
: t3 o! P0 Y- D" K  E2 i
import requests
& `6 ]8 A( `' _! H
0 d0 E' B; C1 N& W
* I. A7 I  M! A( u$ F& o6 p
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"}/ V0 m$ g' S/ a# f

1 J0 ?: U6 c+ i; Y# W, w6 J
% X) R9 F& L- |
# 这是目标url
6 |5 c* l& x7 Q% o" O  n- S# url = 'https://www.baidu.com/s?wd=python'
$ m4 Z7 E4 l$ E0 B" v8 h3 N3 `  d3 j' Z7 U

3 c2 `; C' C/ G# 最后有没有问号结果都一样
3 o9 K1 [* o, `+ U% furl = 'https://www.baidu.com/s?'' Y! \% e; t+ ~3 h  x9 O
% G7 {$ H$ f5 p4 s4 c/ G$ }$ @
: Z5 Y; J- f. J4 }' t0 p) s
# 请求参数是一个字典 即wd=python
2 v; ]; T/ U( f" b  `$ w2 S, ~kw = {'wd': 'python'}3 s. M- y) X. s, \, y- m( N
! h$ @' U: o; ?2 \! J

7 K* b, f1 V6 u( ]# 带上请求参数发起请求,获取响应: S7 x3 w1 q9 j  M4 P$ V$ S7 k
response = requests.get(url, headers=headers, params=kw)  I7 B' {2 E3 @
4 E- ?6 y$ Y1 @  f/ i) y
' v5 q# p. M& e/ M
print(response.content)* \" [: N% |+ ^( F8 }' Z' _" N
1
( N: V( E0 H. j' T& z" ~, v28 |6 N  M7 q2 g, d
32 C% S' j( ~9 e0 Z0 d+ k
4& K/ D' W# j2 n6 C4 Z1 ^8 F
5
. `- h) y$ I8 K, Q0 H2 b6
1 P$ Y) _9 F% r3 O+ ?1 y7
& z9 n# S) _# l8. E0 i7 L4 J# H1 c4 a6 Q5 X7 }
9
4 i: J* u5 v3 a6 O0 Y2 x1 B+ O9 B105 |- u) [8 `0 R1 M& [2 v8 I
11
5 P+ ^9 A$ I  @( S6 I12' z9 k& e4 g/ j1 j
136 M1 T' P" P/ ^& j3 ?7 Z, {: |! z
146 x2 G! c/ P6 D$ v0 }9 T. s0 d
155 h6 e- i* l# Y7 z, c1 T, O* p  d2 N
16
( Z0 r0 e( J$ m4 c: q3 e6 Q" R8 j5 {8 a17
: O& A/ S; w9 P0 j/ I2 d, s7 j' W从浏览器中复制User-Agent和Cookie: |% ]  M  w/ P5 A' s
浏览器中的请求头字段和值与headers参数中必须一致
2 B" b4 V$ v1 C, l" ^headers请求参数字典中的Cookie键对应的值是字符串
6 d7 \  ~( j) Ximport requests
4 }: ?5 o, n8 l! B
* D5 X% B2 v0 c- o2 A

. S. Y9 ?/ R  Y7 c; P% C  _url = 'https://github.com/USER_NAME'
. V5 k( A2 J. P: B* U" y. I% ~6 S9 N! O5 q

' ^5 d8 _: N# o3 ^# 构造请求头字典5 j5 [5 z0 g0 N4 A" f& }  i+ M
headers = {
+ O& G) I) G4 |& s+ ]* b    # 从浏览器中复制过来的User-Agent
3 g. b8 V. Z3 \3 k4 D    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36',
+ }9 f7 h" A, y    # 从浏览器中复制过来的Cookie
. B: T- {$ ?3 ?    'Cookie': 'xxx这里是复制过来的cookie字符串'  I7 |% |6 a3 }
}
& h' e& ]  A3 p% W+ c
7 |9 ]$ n" P" O4 C. P- h
) r2 ^9 K% P6 n6 ~
# 请求头参数字典中携带cookie字符串% S  b: b0 x! C, y% P5 l
resp = requests.get(url, headers=headers)9 Q6 C* ?' W- h9 M
* _3 v, v# L  g+ c0 O& A

8 j1 V  q2 m  o1 U% jprint(resp.text)
( f5 S8 K& E4 K2 @) F1
2 u7 I: F' Y( u' h( z% D6 E2
# r/ b; _% \; V) G( s3 J' r; q0 N3
- |9 ?$ P9 U: z  C" `6 Z4  B6 p5 t2 I) g9 y6 H
5/ ~, [, j' c; k
6
# C0 ^  o2 t  H! n0 O. N7
; O9 j0 }9 W! T8 `4 @8
1 p1 C7 E7 L2 V& x' w; P* K) @" y: _8 ?92 G6 {& A+ M4 |* B
10
5 w" k" q8 e4 n5 K, I+ T11
1 T! \; m: f8 F4 U$ s& U12+ K/ V+ z5 X2 ?* ~8 r% ~/ H* `8 e
13
: Z3 w4 x6 G& A$ {9 ]- O14
/ f  V4 V; [) U  R% G7 `156 @' [/ J7 T8 r
164 `& b+ f2 ?4 Q5 g( F% V: _8 b6 J
超时参数timeout的使用1 `: r8 O, W; ]' ~: V+ W/ f6 _; b/ F
在平时网上冲浪的过程中,我们经常会遇到网络波动,这个时候,一个请求等了很久可能任然没有结果。$ H( h9 _  a; j6 x' {4 F

  _- E/ ~1 i" x4 V* F1 Z

1 r! z0 N" J7 N: g1 s, }0 o在爬虫中,一个请求很久没有结果,就会让整个项目的效率变得非常低,这个时候我们就需要对请求进行强制要求,让他必须在特定的时间内返回结果,否则就报错。# `  `; L. ]' w" E7 E, `
1 a8 b+ M% [3 p& b# l
, `% T7 a( l6 T3 W
超时参数timeout的使用方法9 v! R% d7 D! j! `3 a" p
% _/ Q" Z* R' F1 t1 N6 p
1 l! }! r2 i' r" `7 A$ r  @
response = requests.get(url, timeout=3)7 o/ _0 ^6 @4 U

+ h+ W1 L  j6 Q8 q
1 b3 r' l9 H  s
timeout=3表示:发送请求后,3秒钟内返回响应,否则就抛出异常
  k; U' @) i5 j: n: y& P( |7 ^8 D8 o
0 p" k$ t3 U+ k: m# w& x
import requests
/ e% _9 Z0 {/ ~* t* u: e% w6 z
6 C. t- E" C( B& ~6 N& S( k/ U

% E( T+ Q8 R: q, R
4 F6 C4 T& ?9 q8 k
2 I! ~. O. ~5 {  E/ S
url = 'https://twitter.com'- {9 @: Z' Z/ X+ {1 L" C' W" m
response = requests.get(url, timeout=3)     # 设置超时时间
5 b: [0 ~* Q8 s. t. `' |# Z+ _' b: z, W+ k/ F+ j

  V, ^. H. p  l+ G$ H17 l3 K" d8 p9 M2 w
24 [5 `* E: g0 R' e" S
39 {9 |( ]. e3 z" T# M2 j: W" \
4! U& x8 v1 j3 p. y1 U( ~6 W
5
5 k3 i/ O* Z/ P1 F  i6 l6, ^) D8 X( G) `! `, T
requests发送post请求的方法
3 \  y1 T" y: N+ lresponse = requests.post(url, data)  C1 Q/ K5 P0 P
2 e6 b9 \: {# E! a; R% f" V

- `6 y8 c5 j4 m5 W0 d% n8 Sdata参数接收一个字典
. r4 ~' P1 M" J9 I3 S$ v+ d0 s- _" U0 e1 y. j7 E

+ H  g" o4 H, arequests模块发送post请求函数的其它参数和发送get请求的参数完全一致  m2 |- h3 X$ y  F
- e4 i; g4 F7 f9 F- @$ d; v5 t  o3 K

0 Z8 F1 q3 h& d! G* z( MBeautifulSoup
+ ?$ @* v2 F0 w% p) C6 o8 P  _BeautifulSoup官方文档 https://beautifulsoup.readthedocs.io/zh_CN/v4.4.0/
3 ^) A' u  c1 M
9 E+ ]  ?& T( C1 p1 y$ m7 H

5 i* M0 Y/ {$ @! BBeautiful Soup 是一个可以从HTML或XML文件中提取数据的Python库.它能够通过你喜欢的转换器实现惯用的文档导航,查找,修改文档的方式.Beautiful Soup会帮你节省数小时甚至数天的工作时间.
* Y5 w0 t% e7 M6 ^/ A% [! O8 }9 g/ L& [* ^) _

) |! p, N3 L) v5 m3 @* n
# d, c1 t" z% H2 E
2 }6 E, ]$ p/ A% Y9 x. P4 O5 C
# p0 Z% C5 S1 g% K3 a9 h2 K

; ?' u& k1 }1 S0 P文章目录
/ E0 Z) A1 t# y4 r$ r+ wrequests
& G' P+ d0 b6 Q+ t9 @: B8 s/ jrequests基础
5 i5 X  F. K8 W0 X. G' i' w' P) Xrequests模块发送get请求
8 B( x6 R$ p6 i" l$ O- iresponse响应对象. R/ @2 q" T0 e: h& y8 r
response.text 和response.content的区别
% [' W" Y% P: y& Y解决中文乱码
- ]* u1 Y0 R' C& v& m, Y9 k4 Iresponse响应对象的其它常用属性或方法
! A: }! E1 }$ n& K+ t5 U/ Vrequests实操
1 n5 p; K) X" M- B3 i- w, orequests模块发送请求( G, ~) J( K7 Z
发送带参数的请求
. d$ t- p7 m- Z( i超时参数timeout的使用
* J+ G. Z* ^2 Lrequests发送post请求的方法
) ^1 t5 _6 n, _: }( CBeautifulSoup
! E( i' H& z. c" }, I: E+ [. y常见解释器的优缺点
. y9 X" h# d. N8 p5 n1 S% n, t) B常用操作
# J/ {. S# ], v+ F" N1 c1 j( L. C几个简单的浏览结构化数据的方法$ c$ s9 w5 B. w- ^/ G
从文档中找到所有的< a>标签的链接
' p/ T5 x  K) [* p( q在文档中获取所有的文字内容
5 s& M; P( C3 w% [' T8 e! [, X4 G通过标签和属性获取
1 V( P1 L6 z; z; DName属性; Y$ Z% I' l: m: k
多个属性
7 p% {: O5 h- w+ y$ k) W! U% A3 U. r多值属性6 B  t0 U) ~: Q
可以遍历的字符串
8 x* I! _4 N: }  D注释及特殊字符串& [' b2 ~7 B: h* O6 y7 [/ k" }
遍历文档树* L, m. s6 Y, j. Q8 z$ G9 n
子节点
: _+ O: F+ _/ Cfind_all方法
' O( k$ o* {, W* L.contents和.children
! l" R$ \8 v5 P5 h% F& [- n6 i! P2 qselenium
" C% J. O6 S$ _6 t% _/ vselenium介绍
. B7 ]' T9 v, y( Y9 Xchrome浏览器的运行效果
. H9 d# u  b1 z! H" O+ G- T) U/ iphantomjs无界面浏览器的运行效果
5 F) ^4 Z2 j. Oselenium的作用和工作原理
% u+ Z3 I, x) @. p0 z; H; rselenium的安装以及简单使用
- k2 x9 u3 s  ~. e! w8 m/ G$ ?  ^, N$ vselenium的简单使用
, C1 B: t; m: _, c* L  K+ plxml
: ~7 V: |6 |3 ?9 N( h" t常见解释器的优缺点
) d& ~$ X/ ]9 H. F4 F7 E3 h  F8 q/ N8 G% k/ g3 M) ~

% \! o7 Z2 q! Y, ~0 {
# [" \6 }( C; y: z; ~

3 A% X& t" F( V7 _+ ]% `常用操作
' f# p. M! V5 R安装方法5 @8 ^% D/ P1 ~  }

* 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

, i1 O& C7 {5 x5 Dtag中包含的字符串不能编辑,但是可以被替换成其他的字符串,用replace_with()方法/ u. ~; l- p! X( u) l+ J

  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

3 ?0 u/ z5 @, }8 |

4 }1 X/ }6 [2 t  a+ N  V4 csoup.head! E1 b% s& Y9 u0 w# r
1) I8 X7 [  U4 |* S) [
<head><title>The Dormouse's story</title></head>
: B" O: w$ S: B; m+ F/ ^1
$ Z$ i+ N- U2 {$ ]6 Z  osoup.title% P1 C; s% K  a  g* v
1
$ L4 \- w" \+ P' \/ K( f<title>The Dormouse's story</title>: X% B/ q  K& o4 T% Y5 ]" }
1
4 k4 B/ I. f  g+ F( |0 Z0 S9 C这是个获取tag的小窍门,可以在文档树的tag中多次调用这个方法.下面的代码可以获取标签中的第一个标签:
$ i% [) z6 b! C+ l! M3 r% Z6 ?/ s8 C& X1 z( l1 ?: S% Q+ Q
! p* ~; ^' D' D( v
soup.body.b
( ]5 X3 q# ?6 k* t6 ^$ j- \1 S+ H' H6 q1) M6 Y; ^4 R9 q' b: }
<b>The Dormouse's story</b>
; E# P: p1 B! ^7 l) A+ H& |1/ |* E8 u3 d! u- v( s
通过点取属性的方式只能获得当前名字的第一个tag:4 ~% K3 a, k+ ~5 M  C* X

7 n; ?! H) N3 Q! i, J! \  y1 P

2 N" N) g/ |3 d% m% U5 |soup.a$ M9 Z2 P4 p  l2 @! y, K1 L
1
/ t9 C3 z5 T# n/ j. h7 e) U<a class="sister"  id="link1">Elsie</a>9 |* A/ t# [; w8 M$ u
13 p' F+ e6 q" Z3 k8 X0 Z+ c' W
find_all方法
/ h! N& P# R% ?& O如果想要得到所有的标签,或是通过名字得到比一个tag更多的内容的时候,就需要用到 Searching the tree 中描述的方法,比如: find_all()
7 w4 v' P1 G! E! A6 V! I9 ^- A1 k1 \- M; |5 ]
% b# y) g2 @  ~5 ?: Z
soup.find_all("a")
# _9 m/ l2 M. D1
, K& S7 d2 ]: }2 F[<a class="sister"  id="link1">Elsie</a>,
  [) j. c4 {  n8 x1 j <a class="sister"  id="link2">Lacie</a>,
* Q9 u8 `6 H  c, \) q <a class="sister"  id="link3">Tillie</a>]8 v8 x0 Z* ^, z8 p' ^# `
1
2 B% O5 t$ R1 E$ \. O+ |2) K% e8 X. `+ u* ?7 N" l) R
3+ i/ ~. e. I# |
.contents和.children
* @- n7 ~! a7 B  ~% n  z$ h; E* |6 |1 xhead_tag = soup.head
2 _+ o" ~) L* whead_tag
; p& {9 p- j$ c1 ?& ?* w1
# J) n- g4 N9 z+ B& S22 }0 t! |' ?4 O% y- i$ P4 _
<head><title>The Dormouse's story</title></head>
! N9 T) ~. o4 H/ Q0 F) M1
1 u2 j% f) [9 H; L% bhead_tag.contents% k& q- s  r6 i2 W1 Q7 R9 ?; j
1+ S) q8 D4 w9 @
[<title>The Dormouse's story</title>]; a6 B" u9 N% ~: M
1
/ S+ ^$ U# H# h, i( E0 m; ?head_tag.contents[0]
/ Q$ b5 h" B" y! ]  I& b6 B: D1
! ~7 D# w1 [8 S+ T: s/ Z0 N<title>The Dormouse's story</title>
+ S0 v% D+ f' d1" J: i8 J8 M3 q+ u2 [8 E: y* ^; S
head_tag.contents[0].contents9 v5 N+ ]4 C: s1 m3 H. D
1: D' V& Q* w  ^( ?
["The Dormouse's story"]& L% N: j1 {$ n5 `
1/ |9 `: R  V" U! `  o5 G7 b
selenium  |! h: k3 d7 S
# j+ u; q0 z4 l- s
$ @+ G+ v2 ^2 b" Y( x$ S8 r
! j% O$ T6 a* v3 @2 n# g* M$ i) s
# I1 A/ l# E# n$ h* p  |
selenium官方文档 https://www.selenium.dev/selenium/docs/api/py/api.html8 Z; V6 O2 k) X0 {# y5 u
8 |" z& a0 R* K' ~2 h) a$ D

2 m/ l# a- e& v* Z1 gselenium介绍0 N1 v0 f, r1 B* x% R' S& W7 z
chrome浏览器的运行效果2 t+ [. B4 [4 Y3 H; J
在下载好chromedriver以及安装好selenium模块后,执行下列代码并观察运行的过程
. A7 `; w3 i. ^, ]$ B2 C5 u' _) C' Z7 H1 [) W9 V

, u( b! s; B2 h: |& Cfrom selenium import webdriver # u# v0 {! x) _8 W% `7 B9 A

- ?, I% ~  x1 Z) ^

( i- L6 [3 s. p1 k$ ?7 }9 h7 O# 如果driver没有添加到了环境变量,则需要将driver的绝对路径赋值给executable_path参数
' m0 X- C2 M, v+ A" e& i; v# driver = webdriver.Chrome(executable_path='/home/worker/Desktop/driver/chromedriver')  H$ C& c3 b0 q9 K

% 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/ z
4 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

9 Y4 U+ E! m7 @0 f+ @" m& d

' c$ e4 ]) I3 G9 i5 E, g通常在开发过程中我们需要查看运行过程中的各种情况所以通常使用有头浏览器
* T) U0 q  q" L1 L8 j8 w& {* g0 ^在项目完成进行部署的时候,通常平台采用的系统都是服务器版的操作系统,服务器版的操作系统必须使用无头浏览器才能正常运行
0 ~/ D" ~6 ^1 t5 Nselenium的作用和工作原理
4 E" h" D6 R1 G3 |- {利用浏览器原生的API,封装成一套更加面向对象的Selenium WebDriver API,直接操作浏览器页面里的元素,甚至操作浏览器本身(截屏,窗口大小,启动,关闭,安装插件,配置证书之类的)3 e1 ^& u" @# o

" l* t% N4 Q3 u2 |: {* {

$ o/ A, n* u" |" F* x5 @selenium的安装以及简单使用2 k4 Z4 [$ J3 t9 o2 w( ^
以edge浏览器为例 参见这个blog哦,驱动chrome浏览器同理6 H0 T+ w; j, M  j5 L% U
selenium驱动edge浏览器
2 L3 z! g! T# J7 ]# C% d! U0 @5 C6 e9 m) ?$ W4 h6 j# m" M0 z

2 j4 d. D; l, H! Zchromedriver环境的配置
: \) ]: e8 L, uwindows环境下需要将 chromedriver.exe 所在的目录设置为path环境变量中的路径
, A2 L( i" h6 d, K6 elinux/mac环境下,将 chromedriver 所在的目录设置到系统的PATH环境值中# B* I+ {( T/ K5 m+ m* T8 b: h
selenium的简单使用: l5 B/ c; `9 U$ d
接下来我们就通过代码来模拟百度搜索
) m( O0 H' Y( o+ c& ?' |7 y3 R' I/ S7 O* x+ K+ d! {" e7 z9 h$ @

; J/ G; w8 @5 D' \import time1 b! z8 R% ?/ P% Q9 g8 H
from selenium import webdriver9 f) ^6 G3 s( B1 {, t$ u: }

* w: D* j5 _& l7 @4 _+ Y# i

- H7 L2 V+ H. }$ e7 |% a# 通过指定chromedriver的路径来实例化driver对象,chromedriver放在当前目录。2 C( o) Y, T' ?: f% R# X9 i
# driver = webdriver.Chrome(executable_path='./chromedriver')- G7 t$ d3 V: P7 c2 q. |5 k
# chromedriver已经添加环境变量6 \/ C" Q: w7 Q& R; t2 ^5 n
driver = webdriver.Chrome()
) T, N$ f; P* U, W( |+ u0 |
: D/ m1 A: D/ R5 L$ k- R, J# r
1 g! ~0 q/ c1 y3 G  H# D
# 控制浏览器访问url地址, ]7 {/ L" V4 x" \; R  }
driver.get("https://www.baidu.com/")
' a& E8 F% o3 @4 e, R9 W( m5 g- ]( Q  V0 u) ^; d' g
( ?  {' Y8 A" P+ N6 r) e5 Z$ o
# 在百度搜索框中搜索'python'. `; b) t1 ]/ d+ A6 S# y
driver.find_element_by_id('kw').send_keys('python')
9 v6 J" X4 g$ L  U# 点击'百度搜索'% R* |: G- ~3 t) J, m9 g
driver.find_element_by_id('su').click()' i  ]5 U- x2 y% c( h) F# H! e7 |

) j+ R( j. r6 I1 ~& p
8 y' _* C# b+ w$ F1 I0 L5 c' ]
time.sleep(6)+ j4 h/ P, p8 Z2 G0 r! ~
# 退出浏览器
. W4 w2 r* k0 odriver.quit()/ J5 G7 R5 K" C# {# w- m1 {6 ^& n
15 j/ g; O1 \* |! v
2
. R+ u5 D  l8 L6 Y& z* F3/ s9 n. `  r" P  f/ M8 i
4& Z% @& y4 |% c3 ~* \* J
5
  J' W) b3 h$ N" G9 J) c6
; l  ]4 G3 K2 L9 V) J7; @7 e' @, G; I2 x* X7 f- p/ p+ O! v* x
86 w- N4 l) ?9 x2 L
9
1 g4 g$ D% S; \6 o$ L5 W" d$ z10! o( n$ \0 N+ i  U' I2 G5 p
11
3 L  L! r( |5 k( a2 n( h/ t12
" x# t! h- l: q' U  U* i2 R' ~13# T8 g8 P; L- \5 M' q
14
. y( A$ E! k  A15( B* x$ Y+ y4 o
16
% u" D1 v  A2 g8 n17
: m) {9 K* D( B3 G18
8 F, Y1 s$ q: A. N/ S/ \193 \' I8 D) h  i9 N
webdriver.Chrome(executable_path='./chromedriver')中executable参数指定的是下载好的chromedriver文件的路径
& E6 Q0 _! T+ {5 I! s" hdriver.find_element_by_id('kw').send_keys('python')定位id属性值是’kw’的标签,并向其中输入字符串’python’
3 {% P/ v+ O4 p' ?: {driver.find_element_by_id('su').click()定位id属性值是su的标签,并点击: n3 T2 N2 v& N4 d. w: g! o" U
click函数作用是:触发标签的js的click事件3 P; R# _8 H( n( a  O/ E8 x
值是’kw’的标签,并向其中输入字符串’python’
+ I+ O4 R4 I/ m: k4 {" D3 y8 m1 e* G1 F- f, t1 n, x) k
" ^- Y" k1 i) i' H
driver.find_element_by_id('su').click()定位id属性值是su的标签,并点击
/ `2 z% r6 [- z) qclick函数作用是:触发标签的js的click事件( T. M9 G: F, V- p/ A3 T% W
使用xpath来提取数据,爬取数据的简单语法。+ V* I4 c8 T' A0 O/ [: W5 o

) t; U3 E: o8 Z. r0 i

2 I# U. q* H$ X7 xlxml
3 R: q( Q1 j  i* H; ^
7 J/ F4 |& |2 G9 L$ A) K0 @# t

8 ^% [6 v5 t2 Z) x1 R7 B4 w
& l9 ?* |8 [' D0 \
2 x8 f* o- f1 z# F
requests官方文档 https://lxml.de/2 K9 d, Y7 p7 i  L
3 o& @6 W4 X. ^$ K# Q9 B

  a: U2 ?& e, M/ wpip install lxml( a# T, v! f( M. t& \: U/ E
1
( D" L+ A; [* U( C, }( [导入模块8 B4 S& t# O2 n5 s
from lxml import etree
4 w& t$ D4 K- Z& O' d9 r1
' D+ J% h% C. w& S7 p: R" L利用xpath获取text或者href内容
% i! p6 I- S" ]5 w/li/a/@href 这样取的应该是href的内容
- L' L* G3 T% `' I( K. J/li/a/text() 这样取得是text内容+ Q4 B/ g* j) c  l/ O
1
$ G4 a* I4 y2 _8 B2
- N1 T" ]. ^+ q) d& uetree的使用
1 e  H8 @; E4 H, Eh=etree.HTML(response.text)#response.text是网页的源码
* H* x* }4 V+ Fh.xpath('//img')  #寻找所有的img结点,
2 N4 X4 i: l; ^- g8 [3 l7 F  Nh.xpath('//div').xpath('.//img')#寻找所有div下的所有img结点
6 i3 W5 K( ?) [% [/ |1 @13 u3 O4 x; v0 D1 Z3 @
2
  n/ r: q& S* _) b3
7 D+ u4 ^/ ]# y4 y4 m* ~# Oxpath的语法: F" p2 a: i0 y: S8 b
符号
7 ]9 U; u7 R/ v  \) c3 R' vXPath 使用路径表达式在 XML 文档中选取节点。节点是通过沿着路径或者 step 来选取的。
1 K  ~8 w3 N7 U- J, l2 B) e+ E9 Q  ]3 K9 h' e/ T% e0 x( q- {

% a: }# p) x9 m0 H, Z* B* _% W3 V表达式        描述
  J1 @3 k+ O3 x# M/        从根节点选取- R+ B. V3 d" \6 M) o# y
//        从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。4 _2 x% @  k5 M. h
.        选取当前节点。4 V8 z' |$ n4 W6 d6 b  J
. .        选取当前节点的父节点。
2 m' `/ m! F7 z- Z8 t# o9 D@        选取属性。$ I; B. W  N) b0 ?4 Z) F( d
|        在两个中结点中选择* q3 e. `' q7 X% a0 ~+ m5 H7 T$ y
()        用()来包含|
+ z& J- K$ l- w( d9 c' ^*        包含所有元素$ o6 y5 k3 c# t" C! p; n: t
not        取反; X3 S( d0 {2 Y  f2 S' c
实例' T0 T5 ~  z: c5 X

( G9 b+ I$ M3 a% [
  u. n7 z; S- m& E% F+ X" L
路径表达式        结果
/ M8 Z/ B1 F: Z: |% |3 ]5 vbookstore        选取 bookstore 元素的所有子节点。
  M; }$ Z- x1 Y" U% ~/bookstore        选取根元素 bookstore。注释:假如路径起始于正斜杠( / ),则此路径始终代表到某元素的绝对路径!
! {7 e5 T" e! E( p* `& x5 f7 d$ Dbookstore/book        选取属于 bookstore 的子元素的所有 book 元素。# _' t( Y5 O) J+ r
//book        选取所有 book 子元素,而不管它们在文档中的位置。% {) N2 v" c, p3 o- L# l' A
bookstore//book        选择属于 bookstore 元素的后代的所有 book 元素,而不管它们位于 bookstore 之下的什么位置。
) s  P8 O/ U: _1 K  P1 }+ J9 X, f# i% p//@lang        选取名为 lang 的所有属性。
! `8 N" m$ f. y1 V* i6 E//*[@class]        选取带有class属性的所有元素2 P* R+ o' R4 o1 `  j
//div[@*]        匹配任意属性的div元素( ]& L, _, ?* S4 N# p
//a[not(@class)]        匹配没有class属性的a元素
7 f1 K8 M- Q8 A8 j* k4 o+ o谓语2 s# ]# m" U! j/ \
带谓语的路径表达式" @, w) s8 }( Y4 M0 t+ o

7 D( x. s/ |* S- d6 \
9 b) R4 c& N7 z5 l' V# x& z& q
路径表达式        结果5 u/ d. m9 E0 J  c& w
/bookstore/book[1]        选取属于 bookstore 子元素的第一个 book 元素。
7 m1 q2 m9 q9 j' f/bookstore/book[last()]        选取属于 bookstore 子元素的最后一个 book 元素。  ~6 y5 ^6 I+ A4 {  ^/ @
/bookstore/book[last()-1]        选取属于 bookstore 子元素的倒数第二个 book 元素。. t) {5 j% W8 N. X! b; w# R: R/ b
/bookstore/book[position()< 3]        选取最前面的两个属于 bookstore 元素的子元素的 book 元素。
8 O4 [8 M# M+ R8 k% j  o$ s- d//title[@lang]        选取所有拥有名为 lang 的属性的 title 元素。
5 w* z+ D5 P& N- d5 |//title[@lang=‘eng’]        选取所有 title 元素,且这些元素拥有值为 eng 的 lang 属性。+ R0 i1 G  R8 x+ a7 Z5 j$ ?3 {3 X
/bookstore/book[price>35.00]        选取 bookstore 元素的所有 book 元素,且其中的 price 元素的值须大于 35.00。! Z; N7 O4 a; b
/bookstore/book[price>35.00]/title        选取 bookstore 元素中的 book 元素的所有 title 元素,且其中的 price 元素的值须大于 35.00。6 H- C( R1 p# ?5 t4 {7 p
————————————————+ ?, h' @. F' G2 k. d
版权声明:本文为CSDN博主「北山啦」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。3 Q) t( U* v+ H6 {9 Q1 C
原文链接:https://blog.csdn.net/qq_45176548/article/details/118187068
) g/ a9 C  p* v& Q, L( V
; W! [3 l/ n5 ?; f) j  z
6 B% o5 l% R9 ~# P' i3 s
作者: 1051373629    时间: 2021-8-15 17:54
厉害厉害厉害厉害厉害厉害   感谢
; K6 u" w6 H$ A  S% x$ _& U6 a% p+ f
作者: 1051373629    时间: 2021-8-17 17:08
厉害厉害厉害厉害厉害厉害  
1 e8 R2 z$ D; ^6 q




欢迎光临 数学建模社区-数学中国 (http://www.madio.net/) Powered by Discuz! X2.5