python爬虫模块(RE、BS4、Xpath)

一、RE模块

基本使用:

findall:匹配字符串中所有符合正则的内容

finditer:匹配内容后返回迭代器,从迭代器中取数据可以用循环加.group()

search:找到一个结果就返回,返回的结果是match对象,取数据可以用.group()

match:从头开始匹配,从第一个字母匹配不到就没有(不常用)

其中,上述内容的调用通过
ret=re.findall(r'正则串','匹配目标串')实现调用。

预加载:

预加载正则表达式
(用于正则表达式较长、且多次使用时的封装)

格式为:

1
2
3
4
obj = re.compile(r"\d+")
ret = obj.finditer("目标字串666")
for it in ret:
print(it.group())

内容提取:

​ 正则可以用来匹配固定格式的一串字符串,如<html>xxx<html>,然而当我们不想要两侧的,只想要xxx时,需要从正则中取出不重复信息,即内容提取。

举例说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
str="""
<div class='aa'><span id='1'>qwe</span><div>
<div class='bb'><span id='2'>qwer</span><div>
<div class='cc'><span id='3'>qwert</span><div>
<div class='dd'><span id='4'>qwerty</span><div>
<div class='ee'><span id='5'>qwertyu<span><div>
"""

#原匹配方式
#obj = re.compile(r"<div class='.*?'><span id='\d+'>.*?</span><div>",re.S)
# re.S :让.可以匹配换行符

obj = re.compile(r"<div class='(?p<name1>.*?)'><span id='(?P<name2>\d+)'>(?P<name3>.*?)</span><div>",re.S)
result = obj.finditer(s)

for it in result:
print(it.group('name1'))
print(it.group('name2'))
print(it.group('name3'))

即,使用(?P<分组名字>原来正则)从正则匹配的内容中进一步提取内容。

二、BS4模块

引入方式:

1
from bs4 import BeautifulSoup

使用方式(以抓取表格为例):

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
# 1. 拿到页面源代码
# 2. 使用bs4进行解析. 拿到数据
import requests
from bs4 import BeautifulSoup
import csv

url = "http://www.xxx.com/a.html"
resp = requests.get(url)

# 解析数据
# 1. 把页面源代码交给BeautifulSoup进行处理, 生成bs对象
page = BeautifulSoup(resp.text, "html.parser") # 指定html解析器
# 2. 从bs对象中查找数据
# find(标签, 属性=值)
# find_all(标签, 属性=值)
table = page.find("table", attrs={"class": "hq_table"})
trs = table.find_all("tr")[1:]
for tr in trs: # 每一行
target_1 = tr.find_all("td") # 拿到每行中的所有td
target_2 = tds[0].text # .text 表示拿到被标签标记的内容
target_3 = tds[1].text # .text 表示拿到被标签标记的内容
target_4 = tds[2].text # .text 表示拿到被标签标记的内容
target_5 = tds[3].text # .text 表示拿到被标签标记的内容
target_6 = tds[4].text # .text 表示拿到被标签标记的内容
target_7 = tds[5].text # .text 表示拿到被标签标记的内容
target_8 = tds[6].text # .text 表示拿到被标签标记的内容
print(name, low, avg, high, gui, kind, date)

print("over!")

核心方法:

find(标签, 属性=值)

find_all(标签, 属性=值)

​ 按照次序填写要找的标签名和属性名即可。其中,标签名的传参为字符串,属性名由于class、id等是python的关键字,可以使用attrs={键值对}来代替,键值对的键和值均为字符串

三、Xpath模块

Xpath是XML文档中搜索的一门语言,html是XML的子集

模块引入:

1
from lxml import etree

具体使用:

​ 若xml为一个字符串形式的,有标签节点组成的xml字符串,从中提取信息可以遵循这样的语法:

​ 注:Xpath的定位在开发者工具中右键复制选项,可实现智能定位

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
42
43
44
45
46
47
48
49
50
51
52
from lxml import etree

xml = """
<book>
<id>1</id>
<name>野花遍地香</name>
<price>1.23</price>
<nick>臭豆腐</nick>
<author>
<nick id="10086">周大强</nick>
<nick id="10010">周芷若</nick>
<nick class="joy">周杰伦</nick>
<nick class="jolin">蔡依林</nick>
<div>
<nick>TTTarget1</nick>
</div>
<span>
<nick>TTTarget2</nick>
<div>
<nick>TTTarget3</nick>
</div>
</span>
</author>

<partner>
<nick id="ppc1">AAAAim1</nick>
<nick id="pbc2">AAAAim2</nick>
<nick id="pbc3">AAAAim3</nick>
<nick id="pbc4">AAAAim4</nick>
</partner>
</book>
"""

tree = etree.XML(xml)
#etree.XML用于匹配xml
#etree.HTML用于匹配html
#etree.parse用于匹配本地文件

result1 = tree.xpath("/book") # /表示层级关系. 第一个/是根节点

result2 = tree.xpath("/book/name/text()") # text() 拿文本

result3 = tree.xpath("/book/author//nick/text()") # //所有后代

result4 = tree.xpath("/book/author/*/nick/text()") # * 任意的节点. 通配符

result5 = tree.xpath("/book/partner/nick[1]/text()") # 表示在该目录下的第一个元素,匹配到AAAAim1。注意,此语法中无[0]!

result6 = tree.xpath("/book/partner/nick[@id="pbc2"]/text()") # 使用[]用于按照标签筛选,结果为AAAAim2

result7 = tree.xpath("/book/partner/nick/@id") # 直接提取nick标签的id属性
print(result)

其中//代表任意的子集(可以是一层或多层),只要存在就能找到;
/*/代表且只能代表任意一层。

举例说:
代码块中,result4可以匹配到TTTarget1TTTarget2TTTarget
result5只能匹配到TTTarget1TTTarget2,不能匹配到TTTarget3

当使用../xxx[n]/../text()语法时,一定要记住这里的n是从1开始计数,如代码块中匹配到的就是AAAAim1,并非AAAAim2

注意,当筛选两次,即使用如下语法

1
2
3
4
ol_li_list = tree.xpath("/html/body/ol/li")
for li in ol_li_list: # 从每一个li中提取到文字信息
result = li.xpath("./a/text()") #在li中继续去寻找. 相对查找
print(result2)

在相对查找时,需要使用./而非/以表示相对路径!