• 1. Python BeautifulSoup
  • 2. Beautiful Soup 是一个可以从HTML或XML文件中提取数据的Python库.它能够通过你喜欢的转换器实现惯用的文档导航,查找,修改文档的方式. Beautiful Soup提供一些简单的、python式的函数用来处理导航、搜索、修改分析树等功能。它是一个工具箱,通过解析文档为用户提供需要抓取的数据,因为简单,所以不需要多少代码就可以写出一个完整的应用程序。 Beautiful Soup自动将输入文档转换为Unicode编码,输出文档转换为utf-8编码。你不需要考虑编码方式,除非文档没有指定一个编码方式,这时,Beautiful Soup就不能自动识别编码方式了。然后,你仅仅需要说明一下原始编码方式就可以了 Beautiful Soup已成为和lxml、html6lib一样出色的python解释器,为用户灵活地提供不同的解析策略或强劲的速度。
  • 3. bs4在windows环境下的安装1.到http://www.crummy.com/software/BeautifulSoup/网站上上下载,我下的版本是4.5.1 2.下载完成后解压缩到,python的安装目录 3.运行cmd,进入bs4文件夹,如图: 4.执行 setup.py build 5.执行 setup.py install 即可以完成安装。 安装完成之后,可以去import 一下检查是否安装成功,如图:
  • 4. 创建BeautifulSoup对象首先我们要导入bs4库 上面的html是我们要用到的字符串 然后我们创建一个BeautifulSoup对象
  • 5. 创建BeautifulSoup对象我们也可以调用本地HTML文件来创建对象,如下面所示: soup = BeautifulSoup(open('index.html')) 下面我们来打印一下soup对象的内容,格式化输出: print(soup.prettify()) The Dormouse's story 输出结果就是这种标准的html输出
  • 6. 获得标签中的内容针对上面的标准输出,我们举一个简单的例子(获得,html文件中所有的a标签中的内容) soup = BeautifulSoup(html_doc,"html.parser") for link in soup.find_all("a"): print(link.get("href")) 输出结果:
  • 7. 四大对象种类Beautiful Soup将复杂HTML文档转换成一个复杂的树形结构,每个节点都是Python对象,所有对象可以归纳为4种: Tag NavigableString BeautifulSoup Comment
  • 8. TagTag通俗点讲就是HTML中的一个个标签。 Elsie 这就是一个tag Beautiful Soup是如何方便的获取Tags? 形如这种方法: print(soup.title) print(type(soup.title)) 我们可以看到,我们获得了一个tag,并且获得他们的属性。
  • 9. Beautiful Soup获取tagsoup.title # The Dormouse's story soup.title.name # u'title' soup.title.string # u'The Dormouse's story' soup.title.parent.name # u'head' soup.p #

    The Dormouse's story

  • 10. Beautiful Soup获取tagsoup.p['class'] # u'title' soup.a # Elsie soup.find_all('a') # [Elsie, # Lacie, # Tillie] soup.find(id="link3") # Tillie
  • 11. nameTag有两个属性,name和attrs,我们来分别感受一下 print(soup.name) print(soup.head.name) print(soup.a.attrs) 我们还可以对这些属性和内容进行修改和删除
  • 12. NavigableString我们已经获得标签的内容,怎么样获取标签内部的文字呢?用.string。 它的类型是一个 NavigableString,翻译过来叫可以遍历的字符串。 接下来,们举个例子,并且检查它的类型: print(soup.p.string) print(type(soup.p.string))
  • 13. BeautifulSoupBeautifulSoup 对象表示的是一个文档的全部内容.大部分时候,可以把它当作 Tag 对象,是一个特殊的 Tag,我们可以分别获取它的类型,名称,以及属性来感受一下。 print(type(soup)) print(soup.name) print(soup.attrs)
  • 14. CommentComment 对象是一个特殊类型的 NavigableString 对象,其实输出的内容仍然不包括注释符号,但是如果不好好处理它,可能会对我们的文本处理造成意想不到的麻烦。 我们找一个带有注释的标签: print(soup.a) 运行结果(一个带有注释的a标签) print(soup.a.string) 运行结果(输出了注释,但是没有注释符号) Elsie 注:我们在打印.string的时候,最好先判断(根据类型), 避免注释与非注释混淆打印出来了 print(type(soup.a.string)) 运行结果(类型是Comment)
  • 15. 遍历文档树(直接子节点) 要点:.contents .children 属性 contents tag 的 .content 属性可以将tag的子节点以列表的方式输出 举个例子: print(soup.head.contents) 运行结果: [The Dormouse's story] 我们利用列表属性来获得其中的某一个值: print(soup.head.contents[0]) 运行结果: The Dormouse's story
  • 16. 遍历文档树(直接子节点).children 它返回的不是一个 list,不过我们可以通过遍历获取所有子节点。 我们打印输出 .children 看一下,可以发现它是一个 list 生成器对象。 查看其类型:print(soup.children) 运行结果: 遍历打印输出: for child in soup.body.children: print(child) 运行结果:
  • 17. 遍历文档树(所有子孙节点)要点:.descendants属性。 .descendants .contents 和 .children 属性仅包含tag的直接子点,.descendants 属性可以对所有tag的子孙节点进行递归循环,和 children类似,我们也需要遍历获取其中的内容。 代码如下: print(soup.head.descendants) for child in soup.head.descendants: print(child) 运行结果:
  • 18. 遍历文档树(节点内容)知识点:.string 属性 如果tag只有一个 NavigableString 类型子节点,那么这个tag可以 使用 .string 得到子节点。如果一个tag仅有一个子节点,那么这个tag也可以使用 .string 方法,输出结果与当前唯一子节点的 .string 结果相同。 通俗点说就是:如果一个标签里面没有标签了,那么 .string 就会返回标签里面的内容。如果标签里面只有唯一的一个标签了,那么 .string 也会返回最里面的内容 如果tag包含了多个子节点,tag就无法确定,string 方法应该调用哪个子节点的内容, .string 的输出结果是 None
  • 19. 遍历文档树(节点内容)例如: print(soup.head.string) 输出: The Dormouse's story print(soup.title.string) 输出:The Dormouse's story 如果tag包含太多子节点,例如: print(soup.html.string) 输出:None
  • 20. 遍历文档树(多个内容)知识点: .strings .stripped_strings 属性 .strings 获取多个内容,不过需要遍历获取 .stripped_strings  输出的字符串中可能包含了很多空格或空行,用 .stripped_strings 可以去除多余空白内容。
  • 21. 遍历文档树(父节点) 知识要点: .parent 属性 举个例子:print(soup.p.parent.name) 输出: 输入: content = soup.head.title.string print(content.parent.name) 输出:title
  • 22. 遍历文档树(全部父节点)知识要点:.parents 通过元素的 .parents 属性可以递归得到元素的所有父辈节点. 例如: content = soup.head.title.string for parent in content.parents: print(parent.name) 输出: title head html [document]
  • 23. 遍历文档树(兄弟节点)知识点:.next_sibling .previous_sibling 属性 知识点:.next_siblings .previous_siblings 属性 兄弟节点可以理解为和本节点处在统一级的节点,.next_sibling 属性获取了该节点的下一个兄弟节点,.previous_sibling 则与之相反,如果节点不存在,则返回 None 注意:实际文档中的tag的 .next_sibling 和 .previous_sibling 属性通常是字符串或空白,因为空白或者换行也可以被视作一个节点,所以得到的结果可能是空白或者换行 通过 .next_siblings 和 .previous_siblings 属性可以对当前节点的兄弟节点迭代输出
  • 24. 遍历文档树(兄弟节点)举个例子(还是之前的那个字符串) print(soup.p.next_sibiling) 输出:空白(实际上为空白) print(soup.p.previous_sibling) 输出:空白 print(soup.p.next_sibling.next_sibling) 输出:
  • 25. 遍历文档树(前后节点)知识点:.next_element .previous_element 属性 与 .next_sibling .previous_sibling 不同,它并不是针对于兄弟节点,而是在所有节点,不分层次 比如 head 节点为:The Dormouse's story 那么它的下一个节点为:print(soup.head.next_element) 输出:The Dormouse‘s story 并没有层次关系 知识点:.next_elements .previous_elements 属性 通过 .next_elements 和 .previous_elements 的迭代器就可以向前或向后访问文档的解析内容,就好像文档正在被解析一样
  • 26. 搜索文档树find_all( name , attrs , recursive , text , **kwargs ) find_all() 方法搜索当前tag的所有tag子节点,并判断是否符合过滤器的条件。 1.name参数 name 参数可以查找所有名字为 name 的tag,字符串对象会被自动忽略掉。 A.传字符串 最简单的过滤器是字符串.在搜索方法中传入一个字符串参数,Beautiful Soup会查找与字符串完整匹配的内容,下面的例子用于查找文档中所有的标签 print(soup.find_all("b")) 输出:[The Dormouse's story]
  • 27. find_allB.传正则表达式 如果传入正则表达式作为参数,Beautiful Soup会通过正则表达式的 match() 来匹配内容.下面例子中找出所有以b开头的标签,这表示和标签都应该被找到。 import re for tag in soup.find_all(re.compile("^a")): print(tag.name) 输出: a a a
  • 28. find_allC.传列表 如果传入列表参数,Beautiful Soup会将与列表中任一元素匹配的内容返回.下面代码找到文档中所有标签和标签。 for tag in (soup.find_all(["p","b"])): print(tag.name) 输出: p b p p
  • 29. find_all()D.传 True True 可以匹配任何值,下面代码查找到所有的tag,但是不会返回字符串节点。 for tag in (soup.find_all(True)): print(tag.name) 输出: html head title body p b p
  • 30. find_allE.传方法 如果没有合适过滤器,那么还可以定义一个方法,方法只接受一个元素参数,如果这个方法返回 True 表示当前元素匹配并且被找到,如果不是则反回 False。 下面方法校验了当前元素,如果包含 class 属性却不包含 id 属性,那么将返回 True: def has_tag_but_no_id(tag): return tag.has_attr("class") and not tag.has_attr("id") for tagName in (soup.find_all(has_tag_but_no_id)): print(tagName.name) 输出:
  • 31. keyword参数注意:如果一个指定名字的参数不是搜索内置的参数名,搜索时会把该参数当作指定名字tag的属性来搜索,如果包含一个名字为 id 的参数,Beautiful Soup会搜索每个tag的”id”属性 print(soup.find_all(id = "link2")) 输出: [Lacie] 如果传入 href 参数,Beautiful Soup会搜索每个tag的”href”属性 print(soup.find_all(href = re.compile("elsie"))) 输出: []
  • 32. keyword参数使用多个指定名字的参数可以同时过滤tag的多个属性 print(soup.find_all(href = re.compile("elsie"),id = "link1")) 输出: [] 在这里我们想用 class 过滤,不过 class 是 python 的关键词,这怎么办?加个下划线就可以 print(soup.find_all(class_ = "sister")) 输出(会以一个列表的形式展示): [, Lacie, Tillie]
  • 33. keyword参数有些tag属性在搜索不能使用,比如HTML5中的 data-* 属性 data_soup = BeautifulSoup("
    foo!
    ","html.parser") print(data_soup.find_all("data-foo")) 输出:[] 但是可以通过 find_all() 方法的 attrs 参数定义一个字典参数来搜索包含特殊属性的tag。 print(data_soup.find_all(attrs = {"data-foo":"value"})) 输出: [
    foo!
    ]
  • 34. text参数通过 text 参数可以搜搜文档中的字符串内容.与 name 参数的可选值一样, text 参数接受 字符串 , 正则表达式 , 列表, True. print(soup.find_all(text="Lacie")) 输出:['Lacie'] print(soup.find_all(text=["Lacie","Tillie"])) 输出:['Lacie', 'Tillie']
  • 35. limit 参数limit 参数 find_all() 方法返回全部的搜索结构,如果文档树很大那么搜索会很慢.如果我们不需要全部结果,可以使用 limit 参数限制返回结果的数量.效果与SQL中的limit关键字类似,当搜索到的结果数量达到 limit 的限制时,就停止搜索返回结果. 文档树中有3个tag符合搜索条件,但结果只返回了2个,因为我们限制了返回数量 print(soup.find_all("a",limit = 2)) 输出: [, Lacie]
  • 36. recursive 参数recursive 参数 调用tag的 find_all() 方法时,Beautiful Soup会检索当前tag的所有子孙节点,如果只想搜索tag的直接子节点,可以使用参数 recursive=False .
  • 37. 类似于find_all()的一些其他方法find( name , attrs , recursive , text , **kwargs ) 它与 find_all() 方法唯一的区别是 find_all() 方法的返回结果是值包含一个元素的列表,而 find() 方法直接返回结果 find_parents() find_parent() find_all() 和 find() 只搜索当前节点的所有子节点,孙子节点等. find_parents() 和 find_parent() 用来搜索当前节点的父辈节点,搜索方法与普通tag的搜索方法相同,搜索文档搜索文档包含的内容 find_next_siblings() find_next_sibling() 这2个方法通过 .next_siblings 属性对当 tag 的所有后面解析的兄弟 tag 节点进行迭代, find_next_siblings() 方法返回所有符合条件的后面的兄弟节点,find_next_sibling() 只返回符合条件的后面的第一个tag节点
  • 38. 类似于find_all()的一些其他方法find_previous_siblings() find_previous_sibling() 这2个方法通过 .previous_siblings 属性对当前 tag 的前面解析的兄弟 tag 节点进行迭代, find_previous_siblings() 方法返回所有符合条件的前面的兄弟节点, find_previous_sibling() 方法返回第一个符合条件的前面的兄弟节点 find_all_next() find_next() 这2个方法通过 .next_elements 属性对当前 tag 的之后的 tag 和字符串进行迭代, find_all_next() 方法返回所有符合条件的节点, find_next() 方法返回第一个符合条件的节点 find_all_previous() 和 find_previous() 这2个方法通过 .previous_elements 属性对当前节点前面的 tag 和字符串进行迭代, find_all_previous() 方法返回所有符合条件的节点, find_previous()方法返回第一个符合条件的节点
  • 39. CSS选择器我们在写 CSS 时,标签名不加任何修饰,类名前加点,id名前加 #,在这里我们也可以利用类似的方法来筛选元素,用到的方法是 soup.select(),返回类型是 list。 print(soup.select("title")) 输出:[The Dormouse‘s story](返回列表形式) 通过类名查找 print(soup.select(".sister")) 输出:[, Lacie, Tillie]
  • 40. Id和组合查找通过id查找 print(soup.select("#link2")) 输出:[Lacie] 组合查找(组合查找即和写 class 文件时,标签名与类名、id名进行的组合原理是一样的,例如查找 p 标签中,id 等于 link1的内容,二者需要用空格分开) print(soup.select("p #link1")) 输出:[]
  • 41. 属性查找属性查找 查找时还可以加入属性元素,属性需要用中括号括起来,注意属性和标签属于同一节点,所以中间不能加空格,否则会无法匹配到。 print(soup.select("a[class='sister']")) 输出: [, Lacie, Tillie]