beautifulSoup模块

beautifulSoup模块

在之前的爬虫程序中 我们编写了大量的正则表达式来获取数据,效率非常低
beautifulSoup是一个第三方模块  专门用于解析XML文档
目前最稳定的是bs4 版本
pip install bs4

查找元素

1.bs遍历文档树

from bs4 import BeautifulSoup

# 第一个参数为要解析的文档数据 可以是字符串 也可以是一个文件对象
# 第二个参数表示要使用的解析器  每个解析器有不同的特点  推荐是用lxml 速度快 pip install lxml
soup = BeautifulSoup(html_doc,"lxml")
soup代表整个文档

#查找内容
tag = soup.body # 拿到html中body标签
# 一个标签分为三个部分
# 标签名字  标签属性  标签内容
print(type(tag)) #Tag
print(tag.name)  #标签名
print(tag.text)  #标签内容
print(tag.attrs) #标签属性  返回是一个字典

# 使用点语法来查找标签   要注意 查找范围是全文 只能找到第一个名字匹配的标签
tag = soup.a  #拿到第一个a标签
print(tag.attrs.get("href")) #拿到属性href的内容

# 嵌套选择
print(soup.p.b.text)  #p标签中b标签的文本内容

# 获取子节点
#注意:a标签,还有img标签不能放到head标签中
for i in soup.p.children:  # 返回一个迭代器 iterator
     print(i)

for i in soup.p.contents: # 返回一个列表 
    print(i)
    
# 获取父标签
print(soup.p.parent)

# 获取所有的父辈标签
#soup.p.parents #返回一个生成器 generator
for i in soup.p.parents:
    print(i.name)
    

# 获取所有子孙标签 与soup.p.contents不同之处在于 会把所有子孙标签全部拆出来 包括文本内容
#soup.p.descendants #返回一个生成器 generator
for i in soup.p.descendants:
    print(i.name) # 文本内容没有name属性,返回None
    
# 获取兄弟标签 文本也被当做是一个节点
# 下一个兄弟
print(soup.a.next_sibling)
# 之后的兄弟们
print(list(soup.a.next_siblings))

# 上一个兄弟
print(soup.a.previous_sibling)
# 之前的兄弟们
print(list(soup.a.previous_siblings))

2.bs搜索文档树

1.find_all()

# 过滤器
# find_all查找所有匹配的标签
# 按照名字匹配   可以传一个名字 或 一个列表
print(soup.find_all("a"))  #查找所有的a标签
print(soup.find_all(["a","p"])) #查找所有的a和p标签

# 找id 为link 的a标签
print(soup.find_all("a",attrs={"id":"link"}))
print(soup.find_all(name="a",id="link"))

# 找class为sister 的a标签
print(soup.find_all("a",attrs={"class":"sister"}))

# 注意如果要按照条件为class来查找 需要使用class_ 因为class是关键字
print(soup.find_all(name="a",class_="sister brother")) # 只能找到类名完全匹配的如:<a class="sister brother">

print(soup.find_all(name="a",class_="sister")) #只要类名带有sister就能找到

# 如果属性带有特殊符号 可以把条件装在attrs中
print(soup.find_all(name="a",attrs={"data-a":"sister"}))


# 指定文本
print(soup.find_all(name="a",text="Elsie"))


# 过滤器
# 标签名称中带有b字母的标签
# 正则匹配
c = re.compile("b")
print(soup.find_all(name=c))


# 列表
print(soup.find_all(name=["body","a"]))


# True 表示所有标签
print(soup.find_all(True))

# 所有具备id属性的标签
print(soup.find_all(id=True))


# 方法匹配(写个函数来过滤)
def myFilter(tag):# 必须只能有一个参数 参数表示要过滤的标签
    #标签名是a  文本内容不是"Elsie" 标签含有id属性 
    return tag.name == "a" and tag.text != "Elsie" and tag.has_attr("id")
print(soup.find_all(myFilter)) #返回满足条件的所有数据
print(soup.find_all(myFilter,limit=1))  #返回一条数据

#获取所有a标签
print(soup.find_all(name="a"))


# 五种过滤器: 字符串、正则表达式、列表、True、方法

2.find()

#find( name , attrs , recursive , text , **kwargs )
find_all() 方法将返回文档中符合条件的所有tag,尽管有时候我们只想得到一个结果.比如文档中只有一个<body>标签,那么使用 find_all() 方法来查找<body>标签就不太合适, 使用 find_all 方法并设置 limit=1 参数不如直接使用 find() 方法.下面两行代码是等价的:

