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

声明

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

聚焦爬虫

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

    编码流程

  1. 指定url

  2. 发起请求

  3. 数据解析

  4. 持久化存储

    方法分类

  5. 正则表达式

  6. bs4解析

  7. xpath解析

    简述使用

  8. 需求内容在标签间或作为标签的属性存储

  9. 标签定位

  10. 从标签间或标签属性值中提取所需

    正则表达式

    网页源代码

    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解析

    简述原理

  11. 实例化一个BeautifulSoup对象,将页面源码数据加载到该对象中

  12. 调用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

    使用概要

  13. from bs4 import BeautifulSoup

  14. Beautiful Soup对象实例化

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

    1
    2
    fp = open('./localWeb.html', 'r', encoding='utf-8')
    soup = BeautifulSoup(fp, 'lxml')
  16. 或,将门户网站拉取的页面源码加载到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

    使用举例

  3. 本地:etree.parse(filePath)

  4. 网络: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全部简历拉取结束!!!')
  • 本文标题:爬虫之数据解析相关
  • 本文作者:涂寐
  • 创建时间:2021-12-28 10:26:20
  • 本文链接:https://0xtlu.github.io/article/c86c0afc.html
  • 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
 评论