解析工具的使用——Beautiful Soup、XPath

2.1 Beautiful Soup
Beautiful Soup借助网页的结构和属性等特性来解析网页
其在解析时 实际上依赖解析器,它 除了支持Python标准库中的HTML解析器外,还支持一些第三方解析器(比如lxml)——我选择使用lxml
prettify()
可以把要解析的字符串以标准的缩进格式输出

返回——bs4.element.Tag类型,具有str属性

节点选择器——根据属性**来选择
直接调用节点的名称就可以选择节点元素,再调用string属性就可以得到节点内的文本了
1:这种选择方式速度非常快:
2:如果单个节点结构层次非常清晰,可以选用这种方式来解析
eg:
soup.title.string()
输出HTML中title节点的文本内容
注:soup.title可以选出HTML中的title节点,
再调用string属性就可以得到里面的文本了
——调用string属性来获取文本的值

提取信息
1:获取节点的名称——调用name
print(soup.title.name)

2:获取属性和属性值——调用attrs(获取所有属性,一个节点 有时候不止一个属性)

print(soup.p.attrs)

返回字典{}形式,并且 把选择的节点的所有属性和属性值组合成一个字典

{'class': ['title'], 'name': 'dromouse'}

注:
有的返回结果是字符串,比如,name属性的值是唯一的,返回的结果就是单个字符串。
有的返回结果是字符串组成的列表。对于class,一个节点元素可能有多个class,所以返回的是列表。
∴在实际处理过程中,我们要注意判断类型

如果要获取name属性值,就相当于从字典中获取某个键值,只需要用中括号加属性名就可以了。比如,要获取name属性值,就可以通过attrs[‘name’]来得到

还可以 (法2)
直接在节点元素后面加中括号,传入属性名就可以获取属性值了。不写attrs

print(soup.p['name'])#

——输出

dromouse

3:获取 节点元素 包含的文本内容——调用string

print(soup.p.string)

获取的是 第一个 p节点的文本

嵌套选择:
每一个返回结果都是bs4.element.Tag类型,它可以被继续调用 来进行下一步的选择
eg:
获取了head节点元素(Tag类型),我们可以继续调用head 来选取其内部的head节点元素

关联选择
在做选择的时候,有时候不能做到一步就选到想要的节点元素,需要先选中某一个节点元素,然后以它为基准再选择它的子节点、父节点、兄弟节点等——嵌套选择同理
(1)子节点、子孙节点
选取节点元素之后,如果想要获取它的直接子节点,可以调用contents属性
contents属性 :得到的结果是直接子节点的列表
调用children属性也可以得到相应的结果,返回的是生成器类型,接着用for循环输出相应的内容

如果要得到所有的子孙节点的话,可以调用 descendants属性

(2)父节点、祖先节点
如果要获取某个节点元素的父节点,可以调用 parent属性 ——直接父节点,没有再向外寻找父节点的祖先节点

如果想获取所有的祖先节点,可以调用 parents属性——返回结果:生成器类型

(3)兄弟节点(同级节点)
next_sibling 和 previous_sibling 分别获取节点的下一个和上一个兄弟元素

next_siblings 和 previous_siblings 则分别返回所有前面和后面的兄弟节点 的生成器

关联元素节点的信息 提取:
1:如果返回结果是单个节点,那么可以直接调用string、attrs等属性获得其文本和属性:
2:如果返回结果是多个节点的生成器,则可以转为列表后取出某个元素,然后再调用string、attrs等属性获取其对应节点的文本和属性(需要处理一下)
eg:

print(list(soup.a.parents)[0].attrs['class'])

方法选择器——调用方法选择
find_all :
返回 所有 匹配成功元素 所组成的列表

1:name:根据节点名 查询元素

print(soup.find_all(name='节点名'))

嵌套查询:

for ul in soup.find_all(name='节点名1'):
    print(ul.find_all(name='节点名1内的节点名'))

2:attrs:根据 属性

print(soup.find_all(attrs={'属性': '属性值'}))

得到列表形式,包含的内容就是符合“属性”为“属性值”的所有节点
3:text:(根据)传入的形式可以是字符串,可以是正则表达式对象(pattern)
可用来匹配 节点的文本

print(soup.find_all(text= str / pattern ))

传入pattern:结果返回 所有匹配正则表达式 的节点文本 组成的列表

find()
ps: 返回 第一个匹配的元素 (单个)

用法 和 find_all 相同

find_parents()
前者返回所有祖先节点,
find_parent()
后者返回直接父节点。

find_next_siblings()
前者返回后面所有的兄弟节点,
find_next_sibling()
后者返回后面第一个兄弟节点。

find_previous_siblings()
前者返回前面所有的兄弟节点,
find_previous_sibling()
后者返回前面第一个兄弟节点。

