您的位置:首页 > 编程语言 > Python开发

Python爬虫实战一之使用Beautiful Soup抓取‘谣言百科’的分类内容

2017-07-15 14:12 881 查看
Beautiful Soup功能介绍


简介

   Beautiful Soup 是一个可以从HTML或XML文件中提取数据的Python库.它能够通过你喜欢的转换器实现惯用的文档导航,查找,修改文档的方式.Beautiful Soup会帮你节省数小时甚至数天的工作时间.

   Beautiful Soup提供一些简单的、python式的函数用来处理导航、搜索、修改分析树等功能。它是一个工具箱,通过解析文档为用户提供需要抓取的数据,因为简单,所以不需要多少代码就可以写出一个完整的应用程序。Beautiful Soup自动将输入文档转换为Unicode编码,输出文档转换为utf-8编码。你不需要考虑编码方式,除非文档没有指定一个编码方式,这时,Beautiful
Soup就不能自动识别编码方式了。

支持的解析器

Beautiful Soup支持Python标准库中的HTML解析器,还支持一些第三方的解析器,其中一个是lxml .根据操作系统不同,可以选择下列方法来安装lxml

解析器

 使用方法优势劣势
Python标准库
BeautifulSoup(markup,"html.parser")
Python的内置标准库
执行速度适中
文档容错能力强
Python 2.7.3 or 3.2.2)前的版本中文档容错能力差
lxml HTML 解析器
BeautifulSoup(markup,"lxml")
速度快
文档容错能力强
需要安装C语言库
lxml XML 解析器
BeautifulSoup(markup,["lxml-xml"])

BeautifulSoup(markup,"xml")

速度快
唯一支持XML的解析器
需要安装C语言库
html5lib
BeautifulSoup(markup,"html5lib")
最好的容错性
以浏览器的方式解析文档
生成HTML5格式的文档
速度慢
不依赖外部扩

使用简单案例

from bs4 import BeautifulSoup
soup = BeautifulSoup(open("index.html"))
soup = BeautifulSoup("<html>data</html>")
print BeautifulSoup("Hello World!")
执行结果:<html><body><p>Hello World!</p></body></htm



解析的对象种类

    Beautiful Soup将复杂HTML文档转换成一个复杂的树形结构,每个节点都是Python对象,所有对象可以归纳为4种:
Tag
,
NavigableString

,
BeautifulSoup
,
Comment


<1>Tag

Tag
对象与XML或HTML原生文档中的tag相同,

soup = BeautifulSoup('<b class="boldest">Extremely bold</b>')
tag = soup.b
type(tag)
# <class 'bs4.element.Tag'>

(1)tag中最重要的属性: name和attributes

每个tag都有自己的名字,通过
.name
来获取,一个tag可能有很多个属性. tag
<bclass="boldest">

有一个 “class” 的属性,值为 “boldest” . tag的属性的操作方法与字典相同,如下

getsoup=BeautifulSoup("<html><body><p class='test'>Hello World!</p></body></html>")
tag=getsoup.p
print tag
print tag.name
print tag['class']
----结果----
<body><p>Hello World!</p></body>
body
['test']

(2) 可以遍历的字符串

字符串常被包含在tag内.Beautiful Soup用
NavigableString
类来包装tag中的字符串:

print tag.string-----------Hello World!

(3)注释及特殊字符串

Tag
,
NavigableString
,
BeautifulSoup

几乎覆盖了html和xml中的所有内容,但是还有一些特殊对象.容易让人担心的内容是文档的注释部分:

markup = "<b><!--Hey, buddy. Want to buy a used parser?--></b>"
soup = BeautifulSoup(markup)
comment = soup.b.string
print type(comment)
print comment

<class 'bs4.element.NavigableString'>
Hey, buddy. Want to buy a used parser?

(4)遍历文档树

子节点

一个Tag可能包含多个字符串或其它的Tag,这些都是这个Tag的子节点.Beautiful Soup提供了许多操作和遍历子节点的属性.

注意: Beautiful Soup中字符串节点不支持这些属性,因为字符串没有子节点

循环遍历:

如果想要得到所有的<a>标签,或是通过名字得到比一个tag更多的内容的时候,就需要用到Searching the tree 中描述的方法,比如: find_all()

print soup.find_all('a')

1).contents 和 .children

tag的
.contents
属性可以将tag的子节点以列表的方式输出

通过tag的 .children 生成器,可以对tag的子节点进行循环

2).descendants 属性可以对所有tag的子孙节点进行递归循环 [5]

3).strings 和 stripped_strings

如果tag中包含多个字符串 [2] ,可以使用 .strings 来循环获取:

输出的字符串中可能包含了很多空格或空行,使用 .stripped_strings 可以去除多余空白内容

父节点

1).parent

通过 .parent 属性来获取某个元素的父节点.在例子“爱丽丝”的文档中,<head>标签是<title>标签的父节点

2).parents

通过元素的 .parents 属性可以递归得到元素的所有父辈节点,下面的例子使用了 .parents 方法遍历了<a>标签到根节点的所有节点.

兄弟节点

1).next_sibling 和 .previous_sibling

在文档树中,使用 .next_sibling 和 .previous_sibling 属性来查询兄弟节点

2).next_siblings 和 .previous_siblings

通过 .next_siblings 和 .previous_siblings 属性可以对当前节点的兄弟节点迭代输出

回退或前进

1).next_element 和 .previous_element

