爬虫之数据解析相关
涂寐 Lv5

声明

本教程仅供学习参考,请勿用在非法途径上,违者后果自负,与笔者无关。 –涂寐

聚焦爬虫

  • 用于爬取页面中指定的页面内容

编码流程

  1. 指定url
  2. 发起请求
  3. 数据解析
  4. 持久化存储

方法分类

  1. 正则表达式
  2. bs4解析
  3. xpath解析

简述使用

  1. 需求内容在标签间或作为标签的属性存储
  2. 标签定位
  3. 从标签间或标签属性值中提取所需

正则表达式

网页源代码

1
2
3
4
5
<div class="thumb">
<a href="/article/124982889" target="_blank">
<img src="//pic.qiushibaike.com/system/pictures/12498/124982889/medium/B39EVD457VB64VZH.jpg" alt="糗事#124982889" class="illustration" width="100%" height="auto">
</a>
</div>

取src正则

1
ex = '<div class="thumb">.*?<img src="(.*?)" alt.*?</div>'

糗事百科糗图

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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#!/usr//bin/env python3
# -*-coding:utf-8-*-

# 解析第一行
# 防止操作系统用户没有将python装在默认的/usr/bin路径里
# 首先会到env设置里查找python的安装路径,再调用对应路径下的解释器程序完成操作
# 白话理解,调用该程序时自动查找合适的python解析器

# 爬取糗事百科图片
import os.path
import re

import requests

if __name__ == "__main__":
# 创个文件夹存储图片
# 旧
# if not os.path.exists('./qiutuLibs'):
# 新
if os.path.exists('./qiutuLibs') is False:
os.mkdir('./qiutuLibs')
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36'
}
# 爬取单个图片数据
# # 复制图片地址,看URL
# url = 'https://pic.qiushibaike.com/system/pictures/12497/124970827/medium/5L6E0C2Y21U8FG4N.jpg'
# # content() 方法是返回图片的二进制形式数据
# # text(字符串) content(二进制) json()(对象)
# img_data = requests.get(url=url, headers=headers).content
#
# with open('./img.jpg', 'wb') as fp:
# fp.write(img_data)
# fp.close()
# print('爬取结束,瞅瞅成功不~')

# 使用通用爬虫对url对应的页面进行爬取
# 单页
# url = 'https://www.qiushibaike.com/imgrank/'
# page_text = requests.get(url=url, headers=headers).text
# 使用聚焦爬虫解析/提取所有图片
# ex = '<div class="thumb">.*?<img src="(.*?)" alt.*?</div>'
# img_src_list = re.findall(ex, page_text, re.S)
# print(img_src_list)
# for src in img_src_list:
# # 拼全地址
# src = 'https:' + src
# # 请求二进制数据
# img_data = requests.get(url=src, headers=headers).content
# # 生成图片名称
# img_name = src.split('/')[-1]
# # 图片存储路劲
# imgPath = './qiutuLibs/' + img_name
# with open(imgPath, 'wb') as fp:
# fp.write(img_data)
# print(img_name, "下载成功")
# fp.close()

# 多页
url = 'https://www.qiushibaike.com/imgrank/page/%d/'
# 可迭代对象:https://www.runoob.com/python3/python3-func-range.html
for pageNum in range(1, 2):
# 拼接页码
# 字符串格式化函数:https://www.runoob.com/python/att-string-format.html
# 老方法
# new_url = format(url % pageNum)
# 新方法
new_url = '{}{}'.format(url, pageNum)
print(new_url)
page_text = requests.get(url=new_url, headers=headers).text
# https://zhuanlan.zhihu.com/p/139596371
# 使用正则表达式解析/提取所有图片
# .* 表示匹配除 \n 外任意字符出现零次或多次
# ? 跟在 * 或 + 后边时,表示懒惰模式,即非贪婪模式,用以匹配尽可能少的字符
# a.*?b 匹配最短的,以a开始,以b结束的字符串
# 在正则里面 "()" 代表分组,即一个括号代表一个分组
# 正则特点,有括号时只能匹配到括号中的内容,没有括号就正常匹配
# (.*?) 表示仅匹配到括号中的内容
ex = '<div class="thumb">.*?<img src="(.*?)" alt.*?</div>'
# def findall(pattern, string, flags=0)
# 返回string中所有与pattern匹配的全部字符串,返回形式为数组
# re.S参数:正则表达式会将字符串page_text中内容作为一个整体进行匹配,不会对\n进行中断
img_src_list = re.findall(ex, page_text, re.S)
for src in img_src_list:
# 拼全地址
src = 'https:' + src
# 请求二进制数据,content中存储字节码
img_data = requests.get(url=src, headers=headers).content
# 生成图片名称
# str.split(str="", num=string.count(str))
# split() 通过第一参数 str 指定分隔符对字符串进行切片,如果第二个参数 num 有指定值,则分割为 num+1 个子字符串。
# src.split('/')[-1] 对 src 字符串以‘/ ’分割,取出最后一段赋予img_name
img_name = src.split('/')[-1]
# 图片存储路径
imgPath = './qiutuLibs/' + img_name
with open(imgPath, 'wb') as fp:
fp.write(img_data)
print(img_name, "下载成功")
fp.close()