find_all_next()
前者返回节点后所有符合条件的节点,
find_next()
后者返回第一个符合条件的节点。

find_all_previous()
前者返回节点后所有符合条件的节点,
find_previous()
后者返回第一个符合条件的节点。

CSS选择器
只需要调用select()方法,传入相应的CSS选择器
输出——列表形式
1:嵌套选择:
select()方法——先选择所有父节点,再遍历每个父节点,选择其子节点

for ul in soup.select('ul'):
    print(ul.select('li'))

2:获取属性
节点类型是Tag类型,所以获取属性还可以用原来的方法(attrs)

3:获取文本
同理,可以调用string属性
另外,get_text()

from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
for li in soup.select('li'):
    print('Get Text:', li.get_text())
    print('String:', li.string)

输出效果是一样的

实战:使用beautifulsoup 提取 丁香园论坛的 回复 内容

import requests
from bs4 import BeautifulSoup as bs

url = 'http://www.dxy.cn/bbs/thread/626626#626626'
headers = {
    'User-Agent' : 'Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.1.6) '
    }

def get_page(url):
    url = 'http://www.dxy.cn/bbs/thread/626626#626626'
    response = requests.get(url)   #获得页面
    if response.status_code == 200:   #状态码
        return response.text       #页面的源代码
    return None

def analysis(html):
    # html=response.text
    soup = bs(html, 'lxml') #初始化BS对象,赋值给Soup
    info=soup.find_all(attrs={'class':'postbody'}) #得到回复内容
    for i in info:                                 #去掉杂乱信息
        print(i.text)
def main():    #定义main,目的:执行def的两个 函数
    html = get_page(url)
    analysis(html)

if __name__ == '__main__':    #执行此文件时,执行以下方法
    main()                    #执行main


#一个def内的 参数 在下一个定义 是用不了的,比如#html=response.text,不能把#去掉,去掉会是无定义的,因为 =右边 是上一个 def函数 的内容,不能作用于 此def中
#定义是抽象化的,尽量不具体化

2.2 XPath
返回:列表类型
常用规则:
nodename:选取此节点的所有子节点

/:从当前节点选取直接子节点

//:从当前节点选取子孙节点
注:一般用xpath规则 用//开头,来选取所有符合 要求 的节点

. :选取当前节点

… :选取当前节点的父节点

@:选取属性
属性匹配://title[@lang='eng'](选择title节点中 class为lang的,属性值为eng的)——已有属性值
属性值获取:xpath('//li/a/@href')
属性多值匹配(某个属性有多个值):通过contains()方法,第一个参数传入属性名称,第二个参数传入属性值,只要此属性包含所传入的属性值,就可以完成匹配
eg:

text = '''
  • first item
  • '''#class里面有两个属性li、li-first
    xpath('//li[contains(@class, "li")]/a/text()')
    

    多属性匹配(多个属性确定的一个节点):and
    eg:

  • first item
  • 匹配代码:

    xpath('//li[contains(@class, "li") and @name="item"]/a/text()')
    

    *:代表匹配所有节点

    text():获取节点中的文本

    按序选择:在选择的时候某些属性可能同时匹配了多个节点,但是只想要其中的某个节点——中括号传入索引的方法 ,获取特定次序的节点

    节点轴选择:
    用途:获取子元素、兄弟元素、父元素、祖先元素等

    ancestor轴,可以获取所有祖先节点

    attribute轴,可以获取所有属性值

    child轴,可以获取所有直接子节点

    descendant轴,可以获取所有子孙节点

    following轴,可以获取当前节点之后的所有节点

    following-sibling轴,可以获取当前节点之后的所有同级节点
    eg:
    xpath('//li[1]/ancestor::*') #:: 后可加限定条件

    more:http://www.w3school.com.cn/xpath/xpath_axes.asp

    XPath_MoreUsage:http://www.w3school.com.cn/xpath/index.asp
    实战:

    import requests
    from lxml import etree
    
    url = 'http://www.dxy.cn/bbs/thread/626626#626626'
    headers = {
        'User-Agent' : 'Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.1.6) '
        }
    
    def get_code(url):
        page = requests.get(url)    #页面获得
        if page.status_code == 200:
                return page.text   #源代码获得
        else:
            return None
    
    def analysis(html):
        #下一步,运用Xpath来提取 回复内容
        # //td [@class='postbody']
        html = etree.HTML(get_code(url))
        #目前的问题:在哪里使用XPath?怎么使用?——对html使用xpath
        result = html.xpath('//td[@class="postbody"]/text()') #获得td节点下的文本
        for i in result:    #去杂
            print(i)
    def main():
        html = get_code(url)
        analysis(html)
    
    if __name__ == '__main__':    #执行此文件时,执行以下方法
        main()   
    

    你可能感兴趣的:(解析工具的使用——Beautiful Soup、XPath)