Beautiful Soup4学习笔记(四):搜索文档树

Beautiful Soup定义了很多搜索方法,这里着重介绍2个: find() 和 find_all() .其它方法的参数和用法类似,请读者举一反三.

使用 find_all() 类似的方法可以查找到想要查找的文档内容:

过滤器

介绍 find_all() 方法前,先介绍一下过滤器的类型 ,这些过滤器贯穿整个搜索的API.
过滤器可以被用在tag的name种,节点的属性中,字符串中或他们的混合中。

字符串

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

>>> soup.find_all('b') 
[<b>The Dormouse's story</b>]

如果传入字节码参数,Beautiful Soup会当作UTF-8编码,可以传入一段Unicode 编码来避免Beautiful Soup解析编码出错

正则表达式

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

>>> import re  
>>> for tag  in soup.find_all(re.compile("^b")):
...     print(tag.name) 
...     
... 
body
b

下面代码找出所有名字中包含”t”的标签:

 >>> for  tag in soup.find_all(re.compile("t")):
...     print(tag.name)  
...     
... 
html
title

列表

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

>>> soup.find_all(["a","b"]) 
[<b>The Dormouse's story</b>, <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>, <a class="siste
r" href="http://example.com/lacie" id="link2">Lacie</a>, <a class="sister" href="http://example.com/tillie" id="link3
">Tillie</a>]

True

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

>>> for tag in soup.find_all(True): 
...     print(tag.name)   
...     
... 
html
head
title
body
p
b
p
a
a
a
p

方法

如果没有合适过滤器,那么还可以定义一个方法,方法只接受一个元素参数,如果这个方法返回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 three little sisters;
 and their names were
<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a> and
<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>, <p class="story">...</p>]

注意:并没有生效,改天问个大神看看

通过一个方法来过滤一类标签属性的时候, 这个方法的参数是要被过滤的属性的值, 而不是这个标签. 下面的例子是找出 href 属性不符合指定正则的 a 标签.

>>> def not_lacie(href):
...     return   href and not re.compile("lacie").search(href) 
... 
>>> soup.find_all(href=not_lacie)
[<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>, <a class="sister" href="http://example.com/t
illie" id="link3">Tillie</a>]

标签过滤方法可以使用复杂方法. 下面的例子可以过滤出前后都有文字的标签.

>>> from bs4 import NavigableString
>>> def surrounded_by_strings(tag):
...     return ( isinstance(tag.next_element,NavigableString)  and isinstance(tag.previous_element,NavigableString))
... 
>>> for tag in soup.find_all(surrounded_by_strings): 
...     print(tag.name)   
...     
... 
body
p
a
a
a
p

find_all()

find_all(name,attrs,recursive,string,**kwargs)
find_all() 方法搜索当前tag的所有tag子节点,并判断是否符合过滤器的条件.这里有几个例子:

>>> soup.find_all('title')
[<title>The Dormouse's story</title>]
>>> soup.find_all("p","title") 
[<p class="title"><b>The Dormouse's story</b></p>]
>>> soup.find_all("a")
[<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>, <a class="sister" href="http://example.com/lacie" id="link2
">Lacie</a>, <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
>>> soup.find_all(id = "link2") 
[<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]
>>> import re   
>>> soup.find(string = re.compile("sisters") 
... )
'Once upon a time there were three little sisters; and their names were\n'

有几个方法很相似,还有几个方法是新的,参数中的 string 和 id 是什么含义? 为什么 find_all("p", "title") 返回的是CSS Class为”title”的<p>标签? 我们来仔细看一下 find_all() 的参数

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 .
下面的例子在文档树中查找所有包含 id 属性的tag,无论 id 的值是什么:

>>> soup.find_all(id=True) 
[<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>, <a class="sister" href="http://example.com/lacie" id="link2
">Lacie</a>, <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

使用多个指定名字的参数可以同时过滤tag的多个属性:

>>> soup.find_all(href=re.compile("elsie"), id='link1')
[<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>]

有些tag属性在搜索不能使用,比如HTML5中的 data-* 属性:

>>> data_soup = BeautifulSoup('<div data-foo="value">foo!</div>') 
>>> data_soup.find_all(data-foo="value") 
  File "<input>", line 1
SyntaxError: keyword can't be an expression

但是可以通过find_all()方法的attrs参数定义一个字典参数来搜索包含特殊属性的tag:

>>> data_soup.find_all(attrs={"data-foo":"value"}) 
[<div data-foo="value">foo!</div>]

按CSS搜索

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

>>> soup.find_all("a",class_="sister") 
[<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>, <a class="sister" href="http://example.com/lacie" id="link2
">Lacie</a>, <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

class_ 参数同样接受不同类型的 过滤器 ,字符串,正则表达式,方法或 True :

>>> soup.find_all(class_=re.compile("itl")) 
[<p class="title"><b>The Dormouse's story</b></p>]
>>> def has_six_characters(css_class):
...     return css_class is not None and len(css_class) == 6 
...  
>>> soup.find_all(class_=has_six_characters) 
[<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>, <a class="sister" href="http://example.com/lacie" id="link2
">Lacie</a>, <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

tag的 class
属性是 多值属性 .按照CSS类名搜索tag时,可以分别搜索tag中的每个CSS类名:

>>> css_soup = BeautifulSoup('<p class="body strikeout"></p>') 
>>> css_soup.find_all("p", class_="strikeout") 
[<p class="body strikeout"></p>]
>>> 
>>> css_soup.find_all("p", class_="body") 
[<p class="body strikeout"></p>]

搜索 class 属性时也可以通过CSS值完全匹配:

>>> css_soup.find_all("p", class_="body strikeout")
[<p class="body strikeout"></p>]

完全匹配 class 的值时,如果CSS类名的顺序与实际不符,将搜索不到结果:

>>> soup.find_all("a",attrs={"class":"sister"}) 
[<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>, <a class="sister" href="http://example.com/lacie" id="link2
">Lacie</a>, <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

string参数

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

>>> soup.find_all(string="Elsie")
['Elsie']
>>> 
>>> soup.find_all(string=["Tillie", "Elsie", "Lacie"])
['Elsie', 'Lacie', 'Tillie']
>>> soup.find_all(string=re.compile("Dormouse"))
["The Dormouse's story", "The Dormouse's story"]

>>> def is_the_only_string_within_a_tag(s):
...     #""Return True if this string is the only child of its parent tag.""
...     return (s == s.parent.string) 
...  
... 
>>> soup.find_all(string=is_the_only_string_within_a_tag) 
["The Dormouse's story", "The Dormouse's story", 'Elsie', 'Lacie', 'Tillie', '...']

虽然 string 参数用于搜索字符串,还可以与其它参数混合使用来过滤tag.Beautiful Soup会找到 .string 方法与 string 参数值相符的tag.下面代码用来搜索内容里面包含“Elsie”的<a>标签

>>> soup.find_all("a", string="Elsie") 
[<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>]

limit参数

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

文档树中有3个tag符合搜索条件,但结果只返回了2个,因为我们限制了返回数量:

>>> soup.find_all("a", limit=2) 
[<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>, <a class="sister" href="http://example.com/lacie" id="link2
">Lacie</a>]

recursive参数

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

<html>
 <head>
  <title>
   The Dormouse's story
  </title>
 </head>
...

是否使用 recursive 参数的搜索结果:

>>> html_doc = """
... <html>
...  <head>
...   <title>
...    The Dormouse's story
...   </title>
...  </head>
... ...
... """
>>> soup = BeautifulSoup(html_doc, 'html.parser')   
>>> soup.html.find_all("title") 
[<title>
   The Dormouse's story
  </title>]
>>> soup.html.find_all("title",recursive=False) 
[]

<title>标签在 <html> 标签下, 但并不是直接子节点, <head> 标签才是直接子节点. 在允许查询所有后代节点时 Beautiful Soup 能够查找到 <title> 标签. 但是使用了 recursive=False 参数之后,只能查找直接子节点,这样就查不到 <title> 标签了.

Beautiful Soup 提供了多种DOM树搜索方法. 这些方法都使用了类似的参数定义. 比如这些方法: find_all(): name, attrs, text, limit. 但是只有 find_all() 和 find() 支持 recursive 参数.

像调用find_all()一样调用tag

find_all() 几乎是Beautiful Soup中最常用的搜索方法,所以我们定义了它的简写方法. BeautifulSoup 对象和 tag 对象可以被当作一个方法来使用,这个方法的执行结果与调用这个对象的 find_all() 方法相同,下面两行代码是等价的:

>>> soup.find_all("a") 
[<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>, <a class="sister" href="http://example.com/l
acie" id="link2">Lacie</a>, <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
>>> soup("a")
[<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>, <a class="sister" href="http://example.com/l
acie" id="link2">Lacie</a>, <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

这两行代码也是等价的:

>>> soup.title.find_all(string=True) 
["The Dormouse's story"]
>>> soup.title(string=True) 
["The Dormouse's story"]
>>> 

find()

find(name,attrs,recursive,string,**kwargs)
find_all() 方法将返回文档中符合条件的所有tag,尽管有时候我们只想得到一个结果.比如文档中只有一个<body>标签,那么使用 find_all() 方法来查找<body>标签就不太合适, 使用 find_all 方法并设置 limit=1 参数不如直接使用 find() 方法.下面两行代码是等价的:

>>> soup.find_all("title",limit=1)
[<title>The Dormouse's story</title>]
>>> soup.find("title") 
<title>The Dormouse's story</title>

唯一的区别是dind_all()方法返回的结果是包含值的元素列表,而find()函数直接返回结果。

find_all() 方法没有找到目标是返回空列表, find() 方法找不到目标时,返回 None .

>>> print(soup.find("nojscnef"))
None

soup.head.title是tag的名字方法的简写,这个简写的原理就是多次调用当前tag的find()方法:

>>> soup.head.title
<title>The Dormouse's story</title>
>>> soup.find("head").find("title") 
<title>The Dormouse's story</title>

find_parents()和find_parent()

find_parents(name,attrs,recursive,string,kwargs)
find_parent(name,attrs,recursive,string,
kwargs)
我们已经用了很大篇幅来介绍 find_all() 和 find() 方法,Beautiful Soup中还有10个用于搜索的API.它们中的五个用的是与 find_all() 相同的搜索参数,另外5个与 find() 方法的搜索参数类似.区别仅是它们搜索文档的不同部分.

注意:find_all()和find()只搜索当前节点的所有子节点,子孙节点等。find_parents()和find_parent()用来搜索当前节点的父辈节点,搜索方法与普通的tag的搜索方法相同,搜索文档包含的内容。我们从一个文档中的一个叶子节点开始:

>>> a_string  = soup.find(string = "Lacie") 
>>> a_string 
'Lacie'
>>> a_string.find_parents("a")
[<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]
>>> a_string.find_parent("p") 
<p class="story">Once upon a time there were three little sisters; and their names were
<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a> and
<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>

>>> a_string.find_parents("p","title")
[]

文档中的一个<a>标签是当前叶子节点的直接父节点,所以可以被找到.还有一个<p>标签,是目标叶子节点的间接父辈节点,所以也可以被找到.包含class值为”title”的<p>标签不是不是目标叶子节点的父辈节点,所以通过 find_parents() 方法搜索不到.

find_parent() 和 find_parents() 方法会让人联想到 .parent 和 .parents 属性.它们之间的联系非常紧密.搜索父辈节点的方法实际上就是对 .parents 属性的迭代搜索.

find_next_siblings()和find_next_sibling()

find_next_siblings( name , attrs , recursive , string , **kwargs )
find_next_sibling( name , attrs , recursive , string , **kwargs )

这2个方法通过 .next_siblings 属性对当tag的所有后面解析 [5] 的兄弟tag节点进行迭代, find_next_siblings() 方法返回所有符合条件的后面的兄弟节点, find_next_sibling()只返回符合条件的后面的第一个tag节点.

>>> first_link = soup.a  
>>> first_link 
<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>
>>> first_link.find_next_siblings("a") 
[<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>, <a class="sister" href="http://example.com/t
illie" id="link3">Tillie</a>]
>>> first_story_paragraph = soup.find("p","story") 
>>> first_story_paragraph.find_next_sibling("p") 
<p class="story">...</p>

find_previous_siblings() 和 find_previous_sibling()

find_previous_siblings( name , attrs , recursive , string , **kwargs )
find_previou_siblings( name , attrs , recursive , string , **kwargs )

这2个方法通过 .previous_siblings 属性对当前tag的前面解析 [5] 的兄弟tag节点进行迭代, find_previous_siblings() 方法返回所有符合条件的前面的兄弟节点, find_previous_sibling() 方法返回第一个符合条件的前面的兄弟节点:

>>> last_link = soup.find("a",id = "link3") 
>>> last_link 
<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>
>>> last_link.find_previous_siblings("a") 
[<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>, <a class="sister" href="http://example.com/e
lsie" id="link1">Elsie</a>]
>>> first_story_paragraph = soup.find("p", "story")
>>> first_story_paragraph.find_previous_sibling("p")
<p class="title"><b>The Dormouse's story</b></p>

find_all_next()和find_next()

find_all_next( name , attrs , recursive , string , **kwargs )
find_next( name , attrs , recursive , string , **kwargs )

这2个方法通过 .next_elements 属性对当前tag的之后的 tag和字符串进行迭代, find_all_next() 方法返回所有符合条件的节点, find_next() 方法返回第一个符合条件的节点:

>>> first_link = soup.a 
>>> first_link  
<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>
>>> first_link.find_all_next(string=True) 
['Elsie', ',\n', 'Lacie', ' and\n', 'Tillie', ';\nand they lived at the bottom of a well.', '\n', '...', '\n']
>>> first_link.find_next("p") 
<p class="story">...</p>

第一个例子中,字符串 “Elsie”也被显示出来,尽管它被包含在我们开始查找的<a>标签的里面.第二个例子中,最后一个<p>标签也被显示出来,尽管它与我们开始查找位置的<a>标签不属于同一部分.例子中,搜索的重点是要匹配过滤器的条件,并且在文档中出现的顺序而不是开始查找的元素的位置.

find_all_previous()和find_previous()

find_all_previous( name , attrs , recursive , string , **kwargs )
find_previous( name , attrs , recursive , string , **kwargs )

这2个方法通过 .previous_elements 属性对当前节点前面 的tag和字符串进行迭代, find_all_previous() 方法返回所有符合条件的节点, find_previous() 方法返回第一个符合条件的节点.

>>> first_link = soup.a 
>>> first_link 
<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>
>>> first_link.find_all_previous("p")
[<p class="story">Once upon a time there were three little sisters; and their names were
<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a> and
<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>, <p class="title"><b>The Dormouse's story</b></p>]

find_all_previous("p") 返回了文档中的第一段(class=”title”的那段),但还返回了第二段,<p>标签包含了我们开始查找的<a>标签.不要惊讶,这段代码的功能是查找所有出现在指定<a>标签之前的<p>标签,因为这个<p>标签包含了开始的<a>标签,所以<p>标签一定是在<a>之前出现的.

CSS选择器

Beautiful Soup支持大部分的CSS选择器,在Tag或BeautifSoup对象的.select()方法中传入字符串参数,既可以使用CSS选择器的语法找到tag:

>>> soup.select("title") 
[<title>The Dormouse's story</title>]

通过tag标签逐层查找:

>>> soup.select("body a")
[<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>, <a class="sister" href="http://example.com/l
acie" id="link2">Lacie</a>, <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
>>> soup.select("html head title") 
[<title>The Dormouse's story</title>]

找到某个tag标签下的直接子标签:

>>> soup.select("head > title") 
[<title>The Dormouse's story</title>]
>>> soup.select("p > a") 
[<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>, <a class="sister" href="http://example.com/l
acie" id="link2">Lacie</a>, <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
>>> soup.select("p > #link1") 
[<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>]
>>> soup.select("body > a") 
[]
>>> soup.select("p > a:nth-of-type(2)") 
[<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]

找到兄弟节点:

>>> soup.select("#link1 ~ .sister") 
[<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>, <a class="sister" href="http://example.com/t
illie" id="link3">Tillie</a>]
>>> soup.select("#link1 + .sister") 
[<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]

通过CSS的类名查找:

>>> soup.select(".sister") 
[<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>, <a class="sister" href="http://example.com/l
acie" id="link2">Lacie</a>, <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

>>> soup.select("[class~=sister]")
[<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>, <a class="sister" href="http://example.com/l
acie" id="link2">Lacie</a>, <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

通过tag的id查找:

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

同时用多种CSS选择器查询元素:

>>> soup.select("#link1,#link2") 
[<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>, <a class="sister" href="http://example.com/l
acie" id="link2">Lacie</a>]

通过是否存在某个属性来查找:

>>> soup.select("a[href]")
[<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>, <a class="sister" href="http://example.com/l
acie" id="link2">Lacie</a>, <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

通过属性的值来查找:

>>> soup.select('a[href="http://example.com/elsie"]') 
[<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>]
>>> soup.select('a[href^="http://example.com/"]') 
[<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>, <a class="sister" href="http://example.com/l
acie" id="link2">Lacie</a>, <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
>>> soup.select('a[href$="tillie"]') 
[<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
>>> soup.select('a[href*=".com/el"]') 
[<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>]

通过语言设置来查找:

>>> multilingual_markup = """
...  <p lang="en">Hello</p>
...  <p lang="en-us">Howdy, y'all</p>
...  <p lang="en-gb">Pip-pip, old fruit</p>
...  <p lang="fr">Bonjour mes amis</p>
... """ 

>>> multilingual_soup = BeautifulSoup(multilingual_markup)
>>> multilingual_soup.select('p[lang|=en]')
[<p lang="en">Hello</p>, <p lang="en-us">Howdy, y'all</p>, <p lang="en-gb">Pip-pip, old fruit</p>]

返回查找到的元素的第一个

>>> soup.select_one(".sister") 
<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容