BS4解析

简述原理

  1. 实例化一个BeautifulSoup对象,将页面源码数据加载到该对象中
  2. 调用BeautifulSoup对象中的属性和方法进行标签定位和数据提取

环境安装

1
2
3
4
# Beautiful Soup将复杂HTML文档转换成一个复杂的树形结构,每个节点都是Python对象,所有对象可以归纳为4种:tag,NavigableString,BeautifulSoup,Comment。
php install bs4
# lxml是python的一个解析库,支持HTML和XML的解析,支持XPath解析方式
pip install lxml

使用概要

  1. from bs4 import BeautifulSoup

  2. Beautiful Soup对象实例化

  3. 将本地html文档数据加载到BeautifulSoup对象

    1
    2
    fp = open('./localWeb.html', 'r', encoding='utf-8')
    soup = BeautifulSoup(fp, 'lxml')
  4. 或,将门户网站拉取的页面源码加载到BeautifulSoup对象

1
2
page_text = response.text
soup = BeautifulSoup(page_text, 'lxml')

相关属性和方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 本次测试HTML为笔者博客首页网页源码 

soup.tagName: 返回文档中第一次出现的 tagName 标签
soup.tagName['PropertyName']:提取 tagName 标签中属性名为 PropertyName 的值
soup.find('tagName'):返回文档中第一次出现的 tagName 标签
soup.find('a', class_="active"):根据属性再次定位
soup.find_all('tageName'):返回所有 tageName 标签的列表
soup.select('.selectorName'):根据 id/class 等选择器返回对应列表
soup.select('.header-drawer > ul > li >a'):层级选择器,一个 > 表示一个层级
soup.select('.header-drawer > ul a'):一个 空格 表示多个层级
soup.select('.header-drawer > ul a')[2]:如数组,通过下标选择列表中的某个,此处选择位序为 3 的 a 标签
soup.select('.header-drawer > ul a')[1].text:text 属性拿到标签间所有文本内容
soup.select('.header-drawer > ul a')[3].string:string属性拿到标签间直系文本内容,请通过 find() 方法测试
soup.select('.header-drawer > ul a')[4].get_text():get_text() 方法拿到标签间所有文本内容

本地测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/usr/bin/env python3
# -*-coding:utf-8-*-
from bs4 import BeautifulSoup

if __name__ == "__main__":
# 加载本地的html文档数据到 BeautifulSoup 对象中
# 以只读方式、utf-8 编码格式打开 localWeb.html
fp = open('./localWeb.html', 'r', encoding='utf-8')
# 调用 BeauSoup 类的构造方法初始化本对象,以 lxml 的解析方式传入 fp 的数据
soup = BeautifulSoup(fp, 'lxml')
# print(soup)
# 获取第一次出现的 a 标签
# print(soup.a)
# print(soup.div)
# print(soup.find('a', class_="active"))
# print(soup.find_all('a'))
# print(soup.select('.theme-version'))
# 层级选择器,获取标签机间文本内容
# print(soup.select('.header-drawer > ul a')[1].text)
# print(soup.select('.header-drawer > ul a')[1].string)
# print(soup.select('.header-drawer > ul a')[4].get_text())
# 获取标签属性内容
print(soup.a['href'])

三国演义小说

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
#!/usr/bin/env python3
# -*-coding:utf-8-*-
# 要求爬取三国演义所有章节标题和内容
# https://www.shicimingju.com/book/sanguoyanyi.html
import requests
from bs4 import BeautifulSoup

