2.1 Beautiful Soup
Beautiful Soup借助网页的结构和属性等特性来解析网页
其在解析时 实际上依赖解析器,它 除了支持Python标准库中的HTML解析器外,还支持一些第三方解析器(比如lxml)——我选择使用lxml
prettify()
可以把要解析的字符串以标准的缩进格式输出
返回——bs4.element.Tag类型,具有str属性
节点选择器——根据属性**来选择
直接调用节点的名称就可以选择节点元素,再调用string属性就可以得到节点内的文本了
1:这种选择方式速度非常快:
2:如果单个节点结构层次非常清晰,可以选用这种方式来解析
eg:
soup.title.string()
输出HTML中title节点的文本内容
注:soup.title可以选出HTML中的title节点,
再调用string属性就可以得到里面的文本了
——调用string属性来获取文本的值
提取信息
1:获取节点的名称——调用name
print(soup.title.name)
2:获取属性和属性值——调用attrs(获取所有属性,一个节点 有时候不止一个属性)
print(soup.p.attrs)
返回字典{}形式,并且 把选择的节点的所有属性和属性值组合成一个字典
{'class': ['title'], 'name': 'dromouse'}
注:
有的返回结果是字符串,比如,name属性的值是唯一的,返回的结果就是单个字符串。
有的返回结果是字符串组成的列表。对于class,一个节点元素可能有多个class,所以返回的是列表。
∴在实际处理过程中,我们要注意判断类型
如果要获取name属性值,就相当于从字典中获取某个键值,只需要用中括号加属性名就可以了。比如,要获取name属性值,就可以通过attrs[‘name’]来得到
还可以 (法2)
直接在节点元素后面加中括号,传入属性名就可以获取属性值了。不写attrs
print(soup.p['name'])#
——输出
dromouse
3:获取 节点元素 包含的文本内容——调用string
print(soup.p.string)
获取的是 第一个 p节点的文本
嵌套选择:
每一个返回结果都是bs4.element.Tag类型,它可以被继续调用 来进行下一步的选择
eg:
获取了head节点元素(Tag类型),我们可以继续调用head 来选取其内部的head节点元素
关联选择:
在做选择的时候,有时候不能做到一步就选到想要的节点元素,需要先选中某一个节点元素,然后以它为基准再选择它的子节点、父节点、兄弟节点等——嵌套选择同理
(1)子节点、子孙节点
选取节点元素之后,如果想要获取它的直接子节点,可以调用contents属性
contents属性 :得到的结果是直接子节点的列表
调用children属性也可以得到相应的结果,返回的是生成器类型,接着用for循环输出相应的内容
如果要得到所有的子孙节点的话,可以调用 descendants属性
(2)父节点、祖先节点
如果要获取某个节点元素的父节点,可以调用 parent属性 ——直接父节点,没有再向外寻找父节点的祖先节点
如果想获取所有的祖先节点,可以调用 parents属性——返回结果:生成器类型
(3)兄弟节点(同级节点)
next_sibling 和 previous_sibling 分别获取节点的下一个和上一个兄弟元素
next_siblings 和 previous_siblings 则分别返回所有前面和后面的兄弟节点 的生成器
关联元素节点的信息 提取:
1:如果返回结果是单个节点,那么可以直接调用string、attrs等属性获得其文本和属性:
2:如果返回结果是多个节点的生成器,则可以转为列表后取出某个元素,然后再调用string、attrs等属性获取其对应节点的文本和属性(需要处理一下)
eg:
print(list(soup.a.parents)[0].attrs['class'])
方法选择器——调用方法选择
find_all :
返回 所有 匹配成功元素 所组成的列表
1:name:根据节点名 查询元素
print(soup.find_all(name='节点名'))
嵌套查询:
for ul in soup.find_all(name='节点名1'):
print(ul.find_all(name='节点名1内的节点名'))
2:attrs:根据 属性
print(soup.find_all(attrs={'属性': '属性值'}))
得到列表形式,包含的内容就是符合“属性”为“属性值”的所有节点
3:text:(根据)传入的形式可以是字符串,可以是正则表达式对象(pattern)
可用来匹配 节点的文本
print(soup.find_all(text= str / pattern ))
传入pattern:结果返回 所有匹配正则表达式 的节点文本 组成的列表
find()
ps: 返回 第一个匹配的元素 (单个)
用法 和 find_all 相同
find_parents()
前者返回所有祖先节点,
find_parent()
后者返回直接父节点。
find_next_siblings()
前者返回后面所有的兄弟节点,
find_next_sibling()
后者返回后面第一个兄弟节点。
find_previous_siblings()
前者返回前面所有的兄弟节点,
find_previous_sibling()
后者返回前面第一个兄弟节点。
find_all_next()
前者返回节点后所有符合条件的节点,
find_next()
后者返回第一个符合条件的节点。
find_all_previous()
前者返回节点后所有符合条件的节点,
find_previous()
后者返回第一个符合条件的节点。
CSS选择器
只需要调用select()方法,传入相应的CSS选择器
输出——列表形式
1:嵌套选择:
select()方法——先选择所有父节点,再遍历每个父节点,选择其子节点
for ul in soup.select('ul'):
print(ul.select('li'))
2:获取属性
节点类型是Tag类型,所以获取属性还可以用原来的方法(attrs)
3:获取文本
同理,可以调用string属性
另外,get_text()
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
for li in soup.select('li'):
print('Get Text:', li.get_text())
print('String:', li.string)
输出效果是一样的
实战:使用beautifulsoup 提取 丁香园论坛的 回复 内容
import requests
from bs4 import BeautifulSoup as bs
url = 'http://www.dxy.cn/bbs/thread/626626#626626'
headers = {
'User-Agent' : 'Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.1.6) '
}
def get_page(url):
url = 'http://www.dxy.cn/bbs/thread/626626#626626'
response = requests.get(url) #获得页面
if response.status_code == 200: #状态码
return response.text #页面的源代码
return None
def analysis(html):
# html=response.text
soup = bs(html, 'lxml') #初始化BS对象,赋值给Soup
info=soup.find_all(attrs={'class':'postbody'}) #得到回复内容
for i in info: #去掉杂乱信息
print(i.text)
def main(): #定义main,目的:执行def的两个 函数
html = get_page(url)
analysis(html)
if __name__ == '__main__': #执行此文件时,执行以下方法
main() #执行main
#一个def内的 参数 在下一个定义 是用不了的,比如#html=response.text,不能把#去掉,去掉会是无定义的,因为 =右边 是上一个 def函数 的内容,不能作用于 此def中
#定义是抽象化的,尽量不具体化
2.2 XPath
返回:列表类型
常用规则:
nodename:选取此节点的所有子节点
/:从当前节点选取直接子节点
//:从当前节点选取子孙节点
注:一般用xpath规则 用//开头,来选取所有符合 要求 的节点
. :选取当前节点
… :选取当前节点的父节点
@:选取属性
属性匹配://title[@lang='eng']
(选择title节点中 class为lang的,属性值为eng的)——已有属性值
属性值获取:xpath('//li/a/@href')
属性多值匹配(某个属性有多个值):通过contains()方法,第一个参数传入属性名称,第二个参数传入属性值,只要此属性包含所传入的属性值,就可以完成匹配
eg:
text = '''
first item
'''#class里面有两个属性li、li-first
xpath('//li[contains(@class, "li")]/a/text()')
多属性匹配(多个属性确定的一个节点):and
eg:
first item
匹配代码:
xpath('//li[contains(@class, "li") and @name="item"]/a/text()')
*:代表匹配所有节点
text():获取节点中的文本
按序选择:在选择的时候某些属性可能同时匹配了多个节点,但是只想要其中的某个节点——中括号传入索引的方法 ,获取特定次序的节点
节点轴选择:
用途:获取子元素、兄弟元素、父元素、祖先元素等
ancestor轴,可以获取所有祖先节点
attribute轴,可以获取所有属性值
child轴,可以获取所有直接子节点
descendant轴,可以获取所有子孙节点
following轴,可以获取当前节点之后的所有节点
following-sibling轴,可以获取当前节点之后的所有同级节点
eg:
xpath('//li[1]/ancestor::*') #:: 后可加限定条件
more:http://www.w3school.com.cn/xpath/xpath_axes.asp
XPath_MoreUsage:http://www.w3school.com.cn/xpath/index.asp
实战:
import requests
from lxml import etree
url = 'http://www.dxy.cn/bbs/thread/626626#626626'
headers = {
'User-Agent' : 'Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.1.6) '
}
def get_code(url):
page = requests.get(url) #页面获得
if page.status_code == 200:
return page.text #源代码获得
else:
return None
def analysis(html):
#下一步,运用Xpath来提取 回复内容
# //td [@class='postbody']
html = etree.HTML(get_code(url))
#目前的问题:在哪里使用XPath?怎么使用?——对html使用xpath
result = html.xpath('//td[@class="postbody"]/text()') #获得td节点下的文本
for i in result: #去杂
print(i)
def main():
html = get_code(url)
analysis(html)
if __name__ == '__main__': #执行此文件时,执行以下方法
main()