0. 前言
在介绍BeautifulSoup模块前, 我们先分析一下我们要爬取的网页结构是什么样的。通常网页都包含层叠样式表(英文全称:Cascading Style Sheets),例如。 推荐使用谷歌浏览器或者火狐浏览器方便快捷查看网页结构。例如在chrome中百度首页右击,选择'inspect',即可查看到网页结构,以及各个标签层级关系。
1. 创建爬虫爬取网页
爬取网站:url =www.pythonscraping.com/pages/warandpeace.html
网页如图所示,有红色和绿色字体。在绿色字体处右键选择“inspect”分析标签结构可知。绿色字体均包含在标签GreenText当中。
1.1 抓取网页
from urllib.request import urlopen
from bs4 import BeautifulSoup
url ='http://www.pythonscraping.com/pages/warandpeace.html'
html= urlopen(url) #抓取了该url网页
soup = BeautifulSoup(html) #使用BeautifulSoup对网页进行解析
name_list = soup.find_all("span",{'class': 'green'})#find_all抓取所有绿色字体,返回list
for name in name_list:
print(name.get_text()) #get_text()函数剔除字符串中所有tag符号只保留tag中包含的文本
2. find() 和 find_all()
推荐有能力的各位查看BeautifulSoup官方文档,这里简单讲解一下。
请看以下比较:
find_all(tag, attributes, recursive, text,limit, keywords)
# find_all(标签, 属性, 递归, 文本,限制查询数量, 关键字)
find(tag,attributes, recursive, text,keywords)
#find 相当于find_all(,limit=1)
绝大多数的情况我们只会遇到前两个参数,tag和attributes。tag和attributes都可以查找多个值。
from urllib.request import urlopen
from bs4 import BeautifulSoup
url ='http://www.pythonscraping.com/pages/warandpeace.html'
html= urlopen(url) #抓取了该url网页
soup = BeautifulSoup(html) #使用BeautifulSoup对网页进行解析
hs = soup.find_all({'h1', 'h2'})#find_all抓取所有绿色字体,返回list
print(hs)
得到结果:
[<h1>War and Peace</h1>, <h2>Chapter 1</h2>]
同理,属性参数也可以包含多个属性。例如需要查找所有绿色和红色的文本:
....
words = soup.find_all('span', {'class':{'green', 'red'}})
print(len(words))
有兴趣的朋友可以看看绿色和红色的tag分别有多少个。
关键字参数可以用来选择包含特定属性的是标签,比如:
all_text = soup.find_all(id = 'text')
print(all_text[0].get_text()
细心的朋友可能会注意到,其实关键字参数匹配完全可以用属性参数替换。
soup.find_all(id='text')
soup.find_all("",{"id":"text"})
soup.find_all(class="green")
soup.find_all('',{'class':'green'})
注意: 在BeautifulSoup4版本中find_all 和findAll 是一样的。find_all是新版本的写法,findAll是旧版本的写法,但是目前二者在版本4中通用。
3. 1子节点和子孙节点
soup.body.h1# 选中body 标签下的h1,这个h1 标签是body标签的子节点
同理,soup.div.find_all('img')会找到所有div里面的img标签。
.children 和.descendants
对比代码如下:
html = urlopen('http://www.pythonscraping.com/pages/page3.html')
soup = BeautifulSoup(html, 'lxml')
children = soup.find('table',{'id':'giftList'}).children
descendants = soup.find('table',{'id':'giftList'}).descendants
sum = 0
for child in children:
print(child)
sum +=1
print(sum)
sum2 = 0
for descendant in descendants:
sum2+=1
print(descendant)
print(sum2)
运行结果可知 sum = 13, sum2 = 86
取descendants的第一部分作比较可以发现
<tr><th>#=============<tr>是soup.find('table',{'id':'giftList'})的子节点====
Item Title
</th><th>
Description
</th><th>
Cost
</th><th>
Image
</th></tr>#============<tr>是soup.find('table',{'id':'giftList'})的子节点====
<th> #============<th>是<tr>的子节点,('table',{'id':'giftList'})的子孙节点==
Item Title
</th> #============<th>是<tr>的子节点,('table',{'id':'giftList'})的子孙节点==
Item Title#=========文本是<th>标签的内容,也是子孙节点================
<th>#============同上====================
Description
</th>
Description
<th>
Cost
</th>
Cost
....
对比可知,children只列出了<tr>标签所包含的内容。而descendants列出了所有包含的标签节点以及文本,即<tr>子标签中的所有子子孙孙标签都会查找返回。
3.2 父节点
通常情况下我们更经常查找子节点,而在某些特定情况下会用到查询父节点,.parents 和 .parent。
from urllib.request import urlopen
from bs4 import BeautifulSoup
html = urlopen('http://www.pythonscraping.com/pages/warandpeace.html')
soup = BeautifulSoup(html)
print(soup.find('img', {'src':'../img/gifts/img1.jpg'}).parent.previous_sibling.get_text())
分析一下代码是如何工作的。
<tr>
--<td>
--<td>(3)
--"$15.00"(4)
--s<td>(2)
--<img src="../img/gifts/img1.jpg">(1)
1.首先定位到含src="../img/gifts/img1.jpg"的标签img。
2.选中img标签的父节点s<td>.
3.选中s<td>的上一个同层级标签<td>
4.选取<td>标签中的文字
4. 处理同辈节点和父辈节点
BeautifulSoup的next_siblings()函数非常适用于表格查找,尤其是带有标题的表格。
from urllib.request import urlopen
from bs4 import BeautifulSoup
html = urlopen("http://www.pythonscraping.com/pages/page3.html")
soup = BeautifulSoup(html, 'lxml')
siblings = soup.find("table",{'id':'giftList'}).tr.next_siblings
sum = 0
for sibling in siblings:
print(sibling)
sum+=1
print(sum)
结果为:
<tr class="gift" id="gift1"><td>
Vegetable Basket
</td><td>
This vegetable basket is the perfect gift for your health conscious (or overweight) friends!
<span class="excitingNote">Now with super-colorful bell peppers!</span>
</td><td>
$15.00
</td><td>
<img src="../img/gifts/img1.jpg"/>
</td></tr>
<tr class="gift" id="gift2"><td>
Russian Nesting Dolls
</td><td>
Hand-painted by trained monkeys, these exquisite dolls are priceless! And by "priceless," we mean "extremely expensive"! <span class="excitingNote">8 entire dolls per set! Octuple the presents!</span>
</td><td>
$10,000.52
</td><td>
<img src="../img/gifts/img2.jpg"/>
</td></tr>
...
11
0
[Finished in 2.2s]
代码输出产品表中的所有产品,除了首行标题。因为:
1. 查找对象本身不是自己的同辈,因此使用sibling相关函数时查找对象都会被跳过。
2.代码使用的是next siblings,因此会返回查找对象的下一个(些)同辈节点。
补充:除了next_siblings,记住previous_siblings经常用来查找已知最后一行容易定位且不需要抓取的情况。当然,next_sibling 和 previous_sibling 可以用来查找一个同辈节点。