Python 爬虫基础教程——BeautifulSoup抓取入门(2)

大家好,上篇推文介绍了BeautifulSoup抓取的一些基础用法,本篇内容主要是介绍BeautifulSoup模块的文档树使用以及实例。

一、遍历文档树

直接看代码吧

from bs4 import BeautifulSoup

html='      python 知识学堂      '
#上面是随便写的一个页面代码

soup=BeautifulSoup(html,'lxml')
#print(soup.prettify())
print("-------------------------------------------------分割符----------------------------------------------------") 
print(soup.head)                    # 获取head 标签
print("-------------------------------------------------分割符----------------------------------------------------") 
print(soup.a)                       #获取a 标签 默认是第一个
print("-------------------------------------------------分割符----------------------------------------------------") 
print('contents:')
print(soup.a.contents)              #tag的 .contents 属性可以将tag的子节点以列表的方式输出
print("-------------------------------------------------分割符----------------------------------------------------") 
print('children:')
for child in soup.form.children:    #获取标签下的一级子标签 
    print(child)
print("-------------------------------------------------分割符----------------------------------------------------") 
print('descendants:')
for child in soup.form.descendants: #获取标签下的所有tag子孙节点进行递归循环
    print(child)
print("-------------------------------------------------分割符----------------------------------------------------") 
print('strings:')
for str in soup.strings:            #输入标签内的字符串
    print(str)
print("-------------------------------------------------分割符----------------------------------------------------") 
print('stripped_strings:')
for str in soup.stripped_strings:   #输入标签内的字符串 去除空字符串
    print(str)

结果:


上面知识简单的举了几个获取树的节点的方式,还有很多其他的方式,比如获取父节点,兄弟节点等等。有点与jquery 遍历 DOM的概念类似。

二、搜索文档树

Beautiful Soup定义了很多搜索方法, 这里主要介绍一下比较常用的到的两个方法:find()和find_all(),其他的可以用法类似,举一反三。

2.1 过滤器

  1. 字符串
  2. 正则表达式
  3. 列表
  4. True
  5. 方法
from bs4 import BeautifulSoup
import re

html='      python 知识学堂      '
#上面是随便写的一个页面代码
soup=BeautifulSoup(html,'lxml')
print("-------------------------------------------------分割符----------------------------------------------------") 
#最简单的过滤器是字符串.在搜索方法中传入一个字符串参数,Beautiful Soup会查找与字符串完整匹配的内容,下面的例子用于查找文档中所有的标签
print("字符串:")
print(soup.find_all('a'))
print("-------------------------------------------------分割符----------------------------------------------------") 
#如果传入正则表达式作为参数,Beautiful Soup会通过正则表达式的 match() 来匹配内容.下面例子中找出所有以a开头的标签,这表示所有标签都应该被找到
print("正则表达式:")
print(soup.find_all(re.compile("^a")))
print("-------------------------------------------------分割符----------------------------------------------------") 
#如果传入列表参数,Beautiful Soup会将与列表中任一元素匹配的内容返回.下面代码找到文档中所有标签和标签
print("列表:")
print(soup.find_all(['a','head']))
print("-------------------------------------------------分割符----------------------------------------------------") 
#True 可以匹配任何值,下面代码查找到所有的tag,但是不会返回字符串节点
print("Ture:")
print(soup.find_all(True))

结果:

2.2 find_all()

Name:可以查找所有名字为 name 的tag,字符串对象会被自动忽略掉;

keyword 参数:如果一个指定名字的参数不是搜索内置的参数名,搜索时会把该参数当作指定名字tag的属性来搜索,如果包含一个名字为 id 的参数,Beautiful Soup会搜索每个tag的”id”属性;

按CSS搜索:按照CSS类名搜索tag的功能非常实用,但标识CSS类名的关键字 class 在Python中是保留字,使用 class 做参数会导致语法错误.从Beautiful Soup的4.1.1版本开始,可以通过 class_ 参数搜索有指定CSS类名的tag;

string 参数:通过 string 参数可以搜搜文档中的字符串内容.与 name 参数的可选值一样, string 参数接受 字符串 , 正则表达式 , 列表, True .;

limit 参数:find_all() 方法返回全部的搜索结构,如果文档树很大那么搜索会很慢.如果我们不需要全部结果,可以使用 limit 参数限制返回结果的数量.效果与SQL中的limit关键字类似,当搜索到的结果数量达到 limit 的限制时,就停止搜索返回结果;

recursive 参数:调用tag的 find_all() 方法时,Beautiful Soup会检索当前tag的所有子孙节点,如果只想搜索tag的直接子节点,可以使用参数 recursive=False .

from bs4 import BeautifulSoup

html='      python 知识学堂      '
#上面是随便写的一个页面代码
soup=BeautifulSoup(html,'lxml')
#print(soup.prettify())
print("通过tag的name:")
print(soup.find_all('head'))              #获取head 标签
print("通过keyword获取:")
print(soup.find_all(id="head"))           #获取Id 为head的所有标签
print("通过css类名获取:")
print(soup.find_all('a',class_='mnav'))   #获取所有a标签 并且class属性值为mnav
print("通过string获取:")
print(soup.find_all(string="知识"))       #获取所有a标签内容为python 的所有标签,全字符匹配
print("limit参数:")
print(soup.find_all("a",limit=2))         #limit表示获取的数量
print("recursive 参数:")
print(soup.find_all("a",recursive=false)) #recursive 默认为true 表示获取当前tag的所有子孙节点,如果为false 只搜索tag直接子节点

