2. 解析库的使用

2. 解析库的使用

2.1 XPath的使用

先利用htmls = etree.HTML(root_htmls)或打开一个本地文本文件htmls = etree.parse(‘test.html’, etree.HTMLParser())构建一个XPath对象(htmls), 接着利用其xpath方法获取目标节点、节点属性、节点文本等信息:
a. 获取所有节点:htmls.xpath(‘//*’) ;

b. 获取子节点:
b.1 htmls.xpath(‘//li/a’), ps:获取所有li节点的所有直接 子 节点a ;
b.2 htmls.xpath(‘//li//a’), ps:获取所有li节点的所有直接 子孙 节点a ;

c. 获取父节点: htmls.xpath(‘//a[@href=”link4.html”]/../@class’),ps:获取href属性
为link4.html的所有a节 点,再获取其父节点,最后获取这些父节点的class属性值。其等价于:htmls.xpath(‘//a[@href=”link4.html”/parent::*/@class]’);

d. 属性(多值)匹配(通过节点的属性来匹配):
单一属性匹配: htmls.xpath(‘//li[@class=”pname”]’),ps:获取class属性为pname的所有li;
多属性匹配: htmls.xpath(‘//li[contains(@class,”cat”)]’),ps:获取class属性中有cat的所有li;

e.多属性匹配(当节点有多个属性时):
htmls.xpath(‘//li[contains(@class,”cat”) and @name=”mini”]’),ps:获取class属性中有cat,同时name属性的值是mini的所有li;

f. 属性获取:
htmls.xpath(‘//li/a/@href’), ps: 获取所有li节点下所有直接子节点a的href属性的值,结果是列表;

g. 文本获取:
htmls.xpath(‘//li[@class=”cat”]/a/text()’), ps:先选到a节点,再获取文本;
htmls.xpath(‘//li[contains(@class=”cat” and @name=”mini”)/a/text()]’)

h.按序选择:
htmls.xpath(‘//li[1]/a/text()’),ps: 查看第1个(没有0,直接从1开始)li节点的所有直接子节点a的text;
htmls.xpath(‘//li[last()]/a/text()’),ps: 查看最后一个li节点…;
htmls.xpath(‘//li[position()<3]/a/text()’),ps: 查看位置小于3(1和2)的li节点的…;
htmls.xpath(‘//li[last()-2]/a/text()’), ps: 查看倒数第3个li节点的…;

XPath: 斗鱼LOL直播排名爬取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
""" 
爬取斗鱼LOL主播实时排名
"""
import requests
from lxml import etree

class DouYuSpider():

""" 爬虫类 """

url = 'https://www.douyu.com/directory/game/LOL'

def get_contents(self):
""" 获取网页源码并做简单处理(结果存放到dic里) """

# 利用requests中的get()方法快速得到页面源码
reponse = requests.get(DouYuSpider.url)
root_htmls = reponse.text # str类型
#print(root_htmls)

#利用HTML类将其初始化,构建一XPath解析对象
htmls = etree.HTML(root_htmls)# <Element html at 0x2bd9698>
# htmls = etree.tostring(htmls, encoding = 'utf-8') #结果是bytes类型
# htmls = htmls.decode('utf-8') #转化为str类型
names = htmls.xpath('//span[@class="dy-name ellipsis fl"]/text()')
#print(names)
numbers = htmls.xpath('//span[@class="dy-num fr" ]/text()')
#print(numbers)
name_number = []
for index in range(0,len(names)):
name_number.append({'name':names[index], 'number':numbers[index]})

print(name_number)


def go(self):
""" 入口函数 """
self.get_contents()

douyu = DouYuSpider()
douyu.go()

2.2 Beautiful Soup的使用

  1. Beautiful Soup在解析时依赖解析器,下边列出4种常见的:
    a. Python 标准库 —- BeautifulSoup(markup, “html.parser”)
    b. lxml HTML 解析器 —- BeautifulSoup(markup, “lxml”)–(推荐使用)
    c. lxml XML 解析器 —- BeautifulSoup(markup, “xml”)
    d. html5lib —- BeautifulSoup(markup, “html5lib”)
    BeautifulSoup可自动调整输入输出编码格式,基本不需要考虑编码格式,除非文档没有指定编码格式,就需要手动说明一下原始编码方式。

  2. 基本用法

a. 先初始化一个BeautifulSoup对象,soup = BeautifulSoup(htmls, ‘lxml’);
b. 调用soup.prettify()完善不标准的htmls格式;
c. 调用soup.title.string,获得htmls中title节点的文本内容;

  1. 节点选择器
    通过属性选取,但不适合复杂的选取。

a. 获取元素和内容
获取title标签:soup.title (为bs4.element.Tag类型)
获取title标签的内容:soup.title.string
获取第一个p节点:soup.p

b. 提取信息
获取节点名称:soup.title.name
获取节点属性:soup.p.attrs, ps:获得p节点的所有属性和值,dic;
soup.p.attrs[‘name’], 获取p节点name属性的值;单个就返回str,多个就list;

c. 嵌套选择和关联选择
选择内部节点元素并获取内容:soup.head.title.string
子节点和孙节点选取:类似这种,


先获取p节点的内容(不含p标签):soup.p.contents
1
2
3
4
5
6
7
8
# 得到所有直接子节点
for i, child in enumerate(soup.p.children):
print(i, child)

#得到所有子孙节点,都会被遍历出来
for i, child in enumerate(soup.p.descendants):
print(i, child)
#descendants会递归查询所有子节点,得到所有的子孙节点

获取父节点和祖先节点:
父节点:soup.a.parent, 获取a节点的直接父节点;
祖先节点:soup.a.parents , 得到是一个生成器类型;
快速遍历输出一个生成器:enumerate(soup.a.parents)

获取同级(兄弟)节点:
获取节点的下一个兄弟元素:soup.a.next_sibling
获取节点的上一个兄弟元素:soup.a.previous_sibling
获取节点后边的兄弟节点:soup.a.next_sibling, 返回结果为generator

提取关联元素节点的文本、属性等信息:
单个节点可直接提取:

1
2
soup.a.next_sibling.string
soup.a.next_sibling.attrs['class']

多个节点的生成器,先转为list再提取:

1
list(soup.a.parents)[0].attrs['class']

  1. 方法选择器
    适用比较复杂的选取时用;
    1.find_all()
    find_all(name, attrs, recursive, text, **kwargs)

a. name(根据节点名查询元素)

1
2
3
4
5
6
#比如:<ul><li></li></ul>,<ul><li></li></ul>,<ul><li></li></ul>
uls = soup.find_all(name= 'ul') #得到是一个所有ul的list
for ul in uls:
#print(ul.find_all(name= 'li')) #得到的是ul中所有li的list
for li in ul.find_all(name= 'li')
print(li.string)

b. attrs(根据属性查询元素)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
soup.find_all(attrs={'id': 'cat'}) # 参数类型为dict类型
soup.find_all(attrs={'name': 'mini'}) # 返回结果是list

#常用参数可以不通过attrs来传递
soup.find_all(id='cat')
soup.find_all(class_='hyk') # class为python关键字,加_以示区别

```
c. text
通过传入字符串或正则表达式对象来匹配节点的文本。
``` bash
htmls = '<a>hi link</a> <b>hello link</b>'
text = re.compile('link')
soup.find_all(text= text)
# 返回结果为:['hi link','hello link']
#返回所有匹配正则表达式的节点文本组成的列表

2.find()
与find_all()用法基本一样,只不过find_all返回的是所有匹配的元素组成的list;
find()返回的是第一个匹配的元素,返回的是单个元素。

5.CSS选择器
只需要调用select()方法,传入相应的CSS选择器即可。
CSS选择器:. 表示class, # 表示 id

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
soup.select('ul li') # 选择所有ul节点下的所有li
soup.('#cat .mini') #选择所有id=cat,class=mini的节点元素

# 嵌套选择(Tag类型都可以)
for ul in soup.select('ul'):
print(ul.select('li'))

#获取属性
for ul in soup.select('ul'):
print(ul['id']) #直接传入[]和属性名
print(ul.attrs['id']) #通过attrs

#获取文本
for li in soup.select('li'):
print(li.string) #利用string属性获取文本
print(li.get_text())#利用get_text()方法

2.3 pyquery的使用

熟悉CSS选择器,了解jQuery,更适合用pyquery解析库

1.初始化
字符串、URL、文件初始化共三种。

1
2
3
4
5
6
7
from pyquery import PyQuery as pq

html = '某些html字符串'

#初始化PyQuery对象
doc = pq(html) # 也可传入2.url、3.文件:filename='demo.html'
print(doc('li')) # 选取所有li节点

  1. 基本CSS选择器
1
2
3
4
5
6
7
8
9
from pyquery import PyQuery as pq

html = ''' some htmls '''
doc = pq(html)

#先取id为first的节点,
#再选其内部class为list的节点内部所有的li节点
doc('#first .list li')
#结果为PyQuery类型
  1. 查找节点
    会利用到一些查询函数,这些查询函数的用法和jQuery中函数的用法完全一样。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    #1.查找子节点
    from pyquery import PyQuery as pq

    doc = pq(htmls)
    items = doc('.list') #查找class=list的所有节点
    lis = items.find('li') #将内部的li节点(所有子孙节点)都选出来

    #只查找子节点
    lis = items.children()

    #只查找子节点中class=cat的节点
    lis = items.children('.cat')

    #2. 查找父节点
    from pyquery import PyQuery as pq
    doc = pq(htmls)
    items = doc('.list') #查找class=list的所有节点
    container = items.parent() #得到直接父节点

    #要得到所有的祖先节点
    parents = items.parents()# 祖先节点们或逐个返回

    #得到特定的祖先节点
    parent = items.parents('.bug') #得到class=bug的祖先节点

    #3.获取兄弟(同级)节点
    from pyquery import PyQuery as pq
    doc = pq(htmls)

    #先选择class=list的节点,再在这个节点内部选择
    #class= item-0 active 的节点
    li = doc('.list .item-0.active')
    li.siblings() #获取所有的兄弟节点

    #获取指定的兄弟节点
    li.siblings('.active') #获取class=active的兄弟节点
  2. 遍历
    pyquery的选择结果无论是单节点还是多节点,类型都是PyQuery类型的(Beautiful Soup的返回结果一般是列表),单节点可以直接打印输出,或转为str(), 但对于多节点,则需要借助items()方法遍历。

1
2
3
4
5
6
7
8
9
10
from pyquery import PyQuery as pq
doc = pq(htmls)

#调用items()方法后,得到一个生成器
lis = doc('li').items() #类型:generator
for li in lis:
print(li, type(li))

# li依然是PyQuery类型,
# 可以接着进行其他操作
  1. 获取信息
    获取属性,或者,或者文本。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

#1. 获取属性
from pyquery import PyQuery as pq
doc = pq(htmls)

#选取class=cat dog的节点内的所有a节点
a = doc('.cat.dog a')

#就算返回结果a中包含多个节点时,
#通过attr也只会取得第一个节点的值
a.attr('href') #获取属性href的值(第一个节点的)
a.attr.href #同样获取属性href的值(第一个节点的)

#要获取所有a节点的属性值,需要items()进行遍历
a = doc('.cat.dog a')
for item in a.items():
print(item.attr('href'))

#进行属性获取时,若返回结果是多个,
#则需要遍历才能依次获取每个节点的属性。

#2. 获取文本
# text()、html()

from pyquery import PyQuery as pq
doc = pq(htmls)
li = doc('li')
li.html() #<a href="link2.html">se item</a>
li.text() #se item th item fo tiem

#html()返回的是第一个li节点内部的HTML文本,
#text()返回的是所有li节点内部纯文本,
#中间以空格分隔开,是一个字符串
  1. 节点操作
    对节点进行动态修改,比如给某个节点添加一个class,或者移除某个节点等操作;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#1. addClass和removeClass

from pyquery import PyQuery as pq

doc = pq(htmls)
li = doc('.item-1.active')
#li = <li class="item-1 active">test1</li>

li.removeClass('active')
#li = <li class="item-1">test1</li>

li.addClass('active')
#li = <li class="item-1 active">test1</li>

2. attr()、text()和html()

li.attr('name', 'link')
#li=<li class="item-1 active" name="link">test1</li>

li.text('TEST1')
#<li class="item-1 active" name="link">TEST1</li>

li.html('<span>TEST1</span>')
#<li class="item-1 active" name="link"><span>TEST1</span></li>

3.remove()
可移除整个节点等
htmls = '''<li>hi dog
<p>cat mini</p>
</li> '''
doc = pq(htmls)
li = doc('li')
li.find('p').remove()
li.text() # hi dog
  1. 伪类选择器
    主要用于选取第一个节点、最后一个节点、奇偶数节点、包含某一文本的节点。
1
2
3
4
5
6
7
8
9
from pyquery import PyQuery as pq
doc = pq('htmls')

li = doc('li:first-child') #选取第一个li节点
li = doc('li:last-child') #选取最后一个li节点
li = doc('li:nth-child(2)') #选取第二个li节点
li = doc('li:gt(2)') #选取第三个li之后的节点
li = doc('li:nth-child(2n)') #选取偶数位置的li节点
li = doc('li:contains(second)') #选取包含second文本的节点

CSS选择器:http://www.w3school.com.cn/css/css_syntax_id_selector.asp

分享到