使用BeautifulSoup爬取想要的标签(《python网络爬虫权威指南》笔记)

精确爬取标签

我们可以使用标签的CSS属性爬取择我们想要的一个或者多个标签,如class(类)属性、id属性、src属性等。
为了方便演示标签的选择,我们使用书中作者特别准备好的爬虫演示网站为例(http://www.pythonscraping.com/pages/warandpeace.html
)。在这个网站中,人物的对话是红色的,对应的类属性为red,人物名的标签为绿色的,对应的类属性为green,并且对应的标签都是span标签,比如:

<span class="green">the prince</span>
<span class="red">First of all, dear friend, tell me how you are. Set your friend's
mind at rest,</span>

那么我们就可以通过类属性来轻松的抓取出现的任务的名字:

from bs4 import BeautifulSoup
from urllib.request import urlopen

html = urlopen("http://www.pythonscraping.com/pages/warandpeace.html")
bs = BeautifulSoup(html,"html.parser")
nameList = bs.find_all("span",{"class":"green"})
for name in nameList:
    print(name.get_text())
    #get_text()表示只获取标签中的文本,如果不使用该方法,
    #则会打印完整的标签

BeautifulSoup中的find()和find_all()方法

find()方法和find_all()方法从字面意义上就可以看出区别,find用于抓取一个符合条件的标签,find_all()则用于抓取若干(默认为全部)个符合条件的标签,他们的定义如下:

find_all(tag,attributes,recursive,text,limit,keywords)
find(tag,attributes,recursive,text,keywords)

除了参数limit,两个方法的参数含义相同,所以之后都以find_all为例子进行说明:
tag:需要查找的标签名称(或者说类型),当只查找一种标签时以字符串类型传入,当查找多种标签的时候则以列表的形式传入,比如:

.find_all(["h1","h2","h3","h4","h5","h6"])
#查找所有标题标签

attributes:用字典封装,键对应需要查找的标签的属性,值对应于属性的值,比如:

.find_all("span",{"class":"green"})
#对应查找类属性的值为green的span标签
.find_all("span",{"class":{"green","red"}})
#对应查找类属性的值为green和red的span标签,注意对比二者的区别

recursive:传入一个bool变量,当传入True(默认)的时候,会抓取所有满足条件的标签,当为False的时候,只抓取一级标签,比如我们想要抓取p标签,对于下面这个p标签就不能被抓取到,因为它被包含在div标签中:

<div><p>123</p></div>

text:按标签中的文本来抓取内容,比如对于上面被包含在div中的p标签,我们就可以使用以下代码来进行抓取:

.find_all(text="123")

limit:最多抓取符合条件的标签的数目,从HTML代码最上方开始算起,当其值为1时,功能与find()方法等价
keyword:指定被抓取的标签的属性,比如:

.find_all(id="title",class_="text")
#寻找id为title。class为text的标签
#由于class为python中受保护的关键字,在指定类属性的
#时候需要写成class_

BeautifulSoup中的对象

BeautifulSoup对象:如之前展示的那样。
Tag对象:代表从HTML中抓取到的标签,通过find()或者find_all()方法得到,或者访问BeautifulSoup的属性得到。
NavigabelString对象:代表标签中的文字。
comment对象:用于表示HTML中的注释。

兄弟、子、父、后代标签的处理

