Python 从零开始爬虫(二)——BeautifulSoup解析网页
学了requests,了解了伪装技巧后,终于能爬到些比较正常的网页源码(html文档)了,但这离结果还差最后和是最重要的一步——筛选。这个过程就像在泥沙中淘金一样,没有合适的筛子,你就会把有价值的漏掉了,或者做了无用功把没用的也筛了出来。
淘金者看土质,做筛子。对应到爬虫领域就是观察html,定制筛选器。
稍稍了解HTML
信息都在网页源码里,浏览器通过解析源码来加载我们所看到的东西,那我们是不是也应该学下如何看源码呢?——是的
但不要方,这不是html语法课,做爬虫的,只需了解一下html的原理和标签关系就行了,这跟认亲戚一样简单,你会看家族树的话根本不成问题。
<?xml version="1.0" encoding="ISO-8859-1"?> <bookstore> <book> <title lang="en">Harry Potter</title> <author>J K. Rowling</author> <year>2005</year> <price>29.99</price> </book> </bookstore> 有时候会堆成一行,影响观察但不影响使用,丢到排版器排一下就好了 <bookstore><book><title lang="en">Harry Potter</title><author>J K. Rowling</author<year>2005</year><price>29.99</price></book></bookstore>
这是个非常短的示例html,但足以解释所有节点关系。
- <bookstore>,<book>这些有尖括号的叫做标签(或节点),成对存在。bookstore,book是标签名,标签间可以放字符串。
- 标签可以拥有属性,属性在尖括号里,如title标签有名为lang的属性,属性值为"en"。
- A节点被B节点包起来,A就是B的子,或B是A的父。如book和title都是是bookstore的子,但是book是bookstore的直接子(只有一层包含关系)
- 有同一个直接父的标签互相为兄弟,如title,author,year,price互为兄弟。
BeautifulSoup
惯例贴上官方文档
煲靓汤嘛!
把html文档转换为可定位的树结构,并提供索引,查找,修改(对爬虫没什么用)功能。
安装
煲汤模块
- ubuntu下:
apt-get install Python-bs4
- win下:
pip install beautifulsoup4
如果你还需要使用第三方解释器lxml或html5lib,那也安装一下
apt-get install Python-lxml(/html5lib)
pip install lxml(/html5lib)
标签索引
Beautiful Soup将复杂HTML文档转换成一个复杂的树形结构,每个节点都是一个对象,所有对象可以归纳为4种: Tag(标签) , NavigableString(字符串), BeautifulSoup(汤) , Comment(注释) , 知道就行.
BeautifulSoup类接收2个参数,第一个是html文档,第二个是解释器名,不写的话会自动选择。实例化后生成树结构,这里顺便附上各种解释器比较
from bs4 import BeautifulSoup#注意不是直接导入beautifulsoup4 r = requests(......) soup = BeautifulSoup(r.text,"lxml")
- Tag(标签)类,包含该标签的所有内容,有多种方法用于索引元素/获取字符串(下面的tag,tag1指的是标签名)soup相当于最大的Tag对象。
定位:标签定位,previous_sibling/next_sibling
tag2 = soup.tag1.tag2#定位到tag1下的tag2,并将其返回 tag4 = tag2.tag3.tag4,#标签对象可以继续向下定位 brother_tag = tag.previous_sibling/next_sibling#定位到该tag的上一个/下一个兄弟标签并将其返回 brother_tag = tag.previous_siblings/next_siblings#返回的是生成器,内含多个兄弟标签。
索引: attrs,中括号索引
dict1 = tag.attrs #以字典形式返回该tag下的元素及其对应值 lang = tag["lang"] #返回该tag下的lang属性的值,和字典索引用法一样
获取字符串: string,strings,get_text()
string = tag.string #仅限于夹在该tag的字符串,不包子的字符串,将每段字符串无缝连接后返回。 #如对`<a>A1<b>BB</b>A2<c>CC</c>A3<d>DD</d>A4</a>`使用a.string返回的是"A1A2A3A4" strings = tag.strings #以生成器形式返回该tag下(**不包括子**)的**每段字符串**,供for循环使用。 strings = tag.stripped_strings #来先去除空行/空格再返回生成器 text = tag.get_text(分隔符,strip=False) #返回该tag下(包括所有子孙)的字符串,同一个tag下的字符串无缝连接,不同tag下字符串间以指定分隔符连接,strip默认为False,改为True时自动去除空行/空格。
- 以上的方法都是单次定位的,如果有多个符合标准的定位则以出现的第一个为准!!!
- 索引和获取字符串的方法一定是最后用的,因为它不再返回Tag对象!!!
find_all&find方法
将它们单独出来是因为它真的太重要太灵活了,它们们能实现某个范围内的条件定位(不通过父子兄弟关系),返回Tag对象供使用各种索引方法。
- find()的作用是实现范围内的单次条件定位
- find_all()的作用是以可迭代的形式返回范围内多个符合要求的定位,但是你要确保这个标签对你要的信息具有唯一性。
信息有时是分块的,这时定位也应分块,再对每个分块使用单次定位,下面是一个最常用的筛选器。
for each in soup.find_all(.......):#通常第一句就是用find_all进行分块 string = each.find().string #然后在该分块中使用条件定位,再使用索引方法 id = each.find()['id'] ........
find和find_all接收的参数是一样的,现以find_all为例:Tag.find_all(name,attrs,recursive,limit,text,*kwards)
name:标签名,定位到指定标签名的节点如'a'
- 想匹配多种标签名可传列表如['div','a','b']
- 想自定标签名规则可传正则表达式如re.compile(规则)【正则后面再讲】
**kwards:其实是标签里的属性及其值,定位到有指定属性,属性值的标签,如.find_all(lang='en')
- 如果属性叫class,为防止与类的“class”冲突要在后面加下横线,如.find_all(class_='sakura')
- 可以设立多个条件如.find_all(class_='sakura',lang='en')
- 属性值可以用传正则表达式如.find_all(lang=re.compile(r''))
- recursive:默认True,检索当前tag下的所有子孙标签。设置为False时只检索当前tag下的直接子标签
- limit:设置匹配上限,接收自然数,决定了有效定位的数量上限。
- text:搜索文档中的字符串内容,返回匹配字符串的列表,可接收字符串,字符串列表和正则表达式(这个极少用到,通常不理)
到这里BeautifulSoup的主要用法都总结完了,全是文字没有实例可能会有些难啃,但是下一篇会放出几个实例出来让大家体验一下它的用法,到时就比较好理解了。
动手能力强的话,现在就可以开是捣鼓些小爬虫出来啦,加油,頑張って。
!!!!!!!!!!!!!记得常按F12看html源码啊!!!!!!!!!!!!!!