题外话

  • 爬虫相关案例不会详细到一步步放出来,比如获取多个页面的数据,你就去多看几个页面的网址,大概率就能发现他的规律,又或者先获取当前页面的跳转网址,在对获取到的网址再做一次类似的爬取,很多就是一步步轮下去的,而我呈现的是完整的一步到位的代码,所以感觉有跳跃或者有问题,可以勤用print,可以较为直观的看出是哪里有问题。

1、数据解析

上篇中的案例其实主要都是在进行通用爬虫,但对于用户来说,爬取整个页面的信息是冗余的,许多信息是无用数据甚至还需要用户自行去挑选,需要耗费大量的时间和精力,所以数据解析就显得无可或缺。

数据解析的概念:可以将爬取到的数据中指定的数据进行单独提取
作用:实现聚焦爬虫
通用原理:

  • 在一张页面中,爬取到的数据往往存储于html文件中
  • html文件中,可以通过标签去定位想要获取的数据位置,并进行提取
    数据解析技术:
  • xpath(通用性最强)
  • bs4(python独有,学习成本低)
  • 正则表达式(复杂度高)
  • pyquery(css语句)
    这次主要以xpath技术为主,xpath的编码流程:
  • 创建一个etree类型的对象,把被解析的数据加载到该对象中
  • 调用etree对象中的xpath函数结合不同形式的xpath表达式进行数据提取

2.案例一

通过新建一个text.html文件,对html进行分析来初步理解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
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>测试bs4</title>
</head>
<body>
<div>
<p>百里守约</p>
</div>
<div class="song">
<p>李清照</p>
<p>王安石</p>
<p>苏轼</p>
<p>柳宗元</p>
<a href="http://www.song.com/" title="赵匡胤" target="_self">
<span>this is span</span>
宋朝是最强大的王朝,不是军队的强大,而是经济很强大,国民都很有钱</a>
<a href="" class="du">总为浮云能蔽日,长安不见使人愁</a>
<img src="http://www.baidu.com/meinv.jpg" alt="" />
</div>
<div class="tang">
<ul>
<li><a href="http://www.baidu.com" title="qing">清明时节雨纷纷,路上行人欲断魂,借问酒家何处有,牧童遥指杏花村</a></li>
<li><a href="http://www.163.com" title="qin">秦时明月汉时关,万里长征人未还,但使龙城飞将在,不教胡马度阴山</a></li>
<li><a href="http://www.126.com" alt="qi">岐王宅里寻常见,崔九堂前几度闻,正是江南好风景,落花时节又逢君</a></li>
<li><a href="http://www.sina.com" class="du">杜甫</a></li>
<li><a href="http://www.dudu.com" class="du">杜牧</a></li>
<li><b>杜小月</b></li>
<li><i>度蜜月</i></li>
<li><a href="http://www.haha.com" id="feng">凤凰台上凤凰游,凤去台空江自流,吴宫花草埋幽径,晋代衣冠成古丘</a></li>
</ul>
</div>
</body>
</html>

代码中我们需要使用一个新的包lxml,可以通过下述在终端中进行下载

1
pip install lxml
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
from lxml import etree  

#先创建一个etree对象,把数据加载到etree中去
tree = etree.parse('test.html')

#对数据进行提取
#xpath函数返回的是列表,列表中存储的是满足定位要求的所有标签
#具体获取html下的head中的title
ret = tree.xpath('/html/head/title')
# ‘//’获取所有的title
ret1 = tree.xpath('//title')

#对<body>中的数据进行提取,你会发现有多个<div>
ret2 = tree.xpath('//div') #获取所有的div
ret3 = tree.xpath('//div[@class="song"]') #获取class为song的数据
ret4 = tree.xpath('//div[@class="song"]/p')#获取该div下的p标签数据

#运行之后你会发现他给的都是<Element p at 0x2555a90de80>这样的数据
#如果要获得文本数据,则需要使用text()
ret5 = tree.xpath('//a[@id="feng"]/text()') #提取对应a中的数据
ret6 = tree.xpath('//a//text()') #提取a下所有的文本数据
#总结:/text()提取对应下的文本,//text()提取所有的文本

#提取标签的属性值,比如图片地址 //tag/@attrNameret7 = tree.xpath('//img/@src')
print(ret7)

3. 碧血剑小说

网址:https://www.jinyongwang.net/bi/
结果:将各个章节的标题和内容进行爬取然后存储到文件中
先找到对应的标签的位置
image.png
然后右键
image.png
即可复制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
import requests  
from lxml import etree
from urllib.parse import urljoin

url ='https://www.jinyongwang.net/bi/'
header = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36',
'referer':'https://www.jinyongwang.net/bi/',
'cookie': 'PHPSESSID=t880k0dcvvjkbgdulcaa25krf6'
}
response = requests.get(url=url,headers=header)
response.encoding='utf-8'
page_bi = response.text
# 上述代码是和之前差不多的
#但与前一个案例不同,因为这是requests得到数据不是html文件所以不是parse
tree = etree.HTML(page_bi)

# f = open('bixuejian.txt','w')
with open('bixuejian.txt','w',encoding='utf-8') as f:
li_list = tree.xpath('//*[@id="pu_box"]/div[3]/ul/li')


for li in li_list:
#局部标签,只在li中寻找,因为xpath得到的是列表,
# 所以还得[0]是为了得到字符串
title = li.xpath('./a/text()')[0]
detail = li.xpath('./a/@href')[0]
detail_url = urljoin(url, detail)
res = requests.get(url=detail_url,headers=header )
res.encoding= 'utf-8'
content = res.text
detail_tree = etree.HTML(content)
text = detail_tree.xpath('//*[@id="vcon"]//p//text()')
text_join = ',\n'.join(text).strip()
# print(text_join)
# f.write(title+':'+ text_join+'\n') f.write(f"{title}: {text_join}\n")
print(title + ' 下载成功')

image.png
上述就是结果样式

4. 简历爬取

网址= https://sc.chinaz.com/jianli/
下述就是需要得到的xpath
image.png

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
import requests  
from lxml import etree

page_input = input('请输入搜索页码(1-5):')

pages=[]
if '-' in page_input:
start,end = map(int, page_input.split('-'))
pages= list(range(start, end+1))
else:
pages = [int(page_input)]

for page in (1,2):
if page == 1:
url = 'https://sc.chinaz.com/jianli/index.html'
else:
url = 'https://sc.chinaz.com/jianli/index_%d.html'%page
print('当前正在爬取第%d页的数据'%page)
header = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36',

}
response = requests.get(url=url, headers=header)
response.encoding='utf-8'
page_jianli = response.text
tree = etree.HTML(page_jianli)
div_list = tree.xpath('//*[@id="container"]/div')
for div in div_list:
title = div.xpath('./p/a/text()')[0]
detail_url = div.xpath('./a/@href')[0]
jianli_response = requests.get(url=detail_url,headers=header)
jianli_response.encoding= 'utf-8'
page_down = jianli_response.text
#创建etree对象,将数据存入对象中
down_tree = etree.HTML(page_down)
#通过标签定位获取数据
down_url = down_tree.xpath('//*[@id="down"]/div[2]/ul/li[1]/a/@href')[0]
down_response = requests.get(url=down_url,headers=header)
down_content = down_response.content
name = './jianli/'+ title+ '.rar'
with open(name,'wb') as f:
f.write(down_content)

ps(pthon):

  • 序列化:json.dumps()
  • 反序列化:json.load()