if __name__ == "__main__":
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36'
}
url = 'https://www.shicimingju.com/book/sanguoyanyi.html'
# 调用 encode('ISO-8859-1') 方法,解决乱码问题
# 对返回包进行ISO-8859-1编码,保证了中文的正确读写
page_text = requests.get(url=url, headers=headers).text.encode('ISO-8859-1')
print(page_text)
# 检测编码方式
# print(page_text.encoding)
# 在解析章节标题和详情页URL
# 1、实例化 BeautSoup对象
soup = BeautifulSoup(page_text, 'lxml')
# 匹配
li_list = soup.select('.book-mulu > ul > li')
fp = open('./sanguo.txt', 'w', encoding='utf-8')
for li in li_list:
# 拿标题
title = li.a.string
# 拿URL
detail_url = 'https://www.shicimingju.com' + li.a['href']
# 请求详情页内容
detail_page_text = requests.get(url=detail_url, headers=headers, ).text.encode('ISO-8859-1').decode('utf-8')
# print(detail_page_text)
# 解析详情页内容
detail_soup = BeautifulSoup(detail_page_text, 'lxml')
# 通过类选择其专门定位该div标签
div_tag = detail_soup.find('div', class_='chapter_content')
# 得到章节具体内容
# 看了下,额,红楼梦?这网站运维,阔以
# 通过text属性或get_text(),获取div标签中的所有文本内容
content = div_tag.get_text()
fp.write(title + ':' + content + '\n')
print(title, '爬取成功!!!')

xpath解析

原理

  1. 实例化etree对象,将待解析页面源码加载到其中
  2. 调用etree对象得xpath方法,结合xpath表达式实现标签定位和内容捕获

环境

1
pip install lxml

使用举例

  1. 本地:etree.parse(filePath)
  2. 网络:etree.HTML(‘page_text’)

xpath表达式

1
2
3
4
5
6
/:表示一个层级,从html标签(根标签)开始定位
//:表示多个层级,可从任意位置开始定位
tagName[@class="PropertyName"]:属性定位,精准定位
tagName[@class="PropertyName"]/tageName[1]:索引定位,索引以1为始
tagName[@class="PropertyName"]/tageName/text():/text()方法取直系标签文本内容,//text()取非直系标签(其下所有)文本内容,返回列表,可通过下标选择某个列表值
tagName[@class="PropertyName"]/tagName/@PropertyName:通过@标签中属性来提取属性值

本地测试

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
#!/usr/bin/env python3
# -*-coding:utf-8-*-
from lxml import etree

if __name__ == "__main__":
# 实例化etree对象,并写入本地html源码
# 默认是XML解析器,碰到不规范的html文件时就会解析错误,增加解析器
parser = etree.HTMLParser(encoding='utf-8')
tree = etree.parse('localWeb.html', parser=parser)
# 找网站标题,单一,不会重复
# r = tree.xpath('/html/head/title')
# 匹配到该层级下同辈份的所有div标签,
# r = tree.xpath('/html/body/main/div/div')
# 匹配到使用多层级选择符(//)父级(此处为main)之后不同辈分的所有div标签
# r = tree.xpath('/html/body/main//div')
# 属性定位,精准定位--此处定位到个人名言处
# r = tree.xpath('/html/body/main//div[@class="description"]')
# 同效果
# r = tree.xpath('//div[@class="description"]')
# 索引定位
r = tree.xpath('//div[@class="s-icon-list"]/span[1]')
# /text()方法取直系标签内容,返回列表
# r = tree.xpath('//div[@class="header-drawer"]//li[2]/a/text()')
# 利用下标选择返回列表的某个值
# r = tree.xpath('//div[@class="header-drawer"]//li[2]/a/text()')[0]
# //text()取其下所有标签内容
r = tree.xpath('//div[@class="header-drawer"]//li//text()')
print(r)

58二手房

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
#!/usr/bin/env python3
# -*-coding:utf-8 -*-

import requests
from lxml import etree

# 爬取58二手房房源信息
if __name__ == "__main__":
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36 Edg/96.0.1054.53'
}
url = 'https://bj.58.com/ershoufang/'
page_text = requests.get(url=url, headers=headers).text
# xpath解析,并定位需求标签(所取内容父标签)
tree = etree.HTML(page_text)
div_list = tree.xpath('//section[@class="list"]/div')
# print(div_list)
# 存储
fp = open('./58.txt', 'w', encoding='utf-8')
# 取a标签-div-h3标签的内容
for div in div_list:
# 局部定位
title = div.xpath('./a/div[2]//h3/text()')[0]
# fp.write(title,'\n')相当于用了两次write(),推荐用+连接
fp.write(title + '\n')
# fp.close()
# print(title)

彼岸图网图片

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
#!/usr/bin/env python3
# -*-coding:utf-8-*-

