; q, b6 H6 E2 o. x
9 k0 e- G8 ?5 s) y7 F. i+ V
# z& E- ^9 E. O手把手带你5分钟搞定爬虫(聚焦爬虫)
, z/ x/ J, z+ V
7 s/ H3 V4 g( |2 p: @爬虫的原理1 L6 A/ c6 h \9 m, m# s7 m
& P- b$ f6 e" j9 O" T对于很多刚入门的小伙伴来说,了解爬虫的工作原理是非常重要的。只有当你了解了它的原理,你才能结合实际情况写出对应的爬虫。下面我将以通俗的语言方式来介绍爬虫
' V$ ?* C8 L2 g& R把互联网比作一张巨大的网,那爬虫就是这张网上面的一只小蜘蛛,在互联网上面有很多的数据,各种各样的数据,我们可以把这些数据比作是被网粘住的小昆虫等。爬虫的作用就是去找到自己喜欢的口味,然后把它带回家,可以储存起来,也可以马上享受美味
0 J# K1 H# I2 U2 K, |3 G所以说爬虫分为三部曲:! A0 X/ o9 o. O- y
1、明确你自己喜欢什么口味(制定需求,明确你需要的数据类型)
- u5 W5 `) |# h5 }2、去找到你喜欢的食物的位置(得到目标地址)2 a, [ o- I$ [3 U; i4 x2 z
3、把食物搬回家去(解析网址,获取数据,存储数据)& l/ C$ v1 b5 Y, x7 y8 z; V
它的整个流程就是:(简单的通过request请求获取数据并解析获取数据。以下都是固定化格式,可以照搬)" B% O6 ]( s% Y! Z
a、导入需要的库 requests% T8 S0 M6 V6 h, x! i6 w+ J% ]
' ^- }. ^# }/ g- ?5 `; ^! ?" u* aimport requests- y; K+ A3 _3 n1 D8 f
1
5 |& k0 m3 ^% S( O9 ~5 ub、明确你要获取数据的网址(这里以百度网站为例)2 `, K, G( B( V0 |- [
2 R# I: I9 o0 _, K0 ]) G; k D
url = 'http://www.baidu.com'
4 ~/ y5 b5 g g( F3 Y1
7 b; `/ h. J; p* x% ~4 k7 ?) Vc、创建请求
% j! g7 G& @3 Y5 H2 F+ N9 E
3 h" K1 q. Y( U5 U2 Q9 lresponse = requests.get(url)8 g9 J# g F3 x& Y& ]
1
6 N0 h! o, @6 e- `, xd、测试请求是否成功(在实际应用中用的比较少,正常情况都是可以请求成功的,网站对于访问外部信息的爬虫是不会做出限制的)& N8 _2 i( T* D8 N2 ~
7 @9 {, E' S2 n6 eprint("状态码:",response.status_code)6 q- I) w6 A0 p) L
1
& a1 K- q) T& r I4 G当状态码为“200”时表示请求成功;如果不是“200”,很大可能就是你的目标url写错了,核对一下url
, i1 F) b, ?4 W) q* Se、得到网页源码
) @" }3 u/ _1 ?4 R k& Z: E, n/ k5 b# {) T
print("内容:",response.text): }0 ]8 Y# a+ w- K! R9 n
1
( E* X9 v% ^/ K |response.content是response.text的二进制形式
Z3 P$ S G0 q% ^' q* ?0 x
: ^) `# w) z* z3 `5 W7 n2 @至此,一个简单的爬虫请求就算是完成了。
) o. B+ B7 T4 R+ X/ Z" a当我们请求目标url并获取到response后,就需要进行下一步:解析网址并获取数据
9 b0 Z: [. D0 ]; h9 u, a! nf、解析网址
5 s. @3 x) d8 [% s! ^. D. Z1 x刚开始学习阶段,我们最常用的两种方法,一种是BeautifulSoup,一种是etree。其中BeautifulSoup是最常用的。以下将对两种方法分别进行介绍 (以下方法均为测试用例,实际当中应结合自己的实际情况选择应用)5 v/ `% U, Q. q
BeautifulSoup:
; @0 x. w( X, o* A& k% c首先导入需要的库文件:
0 t" P' j* @5 q* ^" g% }
4 K- Y' L7 J- e$ i8 E3 K/ K; Ufrom bs4 import BeautifulSoup
9 D7 }! f' g0 |& V: u/ a! R1
: @ \( @0 l: E然后对上面请求得到的response进行解析:
0 e! m ~1 o4 y4 R; ~
: d% o3 D4 J. e. Nhtml = respone.content.decode('utf-8')
. Y6 L L! D. B- K& Osoup = BeautifulSoup(html, 'lxml')
, a, M3 J" _$ ~/ T) X( I7 P+ S) _5 H1
. M4 Y9 B5 h5 f3 k. x$ }2
! a7 g+ v9 j8 W* H以下是BeautifulSoup中比较常见的方法,有兴趣的朋友可以去一一测试:
1 g: D5 I/ d; I6 ]7 c |# 获取head标签
1 C% J: I! M1 O& Y$ j" _1 {print('head标签内容', soup.head)& c- |6 ]1 j5 v+ s5 ^
# 获取title标签
) g6 D# a1 P+ ^$ ?print('head标签内容', soup.title)
3 e' L- O1 H) d" X( U# 获取body里面的img内容
7 s, F% f; R" X5 x% Q4 _' E8 ?% U# 通过点号的方式只能获取 下属第一个标签(即获取soup下面第一个body里的第一个a标签的第一个img标签)/ w. f! e+ Z1 W# a9 S; R
print(soup.body.a.img) 2 \9 g* J3 i# b+ w
# 获取所有的img标签 使用find_all()搜索整个soup对象
8 h' i8 D5 \4 r- [& _* z$ Aprint("获取所有的img标签", soup.find_all('img'))7 `" q% W, G4 k4 c V
# 获取标签中的属性src地址1 B$ b& U6 ~5 P/ v B
print("获取标签中的属性src地址:", soup.img['src']) # 注意:英文点号都只能获取第一个值
) E5 k% j- ^1 w$ e/ q
; ]6 ?' W1 Y" j, E9 f' P9 M- f) Q+ I2 f- O5 ~8 h; I, B
# 获取soup的name
8 e3 v& T7 S$ H2 @0 `print("获取soup的name:",soup.name)! p3 l" s: [) [% U' C. U" S
# 获取a标签名
) I! ?6 M# M9 y9 P' G, A3 F6 oprint("a标签名字:",soup.a.name). _) I; A* O0 f' j" k2 z6 q7 h: C
tag = soup.a: Q" l8 c+ l" C) ?8 @5 h
print(tag)& Z" _" }* d4 L2 n2 a
# 修改操作 a标签改为b标签
$ p, ~1 C$ X4 L# T) P/ f: q Ztag.name = 'b' # 赋值后<a> </a>变为<b> </b>( O! [8 Y+ e6 m& E$ u5 ~: s5 C7 ^
# 输出修改后的内容. P' P. L3 R6 ~3 j0 {+ S% s2 G( _
print("输出修改后的内容:",tag)* U- }0 K& j. l) ^0 x# b% _8 d/ V
# 获取全部的属性 attrs/ f: `3 ~' s; X7 J. \
print("tag对象的全部属性:",tag.attrs) # 只能得到当前标签的属性(不包含下属标签)
, o1 B, S+ V: j" p2 L) w* J( l% Y& a8 U# `" \) ?2 z
# 获取属性的class的值 根据键取值1 H* n0 b3 n; t5 ]0 g
print("获取属性的class的值:",tag["class"])
) F# W" V) q/ b
3 K; D6 j8 h( n* h4 t/ E% N3 @8 ^5 |& Z! Q0 ?
. t6 x1 j6 b9 b% t1 m
7 |; S1 c$ C& M' W0 U% `"""针对获取的标签属性可以进行增删改查"""$ {& T B) J# ]
# 修改' e! ^3 Q9 v( p/ N m
tag["class"] = "Logo"0 {2 _0 x* o4 w0 F6 y& i
print("tag对象的全部属性:",tag.attrs)$ h) i l3 d6 x8 S6 ]
# 输出属性
+ M& j( `( w0 @& ]- yprint("获取属性的class的值:",tag["class"])5 c( J$ s9 C8 O# ]4 h
# 新增 属性id 赋值logo
1 V7 ^% Z' \6 ^9 {' g* v, ytag['id'] = 'logo'
7 ^0 B8 p- a2 r2 |) _' Oprint("tag对象的全部属性:",tag.attrs)# l9 ]% J; N: ]1 O7 n1 G4 _* w
# 删除属性5 E0 m( S1 B& J* {, T/ m3 B$ w
del tag['class']
* J$ C) D* @) Tprint("tag对象的全部属性:",tag.attrs) M7 j2 i' L) x; b. N7 I& i
8 Z( e1 K$ I3 a3 ^% |+ t
+ N0 Q: V# e! J7 |5 w
+ H6 B( m' y) J"""针对内容操作 (只针对标签,对属性无效)"""
- q8 K1 G0 v" @! M+ D! l3 Q# 获取title标签内容
6 N* V% c5 p# |6 G( r0 A4 ctag = soup.title; k1 p1 M- |" b# ]; }
# 获取title字符串 z) q* m3 c c- K; z
print("tag对象所包含的字符串:", tag.string)4 J9 X7 @$ ^* w9 z) \
print("类型:", type(tag.string))" l V& Z& `2 F9 \) L
# 替换内容, Z* @7 U( _6 ~* e o) j
tag.string.replace_with("你好"), E+ S, F6 V, `& ^3 B
print(tag.string)% ]) l" v* Y6 A9 O- D& ?
# <!-- 学科建设 -->
( ~2 u! V1 M' f2 Y# 获取注释文本内容/ i/ F& C! U$ g, e% q# t, g) f) O
markup = '<b><!-- 学科建设 --></c>'% ^" l$ R( O: s. d6 e! Y5 S& ^
soup_comment = BeautifulSoup(markup, 'lxml')
$ K! p" w [. e: R! B: Tcomment = soup_comment.b.string # 以markup第一个标签作为关键进行搜索( r9 \- \7 L* }/ A9 t! m
print("获取注释文本内容:", comment)
; x+ o+ v' W1 @/ |! R# get_text()' `' U7 U' Z/ _! F; r
print("title的全部节点:",soup.find_all("title"))
6 v4 \* ]0 U% T$ e$ y. u) oprint("title的内容:",soup.title.get_text())
6 e9 \. q" C# ]"""查找ul元素标签"""# S: T3 j. @ v1 t( o) l
# class是关键名,匹配时后面必须加下划线_$ q' q* J7 q! K2 M3 S
# tag = soup.find_all("ul", class_='menu') # 按照css类名完全匹配
) T5 e0 a7 i9 v: |+ O' }( _% a# print(tag), D( D; ]9 o q7 \4 m3 ~9 n
tag = soup.find_all("ul", id='menu') # 按照css类名完全匹配; s* F: p0 X5 `1 X# h) g1 w
print(tag)+ \9 g( {$ c2 P9 k) e9 ~1 w
# 查找名称为a元素的标签内容; ?$ |+ n& f- P
taga = soup.find_all('a')/ o/ B1 U6 N- M" `
for tag in taga:1 z0 e+ {. ]2 I/ f
print("查找名称为a元素的标签内容:", tag.string)+ Y/ a/ X1 y9 i% k4 v
# get 获取属性信息 获取整个页面的img标签图片和src属性值
) C# M i6 E& O( o4 ntagings = soup.find_all('img'): S' d8 w( t w( j$ A- ~ ]
print(tagings), R x3 D5 M2 [9 l7 x1 \
# 循环输出图片地址) D9 [2 a; m% P+ @8 t9 g/ N3 [
for imgsrc in tagings:
2 e* S& p2 ]) o" S) V5 O" r print(url+imgsrc.get('src'))' F( G: g0 R% X/ u, o
2 K# R" |; S- a; r1 Y9 M""""属性只能做为参数,所有的操作只能针对标签"""" S/ Q: e9 Q1 J2 H
8 Y! Z4 s6 r3 U: S" e
etree方法:
# |( j1 ?; m5 [% G6 k2 }导入库文件:# s& t4 A& v& P# i' F
0 a4 _" f& g8 ~' q: o+ e3 N% [
from lxml import etree. i3 L- E5 v7 j. q; u
1: ~" [4 i6 [( N3 P! R' B+ y
解析得到的response对象;/ x; C, e: J: y8 g8 g) a
4 z# O$ R5 E! z
# 设置response的字符集
' f; D( W! z8 M' t) C4 Phtml = req.content.decode('utf-8')9 ]4 Q8 n% K6 {
# 解析对象6 q/ \- _* w7 A$ I# X) a8 L
html = etree.HTML(html,parser=etree.HTMLParser(encoding='utf-8'))
- C( N0 J" @- T) W2 F- ~. Y
- ~4 d* X6 x# R) G$ q& V常用的方法:
$ S: t! N6 M5 P: r& ]# A8 o* x, q2 X, V+ g! X9 Y& S _
' I# t0 b- g* b( l l L- h9 B- W# 定位head节点(元素)+ g9 S' }) p2 ]: h$ z$ k0 D+ y
result1 = html.xpath('head')
1 O" P n* }! K1 o0 oprint(result1)! p ]5 |( u, }! r& q
# 定位title节点
) }0 B9 x& c( O; C7 S0 Mresult2 = html.xpath('/html/head/title')
; q0 I& ~$ q# Y+ F$ {/ C; o8 nprint(result2)
. S1 y5 e9 }$ D# C/ y# 搜索节点 title节点+ k% ~. I. J2 d }# S. f# N; Z
result3 = html.xpath('//title')
! t" C* W( \5 x* j) A+ r8 fprint(result3)
2 a; V! U \+ z) l% W m# 搜索title内容 text()得到文本信息
- |, K7 E ?& {3 u" S% Nresult4 = html.xpath("//title/text()")
$ K. k u5 ?& T0 P6 D" I5 Lprint(result4)+ w$ l$ P6 g4 ~( \- e
# 搜索div节点
' Y2 i4 d- w3 }7 a8 Uresult5 = html.xpath('//div')
$ h9 A" B. Q& kprint(result5)
& M2 O/ `8 z3 K4 R+ Y6 n# 搜索div节点 并用class属性定位) | P) j- ?# e
result6 = html.xpath('//div[@class="news"]')
( w# h# }9 ^( o* g$ Hprint(result6)
! A* ~, B2 c# ^# o( L# ~/ {# 搜索div节点 id属性定位
7 @, U) K% |/ u$ z% Cresult7 = html.xpath('//div[@id="news"]')
$ c, ` b! c5 t# k! o1 cprint(result7)8 t$ Z8 X5 I$ @
# 获取列表span信息( M1 O- T! K7 ]( {% g
text = html.xpath('//div[@class="news"]/a/span/text()')
b" L% T+ s/ b3 P9 A# F+ b; kprint(text)
: `, H# ]2 |" [
7 o, h+ T" O6 Q" Y: j* ?+ H" Cg、通过上一步我们得到自己需要的数据后,下一步就是将我们得到的数据进行保存* z' u; g, ~9 S ~" W
保存的方式有很多种:
* V2 K: J, O7 D' Y* g, d, S# t1、保存至文件! ?0 R( g; I* ~/ Q
csv、json、text# T# Y! K3 F. L p9 {: |; o4 b! A
2、保存至数据库/ z9 f4 [3 T$ B, |2 W
MySql、MongoDB、Redis 等 (目前主流的为以上列举的三种数据库) 关于数据存储后面再做详细介绍
7 z0 {3 I4 ~; e9 D& |# Y; W7 n; D; q" |" Y/ r! ^$ k8 K0 H% G" @
- Q, `9 c( F$ c3 |9 @! M8 e8 v5 Z
————————————————+ ?1 K" g+ A# e0 x$ x+ P/ q8 O
版权声明:本文为CSDN博主「royallucky」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
- F7 H" E* y% n$ f2 b原文链接:https://blog.csdn.net/royallucky/article/details/105930473$ k ]- b6 l- B/ B R* e
( P2 n, K( t8 c# j2 X: k* a
) \/ @7 w" r' b0 {8 c0 I
|