: T. q' r. O, N) w4 o3 b; Z& K
% V, O! H: \2 ]# A
! W: p5 N$ ]/ S- v手把手带你5分钟搞定爬虫(聚焦爬虫) 1 w5 E% [) G3 K! @. P7 L
2 o1 G7 d5 `( h4 v, U, Z爬虫的原理
. T5 e2 l% T6 o+ D8 w0 D8 O. |, C W }5 P! ^2 O2 `3 ?; x% N$ ?; B
对于很多刚入门的小伙伴来说,了解爬虫的工作原理是非常重要的。只有当你了解了它的原理,你才能结合实际情况写出对应的爬虫。下面我将以通俗的语言方式来介绍爬虫
?9 ~$ O4 A: p+ h6 P$ f1 c3 e把互联网比作一张巨大的网,那爬虫就是这张网上面的一只小蜘蛛,在互联网上面有很多的数据,各种各样的数据,我们可以把这些数据比作是被网粘住的小昆虫等。爬虫的作用就是去找到自己喜欢的口味,然后把它带回家,可以储存起来,也可以马上享受美味
! t) b9 L9 ~: n! g所以说爬虫分为三部曲:$ @0 R! @, L- r
1、明确你自己喜欢什么口味(制定需求,明确你需要的数据类型)4 \6 D' |+ z# f; n7 C
2、去找到你喜欢的食物的位置(得到目标地址)
6 r6 [# j, z) G' l4 y3、把食物搬回家去(解析网址,获取数据,存储数据)0 ~; ~, o+ [( N4 i! O' c
它的整个流程就是:(简单的通过request请求获取数据并解析获取数据。以下都是固定化格式,可以照搬); p9 b# Z* E/ y2 o
a、导入需要的库 requests
0 |* j1 d7 i$ I! P. l, e; z
# E# K; u8 Q( w# W& D$ j1 Pimport requests3 w$ M+ Z/ y4 U k8 F
1
u' j2 P7 s& |& V" W4 Lb、明确你要获取数据的网址(这里以百度网站为例)) |% A7 t- @( J: W4 o
) n- e/ B# s) n* Turl = 'http://www.baidu.com'' _3 G/ k: H6 f4 b: l) M
1
2 L1 \& P4 A7 ]. Y: J) ]c、创建请求" r2 F+ A, M; \
. u( R+ u, ?) z4 k5 hresponse = requests.get(url)
' ?8 s: b6 {3 X; Z/ C1. D( e1 t* @; k) Z" K
d、测试请求是否成功(在实际应用中用的比较少,正常情况都是可以请求成功的,网站对于访问外部信息的爬虫是不会做出限制的)
/ e) }& ^+ Y6 {) \# L3 I$ X c% g0 q2 C. {4 n7 k3 k
print("状态码:",response.status_code)
2 E' T! [4 r- f- |, t* H- _) R1; j# ~+ |; n% e5 j5 A+ B3 M
当状态码为“200”时表示请求成功;如果不是“200”,很大可能就是你的目标url写错了,核对一下url
* t+ T/ H9 X n( @5 k& E9 ve、得到网页源码& K, ], Q, e0 {8 h; `
8 }* A7 x2 y8 d1 R$ k& J
print("内容:",response.text)* x7 L H8 E5 l
1: N; S" |4 X; h2 P# m! S6 i! Q
response.content是response.text的二进制形式
- N$ P& }# F k" s: U+ ]8 g8 @& _6 v5 u
至此,一个简单的爬虫请求就算是完成了。" Y$ f7 _7 Q" ^
当我们请求目标url并获取到response后,就需要进行下一步:解析网址并获取数据% I( o, m4 a5 J5 j0 z
f、解析网址& T; C1 I0 L; U, E& h
刚开始学习阶段,我们最常用的两种方法,一种是BeautifulSoup,一种是etree。其中BeautifulSoup是最常用的。以下将对两种方法分别进行介绍 (以下方法均为测试用例,实际当中应结合自己的实际情况选择应用)4 g5 r- t% B9 R3 K
BeautifulSoup:
) z' w. A" W+ C( w# w I+ x& _; I! m) A首先导入需要的库文件:
! _2 n( c) l' ? m9 O" b& p! I
( H6 Y7 j- ]; c- E2 ^, ?! Yfrom bs4 import BeautifulSoup
$ S: E6 Y' G% A' ~) t4 ^+ S f6 v1
+ A# Q9 C( P* U1 W; B, F然后对上面请求得到的response进行解析:0 L, m. _* `7 M! _
$ t2 r: ^% l4 H$ ?" _/ Chtml = respone.content.decode('utf-8')
, y" Y0 X1 z9 H3 qsoup = BeautifulSoup(html, 'lxml')
! w' L1 {5 r5 |- U1 ?1 |( C1
' C4 R( }* c- H% l: T) F; o2
" u: Z' z4 [% |0 V& ~' w5 j以下是BeautifulSoup中比较常见的方法,有兴趣的朋友可以去一一测试:5 M# V% ^7 g4 u( A% S9 H! V5 f8 R
# 获取head标签
0 D, q. M' y; M3 \ G! `1 Y" Hprint('head标签内容', soup.head)
3 k1 r) _# l8 ]4 V5 V. g6 ^/ X# 获取title标签
$ K9 A/ n! O% k* `- Dprint('head标签内容', soup.title)
; J; Y" Z. ~9 f1 f# l6 j! `# 获取body里面的img内容
9 }* u& P3 E' f! r$ y9 K5 n# 通过点号的方式只能获取 下属第一个标签(即获取soup下面第一个body里的第一个a标签的第一个img标签)* A5 y% N, `+ l0 N& ]/ }; q
print(soup.body.a.img)
4 T' d# Y3 {9 a! y) Y2 ]# 获取所有的img标签 使用find_all()搜索整个soup对象% X6 P# {2 H# S5 U- ?7 C
print("获取所有的img标签", soup.find_all('img'))( C T7 i# }: l' q0 F8 L! {' g" [
# 获取标签中的属性src地址0 @ o: n" X$ O3 l% \( g
print("获取标签中的属性src地址:", soup.img['src']) # 注意:英文点号都只能获取第一个值 ] l2 y+ Y* p: [5 A" D+ N
$ C i! R& i# N7 u
p- ?$ C# _7 t* F7 R" s4 w# 获取soup的name' h- Y* v5 S2 ?0 F+ f2 L5 v
print("获取soup的name:",soup.name)
1 P3 ?- N, A1 u" d ~- m# 获取a标签名+ a: ^' p. C* m% a1 G& g1 g
print("a标签名字:",soup.a.name)$ c C) F* h- [/ C
tag = soup.a0 c$ k4 |$ e5 ?$ S
print(tag). U8 c" m P( X* s6 f' S1 @
# 修改操作 a标签改为b标签
& X" r" q2 g( E. a! ltag.name = 'b' # 赋值后<a> </a>变为<b> </b>+ D1 e2 y. p, g% { d; C9 ^1 ^
# 输出修改后的内容) u4 f4 ?! @# ~6 P T' u; k
print("输出修改后的内容:",tag)& K( @7 J) W! v7 C3 J& y/ d
# 获取全部的属性 attrs
# }) ~( h5 g4 m6 E$ {print("tag对象的全部属性:",tag.attrs) # 只能得到当前标签的属性(不包含下属标签)# k% X) a; |& L8 O% Y
% ?9 A+ B: z% e/ d- J- i \6 G/ t# 获取属性的class的值 根据键取值# m, E& |# g4 B# P) |
print("获取属性的class的值:",tag["class"])
$ q! [4 n4 a3 t' ]6 ^4 v2 m# h
# [& [8 P; Q1 X+ F% O1 d5 [4 d
5 J3 S8 H0 f5 ]( n6 V- L8 [& @8 p# C1 n& i. B' T
"""针对获取的标签属性可以进行增删改查"""
2 |2 x4 @$ _: p" m" U6 S+ ~5 `# 修改" @. e# l4 f' M* z) m& N% I7 ?' h
tag["class"] = "Logo") _* X, n) x6 e$ L' U3 ?
print("tag对象的全部属性:",tag.attrs)
6 O; g+ V7 n. I C( [3 L# 输出属性5 J$ W+ P6 M0 E" _
print("获取属性的class的值:",tag["class"])
/ O: a& c) ]# G. M' J% _( }# 新增 属性id 赋值logo
: c. q2 }) v' Z" d) P8 r6 _) Etag['id'] = 'logo'
$ Z& D% a4 W+ D7 e6 \& Eprint("tag对象的全部属性:",tag.attrs)
8 ^6 ?1 @/ q6 B8 l2 Q# 删除属性/ E8 V5 t# N8 ^. Z6 v+ o9 N: r
del tag['class']
6 b( A' C" Q: Q# J* B' D+ `print("tag对象的全部属性:",tag.attrs)" ~1 }/ H8 b7 i- \8 K4 C
) o1 p7 s) o& \8 u
4 [: }' }' `& j$ |3 M9 X. O/ m
"""针对内容操作 (只针对标签,对属性无效)"""0 Z* @+ ?: k0 p
# 获取title标签内容$ N3 F- Q: a, d# l
tag = soup.title
2 }4 E9 P8 x4 ^5 E q# 获取title字符串
5 l7 ~9 ?: [: p" b5 t! zprint("tag对象所包含的字符串:", tag.string)
2 f( p% B; h- Qprint("类型:", type(tag.string))# j, g, X1 ~- e6 G0 S9 i3 C
# 替换内容4 b! p% T7 q# x+ ?9 C
tag.string.replace_with("你好")9 v1 s. D% u9 ?' @/ _
print(tag.string)
1 H) `% T3 J7 U5 ^# <!-- 学科建设 -->3 j1 @. e& Y8 e5 y4 A8 r
# 获取注释文本内容0 u7 M" O/ e' D# v/ R
markup = '<b><!-- 学科建设 --></c>'
7 C+ j' k+ l) O$ a- Q6 Y+ nsoup_comment = BeautifulSoup(markup, 'lxml')
8 a2 U" Z7 H1 D& ~! v1 rcomment = soup_comment.b.string # 以markup第一个标签作为关键进行搜索- m+ C$ }" `# `# e5 Z! [
print("获取注释文本内容:", comment)
+ t# g& X9 a6 a! Z) o5 E# get_text()
$ w+ o L* P( o3 n+ [print("title的全部节点:",soup.find_all("title"))
6 X* _7 J$ D5 y$ Aprint("title的内容:",soup.title.get_text())
0 `$ Y6 T, y( t' ~& v0 l, C- a"""查找ul元素标签"""; T, ^8 n( m8 L4 d% y6 t
# class是关键名,匹配时后面必须加下划线_
. Q' S: @ i/ q) P/ ~7 W+ n# tag = soup.find_all("ul", class_='menu') # 按照css类名完全匹配% `0 Y z. d$ b- n- K# d
# print(tag)1 i# g( w* F4 T" X, F" ^
tag = soup.find_all("ul", id='menu') # 按照css类名完全匹配
' r* y$ }( @) aprint(tag)" b% u8 g [# z( b
# 查找名称为a元素的标签内容: O$ l! _& r0 h3 A3 U
taga = soup.find_all('a')% n4 k! j: A# m! b/ C
for tag in taga:
4 Q# G8 S4 i7 R, u6 \1 R) t print("查找名称为a元素的标签内容:", tag.string)
; M4 e/ o; \ V1 f# get 获取属性信息 获取整个页面的img标签图片和src属性值
6 Z$ w1 Q# K; Y# V% ptagings = soup.find_all('img')# @3 E" x6 X. K- g4 f
print(tagings)
0 o) v- K$ v s, Z5 E# 循环输出图片地址
5 Q2 s3 m1 Q9 o. ?" Sfor imgsrc in tagings:. q0 t, p l* o' m- }3 o1 v5 F
print(url+imgsrc.get('src'))
. f* P+ W g7 |; `" G
7 B2 q# u+ ~. `! b, s0 M6 ~' e""""属性只能做为参数,所有的操作只能针对标签"""
! h5 e0 {# b& O8 y
0 ~9 m$ t/ L3 l9 u* F3 cetree方法:
$ K* M* Q. u1 M) ]8 F导入库文件:
4 `- T# M2 ~: B6 a# o' C$ V
! ]- ]+ K, V |' c4 r( Tfrom lxml import etree+ f: c n; P# p; d4 |5 e$ U
1
& }$ o6 I) r. _解析得到的response对象;8 q* ^" ? L7 t5 ]
/ ]" V+ j6 H' U1 ^
# 设置response的字符集 v% U. ?- q3 l6 J
html = req.content.decode('utf-8')
4 @- o% |' R7 T+ [- n6 {4 y L4 O1 b( B# 解析对象: Q2 U# }' O# F' c+ n/ c# c. Q
html = etree.HTML(html,parser=etree.HTMLParser(encoding='utf-8'))
; X( W9 E0 F \* z% z& q6 s3 v
+ Z" \( r! o; _6 @+ a常用的方法:' e: q% X5 c' r. L' D$ ]7 T& Q( l
5 z9 U; o0 e$ _ q9 |
8 j& n9 l9 ?+ t& g4 v6 E1 C
# 定位head节点(元素)
J4 _$ I) S$ j9 K: g4 b2 W; wresult1 = html.xpath('head')4 P5 @6 M: M- [6 c1 B
print(result1)% o J) e: W4 `3 w: v4 R r' v& ?
# 定位title节点# `; m+ Z6 ?) E+ R: n
result2 = html.xpath('/html/head/title')
/ ~9 h. y* v# J7 p `print(result2)
7 v3 {$ M) a ]7 M" V0 W# 搜索节点 title节点! f5 j0 g o: D
result3 = html.xpath('//title')
$ D# ~3 Y5 H: ~print(result3)
# \2 E/ X+ c* k, u5 F# 搜索title内容 text()得到文本信息: Q: ]+ \7 Y8 E2 l
result4 = html.xpath("//title/text()")
* q) N5 ^* i, i; }- \' yprint(result4)! a' ~* Y: D# R4 e
# 搜索div节点
! V m1 A3 |7 c6 ]! ^! W, mresult5 = html.xpath('//div')' o* ]" R+ F. j/ y! ^% `# w
print(result5)
7 k8 m0 p6 t0 n- s- P0 L# 搜索div节点 并用class属性定位
V/ f) s- c; j( s2 Vresult6 = html.xpath('//div[@class="news"]')
% W% A6 y7 F- z hprint(result6)) }) @9 |# |: S0 o. U
# 搜索div节点 id属性定位
9 g' I+ [" h4 h0 b( Q7 K7 Eresult7 = html.xpath('//div[@id="news"]')
" L( g9 y5 D! g% [ C' tprint(result7)8 m( a7 S. y M
# 获取列表span信息% T/ q% d. }/ l6 m. V; e$ o
text = html.xpath('//div[@class="news"]/a/span/text()')# q, l3 q" S: ~1 r+ J9 t
print(text); N$ H& v' }9 ]6 g& o
, H6 h7 ^4 s# d" r1 ~g、通过上一步我们得到自己需要的数据后,下一步就是将我们得到的数据进行保存
/ _7 F, U. k1 R$ X& x" t# a保存的方式有很多种:
, Y; U" X$ f& x# ?, ^* d1、保存至文件/ U. x" |5 ~# x! F% w
csv、json、text
0 H7 a6 S% c* u+ @8 F3 ^) _" c2、保存至数据库 G5 u, u Z4 t" B0 {: Z
MySql、MongoDB、Redis 等 (目前主流的为以上列举的三种数据库) 关于数据存储后面再做详细介绍
: s, ?: I8 b2 h2 ?6 H f1 D) {
; F/ M5 P# Z& C. I2 p* S5 i* ^
* f( ^0 W0 D: p A( B( \————————————————
+ R$ q; f3 i; n+ k版权声明:本文为CSDN博主「royallucky」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。9 q, L% f$ e7 D1 e
原文链接:https://blog.csdn.net/royallucky/article/details/105930473
9 D6 Y: [5 D- _( t9 w4 R: c& d* k; c+ }
* y# m, p, u" n* R, Y5 X |