soup.find_all('title', limit=1)
# [<title>The Dormouse's story</title>]
soup.find('title')
# <title>The Dormouse's story</title>

唯一的区别是 find_all() 方法的返回结果是值包含一个元素的列表,而 find() 方法直接返回结果.
find_all() 方法没有找到目标是返回空列表, find() 方法找不到目标时,返回 None .

soup.head.title 是 tag的名字 方法的简写.这个简写的原理就是多次调用当前tag的 find() 方法:

soup.head.title
# <title>The Dormouse's story</title>
soup.find("head").find("title")
# <title>The Dormouse's story</title>

# recursive 是否递归

3、CSS选择器

#该模块提供了select方法来支持css,详见官网:https://www.crummy.com/software/BeautifulSoup/bs4/doc/index.zh.html#id37
html_doc = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title">
    <b>The Dormouse's story</b>
    Once upon a time there were three little sisters; and their names were
    <a href="http://example.com/elsie" class="sister" id="link1">
        <span>Elsie</span>
    </a>
    <a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
    <a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
    <div class='panel-1'>
        <ul class='list' id='list-1'>
            <li class='element'>Foo</li>
            <li class='element'>Bar</li>
            <li class='element'>Jay</li>
        </ul>
        <ul class='list list-small' id='list-2'>
            <li class='element'><h1 class='yyyy'>Foo</h1></li>
            <li class='element xxx'>Bar</li>
            <li class='element'>Jay</li>
        </ul>
    </div>
    and they lived at the bottom of a well.
</p>
<p class="story">...</p>
"""
from bs4 import BeautifulSoup
soup=BeautifulSoup(html_doc,'lxml')

#1、CSS选择器
print(soup.p.select('.sister'))
print(soup.select('.sister span'))

print(soup.select('#link1'))
print(soup.select('#link1 span'))

print(soup.select('#list-2 .element.xxx'))

print(soup.select('#list-2')[0].select('.element')) #可以一直select,但其实没必要,一条select就可以了

# 2、获取属性
print(soup.select('#list-2 h1')[0].attrs)

# 3、获取内容
print(soup.select('#list-2 h1')[0].get_text())

案例:爬取汽车之家新闻

import requests
from bs4 import BeautifulSoup

url = "https://www.autohome.com.cn/news/{page}/"

# 过滤标签
def filter(tag):
    return tag.name == "li" and tag.has_attr("data-artidanchor")

# 获取新闻列表
def get_list_paget(url):
    print(url)
    resp = requests.get(url,headers={"user-agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36"})

    soup = BeautifulSoup(resp.text,"lxml")
    lis = soup.find_all(filter)
    for t in lis:
        print("https:"+t.a.attrs.get("href"))
        print("https:"+t.img.attrs.get("src"))
        print(t.h3.text)
        print(t.span.text)
        print(t.em.text)
        print(t.p.text)

get_list_paget(url.format(page = 1))

案例:爬取中关村测评数据

'''
分析可知
1.首页数据存放在html文档中
http://labs.zol.com.cn/

2.后续加载的数据是通过ajax请求,刷新文档,返回数据类型为json
http://labs.zol.com.cn/router.php?c=TestChannel_Default&a=GetChannelNew&module=new&page=2
'''

import requests
from bs4 import  BeautifulSoup

#爬取第一页的数据
url = 'http://labs.zol.com.cn/'
user_agent = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36'

resp = requests.get(url,headers = {'user-agent':user_agent})
soup = BeautifulSoup(resp.text,'lxml')

res = soup.find_all(name='li',class_='content-list-item')
for i in res:
    #连接
    print(i.a.get('href'))

    #标题
    print(i.div.div.text)

    #简介
    print(i.p.text)

    #评论数
    n = i.find(name='div',class_='icon info-comment')
    print(n.text)

    #时间
    print(i.span.text)

    #单张图片获取
    print(i.a.img.attrs.get('src'))
    #多张图片获取
    res = i.find(True,class_='pics-list clearfix')
    if res:
        imgs = i.find_all(name='img')
        for img in imgs:
            print(img.attrs.get('src'))

import time
#爬取加载更多的数据
base_url = 'http://labs.zol.com.cn/router.php?c=TestChannel_Default&a=GetChannelNew&module=new&page={page}'
for i in range(2,4): #爬取第二页和第三页的数据
    resp = requests.get(base_url.format(page=i))
    print(resp.json())
    time.sleep(1)

 

posted @ 2019-04-22 16:55  Zhuang_Z  阅读(229)  评论(0)    收藏  举报