# 爬取彼岸图网4k图片:https://pic.netbian.com
import requests
from lxml import etree
import os

if __name__ == "__main__":
if os.path.exists('./biantuwang') is False:
os.mkdir('./biantuwang')
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36 Edg/96.0.1054.53'
}
# 其他页面图片URL,可做循环来调用:https://pic.netbian.com/4kdongman/index_2.html
url = 'https://pic.netbian.com/4kdongman/'
# page_text = requests.get(url=url, headers=headers).text.encode("ISO-8859-1")
response = requests.get(url=url, headers=headers)
# 手动设置响应数据的编码方式
# response.encoding = 'utf-8'
page_text = response.text
tree = etree.HTML(page_text)
# 建议相对路径的第一个标签属性名为唯一值
li_list = tree.xpath('//div[@class="slist"]/ul/li')
# print(li_list)
for li in li_list:
img_src = 'https://pic.netbian.com' + li.xpath('./a/img/@src')[0]
# img_src = li.xpath('./a/img/@src')
img_name = li.xpath('./a/img/@alt')[0] + '.jpg'
# 通用解决中文乱码,需重新赋值
img_name = img_name.encode('ISO-8859-1').decode('gbk')
# print(img_name, img_src)
img_data = requests.get(url=img_src, headers=headers).content
# 文件夹bianantuwang需提前建立
img_path = './biantuwang/' + img_name
# print(img_path)
with open(img_path, 'wb') as fp:
fp.write(img_data)
fp.close()
print(img_name + '下载成功!!!')

空气检测平台城市

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
#!/usr/bin/env python3
# -*-coding:utf-8-*-
# 爬取中国空气质量在线监测分析平台各城市名称
import requests
from lxml import etree

if __name__ == "__main__":
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36 Edg/96.0.1054.53'
}
url = 'https://www.aqistudy.cn/historydata/'
page_text = requests.get(url=url, headers=headers).text
# print(page_text)
tree = etree.HTML(page_text)
# print(tree)
# 懒得分两步了,直接热门和所有一起拉取
li_list = tree.xpath('//div[@class="bottom"]/ul//li')
# print(li_list)
all_city_names = []
fp = open('./citys.txt', 'w', encoding='utf-8')
for li in li_list:
city_name = li.xpath('./a/text()')[0]
fp.write(city_name + '\n')
all_city_names.append(city_name)
print(city_name)
fp.close()

站长之家免费简历

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
#!/usr/bin/env python3
# -*-coding:utf-8-*-

# 爬取站长之家免费简历模板
import requests
import os
from lxml import etree

if __name__ == "__main__":
if os.path.exists('./jianli') is False:
os.mkdir('./jianli')
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36 Edg/96.0.1054.53'
}
# 其他 https://sc.chinaz.com/jianli/free_2.html
url = 'https://sc.chinaz.com/jianli/free_{num}.html'
# 多页拉取
for x in range(29, 30):
# new_url = format(url % x)
# new_url = 'https://sc.chinaz.com/jianli/free_{num}.html'.format(num=x)
url = url.format(num=x)
# print(new_url)
# 单页拉取
# url = 'https://sc.chinaz.com/jianli/free.html'
page_text = requests.get(url=url, headers=headers).text
tree = etree.HTML(page_text)
# print(tree)
div_list = tree.xpath('//div[@id="main"]/div/div')
# print(div_list)
for div in div_list:
a_href = 'https:' + div.xpath('./a/@href')[0]
# print(a_href)
a_href_text = requests.get(url=a_href, headers=headers).text.encode('ISO-8859-1')
a_tree = etree.HTML(a_href_text)
a_src = a_tree.xpath('//div[@class="down_wrap"]/div[2]/ul/li[3]/a/@href')[0]
# 此处增加去除开头和结尾的空格
a_name = a_tree.xpath('//div[@class="bgwhite"]/div/h1/text()')[0].strip() + '.rar'
# 该方式于此处似乎无效
# 试下.content.decode('utf-8')
# a_name = a_name.encode('ISO-8859-1').decode('gbk')
# print(a_name)
# print(a_src)
# 获取二进制数据,写入a_name文件
rar_page = requests.get(url=a_src, headers=headers).content
with open('./jianli/' + a_name, 'wb') as fp:
fp.write(rar_page)
fp.close()
print(a_name + '-->下载完毕!!!')
print("\n当前拉取到第{}页".format(x))
print('\n全部简历拉取结束!!!')
 评论