美文网首页Python
Python 爬虫基础教程——BeautifulSoup抓取入门

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

作者: 那个百分十先生 | 来源:发表于2021-03-26 23:25 被阅读0次

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

    一、遍历文档树

    直接看代码吧

    from bs4 import BeautifulSoup
    
    html='<html> <head> <meta content="text/html;charset=utf-8" http-equiv="content-type"/> <meta content="IE=Edge" http-equiv="X-UA-Compatible"/> <meta content="always" name="referrer"/> <title> python 知识学堂 </title> </head> <body > <div id="wrapper"> <div id="head"> <div class="head_wrapper"> <div class="s_form"> <div class="s_form_wrapper"> <div id="lg"> <img height="129" hidefocus="true" src="/bd_logo1.png" width="270"/> </div> <form action="/" class="form" id="form" name="fm"> <span class="bg s_ipt_wr"> <input autocomplete="off" autofocus="autofocus" class="keyword" id="kw" maxlength="255" name="wd" value=""/> </span> <span class="bg s_btn_wr"> <input autofocus="" class="bg s_btn" id="su" type="submit" value="搜索"/> </span> </form> </div> </div> <div id="u1"> <a class="mnav" href="/link_1" > Python</a> <a class="mnav" href="/link_2" > 知识 </a> <a class="mnav" href="/link_3"> 学堂 </a> <a class="mnav" href="/link_4" > 欢迎 </a> <a class="mnav" href="/link_5"> 您 </a> </div> </div> </div> </div> </body> </html>'
    #上面是随便写的一个页面代码
    
    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='<html> <head> <meta content="text/html;charset=utf-8" http-equiv="content-type"/> <meta content="IE=Edge" http-equiv="X-UA-Compatible"/> <meta content="always" name="referrer"/> <title> python 知识学堂 </title> </head> <body > <div id="wrapper"> <div id="head"> <div class="head_wrapper"> <div class="s_form"> <div class="s_form_wrapper"> <div id="lg"> <img height="129" hidefocus="true" src="/bd_logo1.png" width="270"/> </div> <form action="/" class="form" id="form" name="fm"> <span class="bg s_ipt_wr"> <input autocomplete="off" autofocus="autofocus" class="keyword" id="kw" maxlength="255" name="wd" value=""/> </span> <span class="bg s_btn_wr"> <input autofocus="" class="bg s_btn" id="su" type="submit" value="搜索"/> </span> </form> </div> </div> <div id="u1"> <a class="mnav" href="/link_1" > Python</a> <a class="mnav" href="/link_2" >知识</a> <a class="mnav" href="/link_3"> 学堂 </a> <a class="mnav" href="/link_4" > 欢迎 </a> <a class="mnav" href="/link_5"> 您 </a> </div> </div> </div> </div> </body> </html>'
    #上面是随便写的一个页面代码
    soup=BeautifulSoup(html,'lxml')
    print("-------------------------------------------------分割符----------------------------------------------------") 
    #最简单的过滤器是字符串.在搜索方法中传入一个字符串参数,Beautiful Soup会查找与字符串完整匹配的内容,下面的例子用于查找文档中所有的<a>标签
    print("字符串:")
    print(soup.find_all('a'))
    print("-------------------------------------------------分割符----------------------------------------------------") 
    #如果传入正则表达式作为参数,Beautiful Soup会通过正则表达式的 match() 来匹配内容.下面例子中找出所有以a开头的标签,这表示所有<a>标签都应该被找到
    print("正则表达式:")
    print(soup.find_all(re.compile("^a")))
    print("-------------------------------------------------分割符----------------------------------------------------") 
    #如果传入列表参数,Beautiful Soup会将与列表中任一元素匹配的内容返回.下面代码找到文档中所有<a>标签和<head>标签
    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='<html> <head> <meta content="text/html;charset=utf-8" http-equiv="content-type"/> <meta content="IE=Edge" http-equiv="X-UA-Compatible"/> <meta content="always" name="referrer"/> <title> python 知识学堂 </title> </head> <body > <div id="wrapper"> <div id="head"> <div class="head_wrapper"> <div class="s_form"> <div class="s_form_wrapper"> <div id="lg"> <img height="129" hidefocus="true" src="/bd_logo1.png" width="270"/> </div> <form action="/" class="form" id="form" name="fm"> <span class="bg s_ipt_wr"> <input autocomplete="off" autofocus="autofocus" class="keyword" id="kw" maxlength="255" name="wd" value=""/> </span> <span class="bg s_btn_wr"> <input autofocus="" class="bg s_btn" id="su" type="submit" value="搜索"/> </span> </form> </div> </div> <div id="u1"> <a class="mnav" href="/link_1" > Python</a> <a class="mnav" href="/link_2" >知识</a> <a class="mnav" href="/link_3"> 学堂 </a> <a class="mnav" href="/link_4" > 欢迎 </a> <a class="mnav" href="/link_5"> 您 </a> </div> </div> </div> </div> </body> </html>'
    #上面是随便写的一个页面代码
    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='<html> <head> <meta content="text/html;charset=utf-8" http-equiv="content-type"/> <meta content="IE=Edge" http-equiv="X-UA-Compatible"/> <meta content="always" name="referrer"/> <title> python 知识学堂 </title> </head> <body > <div id="wrapper"> <div id="head"> <div class="head_wrapper"> <div class="s_form"> <div class="s_form_wrapper"> <div id="lg"> <img height="129" hidefocus="true" src="/bd_logo1.png" width="270"/> </div> <form action="/" class="form" id="form" name="fm"> <span class="bg s_ipt_wr"> <input autocomplete="off" autofocus="autofocus" class="keyword" id="kw" maxlength="255" name="wd" value=""/> </span> <span class="bg s_btn_wr"> <input autofocus="" class="bg s_btn" id="su" type="submit" value="搜索"/> </span> </form> </div> </div> <div id="u1"> <a class="mnav" href="/link_1" > Python</a> <a class="mnav" href="/link_2" >知识</a> <a class="mnav" href="/link_3"> 学堂 </a> <a class="mnav" href="/link_4" > 欢迎 </a> <a class="mnav" href="/link_5"> 您 </a> </div> </div> </div> </div> </body> </html>'
    #上面是随便写的一个页面代码
    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抓取入门

        本文链接:https://www.haomeiwen.com/subject/rmfehltx.html