这里我们以作者提供的虚拟购物网站为例进行说明:(http://www.pythonscraping.com/pages/page3.html

抓取子标签和其他后代标签

在BeautifulSoup中,子标签表示父标签中包含的下一级标签,而后代标签则表示标签中包含的下一级标签、下下一级标签、下下下一级标签等等,也就是说一个标签的子标签是它的后代标签,而一个标签的后代标签不一定是它的子标签。如果我们需要查看一个标签对象的子标签,则可以使用其children属性来获得,比如:

from bs4 import BeautifulSoup
from urllib.request import urlopen

html = urlopen("http://www.pythonscraping.com/pages/page3.html")
bs = BeautifulSoup(html,"html.parser")

for children in bs.find("table",{"id":"giftList"}).children:
    print(children)

从上面代码我们可以看到,首先我们使用了find方法,抓取到类型为table,id为giftList的标签,之后调用其children属性获取其子标签,由于其有多个子标签,所以我们使用for循环来对其子标签进行遍历。
另外我们还可以使用descendant属性来找出所有后代标签。

抓取兄弟标签

兄弟标签即同一级的标签,在BeautifulSoup中可以使用tag对象的next_siblings来抓取改标签之后的所有兄弟标签,也可以使用previous_siblings来抓取改标签之前的所有兄弟标签。同样的,还有next_sibling和previous_sibling,用来抓取后一个或者前一个兄弟标签。下面是一个例子:

from bs4 import BeautifulSoup
from urllib.request import urlopen

html = urlopen("http://www.pythonscraping.com/pages/page3.html")
bs = BeautifulSoup(html,"html.parser")

for sibling in bs.find("table",{"id":"giftList"}).tr.next_siblings:
    print(sibling)
   	#抓取div中第一个tr标签之后的所有同级标签并遍历

抓取父标签

查找抓取父标签的功能并不适用,可以使用parent和parents来抓取当前标签的父标签,下面是一个例子:

from bs4 import BeautifulSoup
from urllib.request import urlopen

html = urlopen("http://www.pythonscraping.com/pages/page3.html")
bs = BeautifulSoup(html,"html.parser")
print(bs.find("img",
             {"src":"../img/gifts/img1.jpg"}).
              parent.previous_sibling.get_text())

最后一行代表先找到符合条件的img标签,之后选择img标签的父标签,之后在选择该父标签的之前的一个兄弟标签,获取其文本内容。

正则表达式

正则表达式在提取字符串时非常有用,它可以理解为一种字符串匹配规则,比如对于满足以下所有条件的字符串:

  1. 字母“a”至少出现一次
  2. 后面跟着“b”,重复五次
  3. 后面再跟着“c”,重复任意偶数次(包括零次)
  4. 最后一位是“d”或者“e”

满足以上规则的字符串有很多,比如“aaaabbbbbccccd”。使用正则表达式则可以表示以上的四个条件,可以表示为:
a+bbbbb(cc)*(d|e)
改正则表达式可以解释为:

  • a+:+号代表前面的字符至少出现一次,如“a”或者“aaaa”
  • bbbbb:5个b
  • (cc)*:星号代表前面的字符至少出现0次或以上,由于加了括号,所以是两个c,所以代表c需要出现偶数次,如“cc”或者“cccc”
  • (d|e):必须是d或者e,可以与其他符号如“+”号进行组合,表示可以出现一次以上的“d”或者“e”

下面是常用的正则表达式符号:

符号含义例子可能的匹配结果
*匹配前面的字符、表达式或者括号里的字符0次或多次abaaabbb,aaa
+匹配前面的字符、表达式或者括号里的字符至少1次a+b+ab,aabb
[]匹配中括号里的任意字符(相当于选一个)[A-Z]*APPLE,QWERTY
()表达式编组(里面的表达式优先执行)(ab)aabaab,abaab
{m,n}匹配前面的字符、表达式或者括号里的字符m次到n次a{2,3}b{2,3}aabbb,aaabbb
[^]匹配任意一个不再括号中的字符[^A-Z]*abc1234,qwert
|匹配任意一个由竖线分隔的字符、子表达式b(a|i|e)dbid,bed,bad
.匹配任意单个字符b.dbad,bzd
^表示以前面的字符或表达式开头^aasdf,a
\转义字符(把具有特殊含义的字符转换为字面形式)\. \| \ \.|\
$表示从字符串的末端匹配[A-Z] *[a-z] *$ABCabc,zzzyx
?!不包含^((?![A-Z]).)$no-caps-here,a4e f!ne
\d表示匹配任意一个数字&#92d+1,123
\D表示匹配任意一个非数字&#92D+ab!c,AbS
\s匹配空白字符(包括\t,\n,\r和空格)\s
\w匹配0-9,A-Z,a-z和下划线\w+abvAWE123_
\w\w的补集\W+++++===-

正则表达式和BeautifulSoup

使用正则表达式可以让我们在抓取网页中想要的标签时更加的方便,比如还是以之前的虚拟购物网站为例子,我们可以使用正则表达式来实现批量抓取文件路径类似的图片:

from bs4 import BeautifulSoup
from urllib.request import urlopen
import re

html = urlopen("http://www.pythonscraping.com/pages/page3.html")
bs = BeautifulSoup(html,"html.parser")

images = bs.find_all("img",
                    {"src":re.compile("\.\.\/img\/gifts\/img[0-9]*\.jpg")})
for image in images:
    print(image["src"])
#     ../img/gifts/img1.jpg
#     ../img/gifts/img2.jpg
#     ../img/gifts/img3.jpg
#     ../img/gifts/img4.jpg
#     ../img/gifts/img6.jpg

其中re模块中的compile函数用于将字符串转化为正则表达式。

获取属性

我们可以使用一个Tag对象的attrs属性来查看该标签的所有CSS属性,以我们之前使用爬虫抓取的图片标签为例:

for image in images:
    print(image.attrs)
#     {'src': '../img/gifts/img1.jpg'}
#     {'src': '../img/gifts/img2.jpg'}
#     {'src': '../img/gifts/img3.jpg'}
#     {'src': '../img/gifts/img4.jpg'}
#     {'src': '../img/gifts/img6.jpg'}

Lambda表达式(匿名函数)

Lambda表达式本质上是一个函数(称为匿名函数),对于find和find_all方法,允许我们将一个函数作为参数传入,该函数必须满足以下条件:

  1. 该函数传入的参数必须是标签对象
  2. 返回的值必须是bool量

之后find或者find_all函数便会将html文本中的每一个标签作为参数传入该函数,倘若返回的为True,则认为符合条件,将其以Tag对象返回,比如我们想要抓取具有两个CSS属性的标签,可以这么写:

bs.find_all(lambda tag:len(tag.attrs) == 2)
  • 7
    点赞
  • 49
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值