结果:

注意只有 find_all() 和 find() 支持 recursive 参数.

find()的方法跟find_all()基本一样,唯一的区别是 find_all() 方法的返回结果是值包含一个元素的列表,而 find() 方法直接返回结果。

2.3 输出

  1. 格式化输出
  2. 压缩输出
  3. 输出格式
  4. get_text()
from bs4 import BeautifulSoup

html='      python 知识学堂      '
#上面是随便写的一个页面代码
soup = BeautifulSoup(html,'lxml')
#prettify() 方法将Beautiful Soup的文档树格式化后以Unicode编码输出,每个XML/HTML标签都独占一行
print("格式化输出:")
print(soup.prettify())
print("-------------------------------------------------分割符----------------------------------------------------") 
#如果只想得到结果字符串,不重视格式,那么可以对一个 BeautifulSoup 对象或 Tag 对象使用Python的 unicode() 或 str() 方法
print("压缩输出:")
print(str(soup))
print("-------------------------------------------------分割符----------------------------------------------------") 
#Beautiful Soup输出是会将HTML中的特殊字符转换成Unicode,比如“&lquot;”
print("输出格式:")
print(str(BeautifulSoup("&&*&*",'lxml')))
print("-------------------------------------------------分割符----------------------------------------------------") 
#如果只想得到tag中包含的文本内容,那么可以用 get_text() 方法,这个方法获取到tag中包含的所有文版内容包括子孙tag中的内容,并将结果作为Unicode字符串返回
print("get_text():")
print(soup.get_text())
print("-------------------------------------------------分割符----------------------------------------------------") 

结果我就不贴出来了,自己执行一下就知道了。

当然还有别的很多方法,在这里就不再赘述了,可以直接参考官方

https://beautifulsoup.readthedocs.io/zh_CN/v4.4.0/

Buaautiful soup 的功能还是很强大的,这里只是简单的描述了一下爬虫常用的一些东西。

下面就来实操一下吧,还是以获取省市区为例子

三、实例

我们还是用上篇的获取省市区来举例子。

import requests
from bs4 import BeautifulSoup
import time

class Demo():
    def __init__(self):
        try:
            base_url = 'http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2019/'
            trlist = self.get_data(base_url, "provincetable",'provincetr') #查看页面,就知道所有的省所在的tr上都有唯一的class='provincetr'
            for tr in trlist:
                for td in tr:
                    if td.a is None:
                        continue
                    p_name = td.a.get_text()
                    c_url = base_url + td.a.get('href')             #获取下级城市的地址
                    print("省:" + p_name) #获取每个省
                    # time.sleep(0.5)
                    trs = self.get_data(c_url, "citytable","citytr")
                    for tr in trs:  #循环每个市
                        if tr.find_all('td')[1] is None:
                            continue
                        #c_code = tr.find_all('td')[0].string       #获取城市code
                        c_name = tr.find_all('td')[1].string        #获取城市 name
                        ct_url = base_url + tr.find_all('td')[1].a.get('href') #获取下级区的地址
                        print(p_name+"-"+c_name)
                        time.sleep(0.5)
                        trs1 = self.get_courtydata(ct_url)
                        if trs1 is None:
                            continue
                        for tr1 in trs1:  #循环每个区
                            if tr1.find_all('td')[1] is None:
                                continue
                            #ct_code = tr.find_all('td')[0].string  #获取区code
                            ct_name = tr1.find_all('td')[1].string  #获取区name
                            print(p_name+"-"+c_name+"-"+ct_name)
        except:
            print("出错了")

    def get_data(self, url, table_attr,attr):
        response = requests.get(url)
        response.encoding = 'gb2312'  #编码转换
        soup = BeautifulSoup(response.text, 'lxml')  #使用lxml的解析器
        table = soup.find('table',class_=table_attr) #查看页面元素就知道数据都在第二个 tbody
        trlist = table.find_all('tr',class_=attr)
        return trlist

    def get_courtydata(self, url):
        response = requests.get(url)
        response.encoding = 'gb2312'                 #编码转换
        soup = BeautifulSoup(response.text, 'lxml')  #使用lxml的解析器
        towntr=soup.find('table',class_='towntable')
        if towntr is not None:
            table = soup.find('table',class_='towntable')
            trlist = table.find_all('tr',class_='towntr')
        else:
            table = soup.find('table',class_='countytable')
            trlist = table.find_all('tr',class_='countytr')
        return trlist

if __name__ == '__main__':
    Demo()

结果

直接给大家看一下获取到的最后一个省市区的结果了,大家注意每次获取的页面信息时的时间间隔;

四、总结

本篇文章讲述了关于BeautifulSoup的一些基础的内容,主要是与爬虫相关的,关于BeautifulSoup其他功能还有很多,可以区官网上自行学习。

贴一下BeautifulSoup官网地址:

https://beautifulsoup.readthedocs.io/zh_CN/v4.4.0/

你可能感兴趣的:(Python 爬虫基础教程——BeautifulSoup抓取入门(2))