.next_element 属性指向解析过程中下一个被解析的对象(字符串或tag),结果可能与 .next_sibling 相同,但通常是不一样的.

2).next_elements 和 .previous_elements

通过 .next_elements 和 .previous_elements 的迭代器就可以向前或向后访问文档的解析内容,就好像文档正在被解析一样:

(5)搜索文档树

过滤器----find_all()

1)字符串

最简单的过滤器是字符串.在搜索方法中传入一个字符串参数,Beautiful Soup会查找与字符串完整匹配的内容,下面的例子用于查找文档中所有的<b>标签:

soup.find_all('b')

2)正则表达式

如果传入正则表达式作为参数,Beautiful Soup会通过正则表达式的 match() 来匹配内容.下面例子中找出所有以b开头的标签,这表示<body>和<b>标签都应该被找到

import re

for tag in soup.find_all(re.compile("^b")):

    print(tag.name)

3)列表

如果传入列表参数,Beautiful Soup会将与列表中任一元素匹配的内容返回.下面代码找到文档中所有<a>标签和<b>标签:

soup.find_all(["a", "b"])

4)True

True 可以匹配任何值,下面代码查找到所有的tag,但是不会返回字符串节点

for tag in soup.find_all(True):

    print(tag.name)

5)方法

如果没有合适过滤器,那么还可以定义一个方法,方法只接受一个元素参数 [4] ,如果这个方法返回 True 表示当前元素匹配并且被找到,如果不是则反回 False

下面方法校验了当前元素,如果包含 class 属性却不包含 id 属性,那么将返回 True:

def has_class_but_no_id(tag):

    return tag.has_attr('class') and not tag.has_attr('id')

将这个方法作为参数传入 find_all() 方法,将得到所有<p>标签:

soup.find_all(has_class_but_no_id)

# [<p class="title"><b>The Dormouse's story</b></p>,

#  <p class="story">Once upon a time there were...</p>,

#  <p class="story">...</p>]

6)name 参数

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

简单的用法如下:

soup.find_all("title")

# [<title>The Dormouse's story</title>]

重申: 搜索 name 参数的值可以使任一类型的 过滤器 ,字符窜,正则表达式,列表,方法或是 True .

keyword 参数

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

soup.find_all(id='link2')

# [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]

如果传入 href 参数,Beautiful Soup会搜索每个tag的”href”属性:

soup.find_all(href=re.compile("elsie"))

# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>]

搜索指定名字的属性时可以使用的参数值包括 字符串 , 正则表达式 , 列表, True .

其它详见:http://beautifulsoup.readthedocs.io/zh_CN/v4.4.0/

Beautiful Soup抓取‘谣言百科’的分类内容

源代码:

# -*- coding:UTF-8 -*-
# 2017年7月15日
# 爬虫目标网站:http://www.yaoyanbaike.com/
# 获取信息BeautifulSoup+request
import urllib2
from bs4 import BeautifulSoup
import re
import codecs

if __name__ == "__main__":
    text_file_number = 0    # 同一类新闻下的索引数
    number = 1  # 同类别新闻不同页面下的索引数
    while (number <= 2):
        if number==1:   # 第一个新闻下地址是baby不是baby_数字所以要区分判断一下
            get_url = 'http://www.yaoyanbaike.com/category/baby.html'
        else:
            get_url = 'http://www.yaoyanbaike.com/category/baby_'+str(number)+'.html'   #这个是baby_数字,number就是目录索引数
        head = {}   #设置头
        head['User-Agent'] = 'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:52.0) Gecko/20100101 Firefox/52.0'
        # 模拟浏览器模式,定制请求头
        download_req_get = urllib2.Request(url = get_url, headers = head)
        # 设置Request
        download_response_get = urllib2.urlopen(download_req_get)
        # 设置urlopen获取页面所有内容
        download_html_get = download_response_get.read().decode('UTF-8','ignore')
        # UTF-8模式读取获取的页面信息标签和内容
        soup_texts = BeautifulSoup(download_html_get, 'lxml')
        # BeautifulSoup读取页面html标签和内容的信息        
        for link  in soup_texts.find_all(["a"]):
            print(str(text_file_number)+"   "+str(number)+"    "+str(link.get('title')))
            # 打印文件地址用于测试
            s=link.get('href')
            if s.find("/a/") == -1:
                print("错误网址")   # 只有包含"/a/"字符的才是有新闻的有效地址
            else:
                download_url = link.get('href')
                head = {}
                head['User-Agent'] = 'Mozilla/5.0 (Linux; Android 4.1.1; Nexus 7 Build/JRO03D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166  Safari/535.19'
                download_req = urllib2.Request(url = "http://www.yaoyanbaike.com"+download_url, headers = head)
                download_response = urllib2.urlopen(download_req)
                download_html = download_response.read().decode('UTF-8','ignore')
                soup_texts = BeautifulSoup(download_html, 'lxml')
                texts = soup_texts.find_all('article')
                soup_text = BeautifulSoup(str(texts), 'lxml')
                p = re.compile("<[^>]+>")  
                text=p.sub("", str(soup_text))
                # 去除页面标签
                f1 = codecs.open('E:\pic\\'+str(text_file_number)+'--'+str(link.get('title')).decode('utf-8')+'.txt','w','UTF-8')
#                 f1 = codecs.open('E:\pic\\'+str(text_file_number)+'.txt','w','UTF-8')
                # 排序id+按标题名称将信息存储在本地
                f1.write(text)
                f1.close()
                text_file_number = text_file_number + 1
        number = number + 1

执行结果:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