BeautifulSoup解析空格
今天爬一个网站,它的class里有空格,导致我用BeautifulSoup半天没爬出来,后来看了文档,这叫多值属性:
HTML 4定义了一系列可以包含多个值的属性.在HTML5中移除了一些,却增加更多.最常见的多值的属性是 class (一个tag可以有多个CSS的class). 还有一些属性 rel , rev , accept-charset , headers , accesskey . 在Beautiful Soup中多值属性的返回类型是list:
1 css_soup = BeautifulSoup('<p class="body strikeout"></p>') 2 css_soup.p['class'] 3 # ["body", "strikeout"] 4 5 css_soup = BeautifulSoup('<p class="body"></p>') 6 css_soup.p['class'] 7 # ["body"]
如果某个属性看起来好像有多个值,但在任何版本的HTML定义中都没有被定义为多值属性,那么Beautiful Soup会将这个属性作为字符串返回
1 id_soup = BeautifulSoup('<p id="my id"></p>') 2 id_soup.p['id'] 3 # 'my id'
将tag转换成字符串时,多值属性会合并为一个值
1 rel_soup = BeautifulSoup('<p>Back to the <a rel="index">homepage</a></p>') 2 rel_soup.a['rel'] 3 # ['index'] 4 rel_soup.a['rel'] = ['index', 'contents'] 5 print(rel_soup.p) 6 # <p>Back to the <a rel="index contents">homepage</a></p>
如果转换的文档是XML格式,那么tag中不包含多值属性
1 xml_soup = BeautifulSoup('<p class="body strikeout"></p>', 'xml') 2 xml_soup.p['class'] 3 # u'body strikeout'
这是文档对多值属性的解释
所以在使用BeautifulSoup.find or BeautifulSoup.find_all的时候要注意
举个例子吧:
假如我现在的HTML是这样的:
>>> html = '<div class="l_post j_l_post l_post_bright "></div>'
先对他用html.parser解析,然后我们看一看里面的class是什么
1 >>> Soup = BeautifulSoup(html,'html.parser') 2 >>> Soup.div['class'] 3 ['l_post', 'j_l_post', 'l_post_bright', '']
咦,我们发现如果最后又空格的话会多一个'',我们用find_all或者find可以找到我们想要的这个标签,但是class可以只要第一个,也可以要整个列表都可以
notice:我在爬一个网站的时候发现了这个的问题,你用列表的化是只要有列表中任一一个元素都能匹配上
1 >>> Soup.find('div', attrs = {'class':'1_post'}) 2 >>> Soup.find('div', attrs = {'class':'l_post'}) 3 <div class="l_post j_l_post l_post_bright "></div> 4 >>> Soup.find('div', attrs = {'class':['l_post', 'j_l_post']}) 5 <div class="l_post j_l_post l_post_bright "></div> 6 >>> Soup.find('div', attrs = {'class':['l_post', 'j_l_post', 'l_post_bright']}) 7 <div class="l_post j_l_post l_post_bright "></div> 8 >>> Soup.find('div', attrs = {'class':['l_post', 'j_l_post', 'l_post_bright', '']}) 9 <div class="l_post j_l_post l_post_bright "></div>
这里再补充一点知识吧,就是find,和find_all的用法,拿这个例子继续(对了,find_all因为常用,所有可以省略,可以直接写Soup(.....))
1 >>> Soup.find(attrs = {'class':['l_post', 'j_l_post', 'l_post_bright', '']}) 2 <div class="l_post j_l_post l_post_bright "></div> 3 >>> Soup.find_all(attrs = {'class':['l_post', 'j_l_post', 'l_post_bright', '']})[0] 4 <div class="l_post j_l_post l_post_bright "></div> 5 >>> Soup.find_all('div', attrs = {'class':['l_post', 'j_l_post', 'l_post_bright', '']})[0] 6 <div class="l_post j_l_post l_post_bright "></div>
经过对BeautifulSoup的深度阅读后,觉得下面这个点对分析多值属性的html很重要:
按照CSS类名搜索tag的功能非常实用,但标识CSS类名的关键字 class 在Python中是保留字,使用 class 做参数会导致语法错误.从Beautiful Soup的4.1.1版本开始,可以通过 class_ 参数搜索有指定CSS类名的tag:
1 soup.find_all("a", class_="sister") 2 # [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>, 3 # <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>, 4 # <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
class_ 参数同样接受不同类型的 过滤器 ,字符串,正则表达式,方法或 True :
1 soup.find_all(class_=re.compile("itl")) 2 # [<p class="title"><b>The Dormouse's story</b></p>] 3 4 def has_six_characters(css_class): 5 return css_class is not None and len(css_class) == 6 6 7 soup.find_all(class_=has_six_characters) 8 # [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>, 9 # <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>, 10 # <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
tag的 class 属性是 多值属性 .按照CSS类名搜索tag时,可以分别搜索tag中的每个CSS类名:
1 css_soup = BeautifulSoup('<p class="body strikeout"></p>') 2 css_soup.find_all("p", class_="strikeout") 3 # [<p class="body strikeout"></p>] 4 5 css_soup.find_all("p", class_="body") 6 # [<p class="body strikeout"></p>]
搜索 class 属性时也可以通过CSS值完全匹配:
1 css_soup.find_all("p", class_="body strikeout") 2 # [<p class="body strikeout"></p>]
完全匹配 class 的值时,如果CSS类名的顺序与实际不符,将搜索不到结果:
1 soup.find_all("a", attrs={"class": "sister"}) 2 # [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>, 3 # <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>, 4 # <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
以上是文档对class_的解释,但是我发现在有些网站的解析时这种方式还是行不通,所以在这种方式行不通的时候,我用了这种re
1 InfoList = Soup.find_all(class_ = re.compile('l_post j_l_post l_post_bright'))
比如说这个多值属性,我用前面的方法都不行,tag的class是“l_post j_l_post l_post_bright ”,这样才解决了我的问题