爬虫入门_3:数据解析及案例实战

2023-11-17

聚焦爬取:爬取页面中指定的页面内容
编码流程:

  1. 指定url

  2. 发起请求

  3. 获取响应数据

  4. 数据解析

  5. 持久化存储

数据解析分类

  • 正则
    • re.M: 多行匹配
    • re.S:单行匹配
  • bs4
  • xpath(***)

数据解析原理概述:

  • 解析的局部的文本内容都会在标签之间或者标签对应的属性中进行存储
  1. 进行指定标签的定位
  2. 标签或者标签对应的属性中存储的数据值进行提取(解析)

1. 数据解析—正则表达式

正则表达式在线测试地址:https://c.runoob.com/front-end/854/

常用正则表达式回顾

  • 单字符

    []: 匹配集合中任意个字符,只要满足中括号中的字符,都可以匹配
    \d: 匹配任意数字,与'[0-9]'一样
    \D: 匹配任意非数字,与'[^0-9]'一样
    \w: 数字、字母、下划线、中文
    \W: 非\w
    \s: 所有的空白字符,包括空格、制表符、换页符等等。等价于[ \f\n\r\t\v]
    \S: 非空白
    
  • 数据修饰

    * :  任意多次 >=0
    + :  至少一次 >=1
    ? :  可有可无 0次或1次
    {m}:  固定m次 hello{3,}
    {m,}: 至少m次
    {m,n}: 大于等于m次并小于等于n次才会被匹配
    
  • 边界

    $: 以某某结尾
    ^: 以某某开头
    
  • 分组

    (ab)

  • 贪婪模式:.*

  • 非贪婪(惰性)模式: .*?

  • re.I:忽略大小写

  • re.M: 多行匹配

  • re.S: 单行匹配

  • re.sub用于替换字符串中的匹配项

    re.sub(正则表达式,替换内容,字符串)

    phone = "2004-959-559 # 这是一个国外电话号码"
    # 删除字符串中的 Python注释 
    num = re.sub(r'#.*$', "", phone)
    print("电话号码是: ", num)
    
    # 输出为
    电话号码是:  2004-959-559 
    

正则练习

import re

# 提取出python
key = 'javapythonc++phpscala'
print(re.findall('python',key)[0])   # 输出:python

# 提取出hello world
key = '<html><h1>hello world<h1></html>'
print(re.findall('<h1>(.*)<h1>',key)[0])  # 输出:hello world

# 提取170
string = '我喜欢身高为170的女孩'
print(re.findall('\d+',string))  # 输出:['170']

# 提取出http://和https://
key = 'http://www.baidu.com and https://boob.com'
print(re.findall('https?://',key))  # 输出:['http://', 'https://']

# 提取出hello
key = 'lalala<hTml>hello</HtMl>hahah' 
print(re.findall('<[Hh][Tt][Mm][Ll]>(.*)</[Hh][Tt][Mm][Ll]>',key))  # 输出:['hello']

# 提取出hit.
key = 'bobo@hit.edu.com'
# 以下两个式子等价
print(re.findall('h.*?\.',key))  # 输出:['hit.']
print(re.findall('@(.*)e',key))  # 输出:['hit.']

# 匹配sas和saas
key = 'saas and sas and saaas'
print(re.findall('sa{1,2}s',key))  # 输出:['saas', 'sas']

实战

需求:爬取彼岸图网站中指定页面的图片,解决分页爬取问题,并将其保存到指定文件夹中

"""
需求:爬取彼岸图网站中风景板块下所有的图片
网站地址:https://pic.netbian.com/
"""

import requests
import os
import re

if __name__ == '__main__':
    # UA伪装,相关的头信息封装在字典结构中
    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'
    }
    # 创建一个文件夹,保存所有的图片
    dir_path = './result/biantuLibs'
    if not os.path.exists(dir_path):
        os.makedirs(dir_path)

    # 设置一个通用的url模板
    url = 'https://pic.netbian.com/4kfengjing/index_%d.html'

    for pageNum in range(1,5):  # 这里只爬取了前4页,可以修改数据爬取多个页面的图片
        # 对应页码的url
        # 由于第一页的url和其他页的不一样,这里需要单独处理
        if pageNum == 1:
            new_url = 'https://pic.netbian.com/4kfengjing/'
        else:
            new_url = format(url%pageNum)

        # 使用通用爬虫对url对应的一整张页面进行爬取
        page_text = requests.get(url=new_url,headers=headers).text

        # 使用聚焦爬虫将页面中所有的风景图进行解析/提取
        ex = '<li>.*?<img src="(.*?)" alt=.*?</li>'
        img_src_list = re.findall(ex,page_text,re.S)  # re.S:单行匹配
        # print(img_src_list)
        for src in img_src_list:
            # 拼接出一个完整的图片url
            src = 'https://pic.netbian.com' + src
            # 请求到了图片的二进制数据
            img_data = requests.get(url=src,headers=headers).content
            # 生成图片名称
            img_name = src.split('/')[-1]  # 该图片名称带后缀
            # 图片存储的路径
            imgPath = dir_path + '/' +img_name
            with open(imgPath,'wb') as fp:
                fp.write(img_data)
                print(img_name,"下载成功!!!")

2. 数据解析—bs4解析

bs4数据解析原理

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

环境安装

pip install bs4
# bs4在使用时,需要一个第三方库,把这个库也需要安装下
pip install lxml

基础使用

使用流程
  • 导包:from bs4 import BeautifulSoup

  • 使用方式:可以将一个html文档,转化为BeautifulSoup对象,然后通过对象的方法或者属性去查找指定的结点内容

    • 将本地的html文档中的数据加载到该对象中

      fp = open('./test.html','r',encoding='utf-8')
      soup = BeautifulSoup(fp,'lxml')
      
    • 将互联网上获取的页面源码加载到该对象中

      page_text = response.text
      soup = BeautifulSoup(page_text,'lxml')
      
    • 打印soup对象显示内容为html文件中的内容

基础巩固
  • 提供的用于数据解析的方法属性

    • soup.tagName:返回的是文档中第一次出现的tagName对应的标签

    • soup.find():

      • find(‘tagName’):等同于soup.div

      • 属性定位:

        soup.find(‘div’,class_/id/attr=‘song’)

    • soup.findall(‘tagName’):返回符合要求的所有标签(列表)

    • select

      • select(‘某种选择器(id,class,标签…选择器)’),返回的是一个列表
      • 层级选择器:
        • soup.select(’.tang > ul > li > a’): > 表示的是一个层级
        • soup.select(’.tang > ul a’): 空格 表示的是多个层级
  • 获取标签之间的文本数据

    • soup.a.text/string/get_text()
    • text/get_text():可以获取某一个标签中所有的文
    • string:只可以获取该标签下面的直系的文本内容
  • 获取标签中属性值

    • soup.a[‘href’]
from bs4 import BeautifulSoup

if __name__ == '__main__':
    # 将本地的html文档中的数据加载到该对象中
    fp = open('./data/test.html', 'r', encoding='utf-8')
    soup = BeautifulSoup(fp, 'lxml')
    print(soup)
    print(soup.a)  # soup.tagName,返回的是html第一次出现的tagName标签
    print(soup.div)

    # find('tagName'):等同于soup.div
    print(soup.find('div'))
    print(soup.find('div', class_='song'))  # 属性定位
    print(soup.find_all('a'))  # 获取所有的a标签,是个列表

    print(soup.select('.tang'))  # 类选择器
    print(soup.select('.tang > ul > li > a')[0])  # 层级选择器,‘>’ 表示的是一个层级
    print(soup.select('.tang > ul a')[0])  # 空格 表示的是多个层级

    # 获取标签之间的文本内容
    # text/get_text():可以获取某一个标签中所有的文本内容
    # string:只可以获取该标签下面的直系的文本内容
    print(soup.select('.tang > ul a')[0].text)
    print(soup.select('.tang > ul a')[0].get_text())
    print(soup.select('.tang > ul a')[0].string)
    print(soup.find('div', class_='song').text)
    print(soup.find('div', class_='song').string)

    # 获取标签中的属性值
    print(soup.select('.tang > ul a')[0]['href'])

实战

需求:爬取三国演义小数所有的章节标题和章节内容
地址: https://www.shicimingju.com/book/sanguoyanyi.html

import requests
from bs4 import BeautifulSoup
if __name__ == '__main__':
    # UA伪装,相关的头信息封装在字典结构中
    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://www.shicimingju.com/book/sanguoyanyi.html'
    # 发起请求,获取响应数据
    html = requests.get(url=url,headers=headers)
    html.encoding = 'utf-8'  # 将编码设置为‘utf-8',不然会出现乱码
    page_text = html.text
    # 在首页中解析出章节的标题和详情页的url
    # 1. 实例化BeautifulSoup对象,需要将页面源码数据加载到该对象中
    soup = BeautifulSoup(page_text,'lxml')
    # 解析章节标题和详情页的url
    li_list = soup.select('.book-mulu > ul > li')
    fp = open('./result/sanguo.txt','w',encoding='utf-8')
    for li in li_list:
        title = li.a.string
        # print(li.a.text)
        detail_url = 'https://www.shicimingju.com' + li.a['href']
        # 对详情页发起请求,解析出章节内容
        detail_html = requests.get(url=detail_url, headers=headers)
        detail_html.encoding = 'utf-8'  # 将编码设置为‘utf-8',不然会出现乱码
        detail_page_text = detail_html.text
        # 解析出详情页中相关的章节内容
        detail_soup = BeautifulSoup(detail_page_text,'lxml')
        div_tag = detail_soup.find('div',class_='chapter_content')
        # 解析到了章节的内容
        content = div_tag.text
        # 持久化存储
        fp.write(title + ':' + content + '\n')
        print(title,'爬取成功!!!')

3. 数据解析—xpath解析

xpath解析原理:

  1. 实例化一个etree的对象,且需要将被解析的页面源码数据加载到该对象中
  2. 通过调用etree对象中的xpath方法结合xpath表达式实现标签定位和内容的捕获

环境安装

pip install lxml

基本使用

使用流程
  1. 将本地的html文档中的源码数据加载到etree对象中

    etree.parse(filePath)

  2. 可以将从互联网上获取的源码数据加载到该对象中

    etree.HTML(‘page_text’)

  3. xpath(‘xpath表达式’)

基础巩固
  • /:表示的是从根节点开始定位。表示的是一个层级

  • //:表示的是多个层级。可以表示从任意位置开始定位

  • 属性定位://div[@class=‘song’] tag[@attrName=“attrValue”]

  • 索引定位://div[@class=“song”]/p[3] 索引是从1开始的

    • /text():获取的是标签中直系的文本内容
    • //text():标签中非直系的文本内容(标签下的所有文本内容)
  • 取文本:

    • /text():获取的是标签中直系的文本内容

    • //text():标签中非直系的文本内容(标签下的所有文本内容)

  • 取属性:

    • /@attrName: ==> img/@src
from lxml import etree

if __name__ == '__main__':
    # UA伪装,相关的头信息封装在字典结构中
    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'
    }
    # 实例化一个etree对象,且将被解析的源码加载到了该对象中
    tree = etree.parse('./data/test.html')
    # 层级定位
    # r = tree.xpath('/html/body/div')
    # r = tree.xpath('/html//div')
    # r = tree.xpath('//div')

    # 属性定位
    # r = tree.xpath('//div[@class="song"]')
    # r = tree.xpath('//div[@class="song"]/p[3]')  # 索引定位,索引标签是从1开始

    # 取文本
    # r = tree.xpath('//div[@class="tang"]/ul/li[5]/a/text()')[0]
    # r = tree.xpath('//li[7]//text()')
    # r = tree.xpath('//div[@class="tang"]//text()')

    # 取属性
    r = tree.xpath('//div[@class="song"]/img/@src')
    print(r)

实战

案例1

需求:爬取58二手房中的房源信息

import requests
from lxml import etree

if __name__ == '__main__':
    # UA伪装,相关的头信息封装在字典结构中
    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 = 'https://bj.58.com/ershoufang/'
    page_text = requests.get(url=url, headers=headers).text

    # 数据解析
    tree = etree.HTML(page_text)
    # 存储的就是div标签对象
    div_list = tree.xpath('//section[@class="list"]/div')
    fp = open('./result/58.txt','w',encoding='utf-8')
    for div in div_list:
        # 局部解析
        title = div.xpath('./a/div[2]/div[1]/div[1]/h3/text()')[0]
        price = div.xpath('./a/div[2]/div[2]/p[1]/span/text()')
        total_price = price[0]+price[1]  # 0:表示价格,1:表示单位
        fp.write(title+' ')
        fp.write(total_price+'\n')
案例2

需求:解析下载图片数据
地址: https://pic.netbian.com/4kmeishi/

import requests
from lxml import etree
import os
if __name__ == '__main__':
    # UA伪装,相关的头信息封装在字典结构中
    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 = 'https://pic.netbian.com/4kmeishi/'
    response = requests.get(url=url, headers=headers)
    # 中文乱码解决方法1:手动设置响应数据的编码格式,如果不行,则采用下面的方法2
    # response.encoding = 'utf-8'  # 将编码设置为‘utf-8',不然会出现乱码
    page_text = response.text

    # 解析数据:src的属性值 alt属性值
    tree = etree.HTML(page_text)
    li_list = tree.xpath('//ul[@class="clearfix"]/li')

    # 创建一个文件夹
    if not os.path.exists('./result/picLibs/'):
        os.makedirs('./result/picLibs/')
    for li in li_list:
        img_src = 'https://pic.netbian.com' + li.xpath('./a/img/@src')[0]
        img_name = li.xpath('./a/img/@alt')[0] + '.jpg'
        # 中文乱码解决方法2:通用处理中文乱码的解决方案
        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
        img_path = './result/picLibs/' + img_name
        with open(img_path,'wb') as fp:
            fp.write(img_data)
            print(img_name,"下载成功!!!")
案例3

需求:解析出所有城市名称
地址:https://www.aqistudy.cn/historydata/

import requests
from lxml import etree

if __name__ == '__main__':
    # UA伪装,相关的头信息封装在字典结构中
    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 = 'https://www.aqistudy.cn/historydata/'
    page_text = requests.get(url=url, headers=headers).text

    tree = etree.HTML(page_text)
    # 解析热门城市和全部城市对应的a标签
    # 热门城市a标签的层级关系://div[@class="bottom"]/ul/li/a
    # 全部城市a标签的层级关系://div[@class="bottom"]/ul/div[2]/li/a
    a_list = tree.xpath('//div[@class="bottom"]/ul/li/a | //div[@class="bottom"]/ul/div[2]/li/a')
    all_city_names = []
    for a in a_list:
        city_name = a.xpath('./text()')[0]
        all_city_names.append(city_name)

    print(len(all_city_names), all_city_names)
案例4

需求:爬取站长素材中免费简历模板
地址:https://sc.chinaz.com/jianli/free.html

import requests
from lxml import etree
import os

if __name__ == '__main__':
    # UA伪装,相关的头信息封装在字典结构中
    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'
    }
    # 创建一个文件夹
    if not os.path.exists('./result/jianli_free/'):
        os.makedirs('./result/jianli_free/')

    for i in range(1, 5):
        if i == 1:
            url = 'https://sc.chinaz.com/jianli/free.html'
        else:
            url = 'https://sc.chinaz.com/jianli/free_{}.html'.format(str(i))
        print(url)
        page_text = requests.get(url=url, headers=headers).text

        tree = etree.HTML(page_text)
        a_list = tree.xpath('//div[@id="main"]/div/div/a')

        for a in a_list:
            a_url = "https:" + a.xpath('./@href')[0]
            a_page_text = requests.get(url=a_url, headers=headers).text
            tree = etree.HTML(a_page_text)
            jianli_url = tree.xpath('//ul[@class="clearfix"]/li[1]/a/@href')[0]
            cur_jianli = requests.get(url=jianli_url, headers=headers).content
            # 持久化存储简历
            jianli_name = a.xpath('./img/@alt')[0].strip() + '_' + jianli_url.split('/')[-1]
            jianli_name = jianli_name.encode('iso-8859-1').decode('utf-8')  # 防止中文乱码
            jianli_path = './result/jianli_free/' + jianli_name
            with open(jianli_path, 'wb') as fp:
                fp.write(cur_jianli)
                print(jianli_name, "下载成功!!!")
        print("第{}页爬取完成!!!".format(i))

如果本文对你有帮助,记得“点赞”哦~

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

爬虫入门_3:数据解析及案例实战 的相关文章

随机推荐

  • webpack4 devserver 如何拦截请求 添加请求headers

    v4官网 https v4 webpack docschina org configuration dev server devserver proxy devServer 配置 devServer host 127 0 0 1 https
  • 使用ffmpeg将mp4转为m3u8并播放

    ffmpeg 下载地址 https ffmpeg zeranoe com builds 这个是我自己的ffmpeg 有积分的大佬可以任性下载 ffmpeg压缩包 下载解压之后需要将ffmpeg添加到环境变量中 cmd中输入 ffmpeg v
  • R语言与数据分析实战4-变量的创建与修改

    第1关 创建新变量 在进行实际的数据分析时 我们会经常需要创建新变量或者为当前存在的变量变换新的取值 这就好比你是一个厨师 现在你要创新菜式 需要做一些新的厨房模具或者是改良当前已有的厨具来进行烹饪 对于创建新变量 其实原理非常简单 大家只
  • shell-2-文本处理工具

    主题 文本处理工具 一 grep grep 全局搜索正则表达式 固定字符表示通用匹配 whatis grep man grep 查看grep用法 正则表达式分为两种 传统 扩展正则表达式 对应的 有两个命令 grep 传统 和 egrep
  • nginx应用指南

    一 nginx基本简述 最新更新 请点击这个里1 概念 nginx是一个开源且高性能 可靠的HTTP中间件 代理服务 开源 直接获取源代码 高性能 支持海量并发 2 nginx应用场景 静态处理 反向代理 负载均衡 资源缓存 安全防护 访问
  • Gson解析JSON数据中动态未知字段key的方法

    有时在解析json数据中的字段key是动态可变的时候 由于Gson是使用静态注解的方式来设置实体对象的 因此我们很难直接对返回的类型来判断 但Gson在解析过程中如果不知道解析的字段 就会将所有变量存储在一个Map中 我们只要实例化这个ma
  • 严格模式和非严格模式区别

    严格模式和非严格模式有什么区别 严格模式对正常的 JavaScript语义做了一些更改 首先 严格模式通过抛出错误来消除了一些原有静默错误 其次 严格模式修复了一些导致 JavaScript引擎难以执行优化的缺陷 有时候 相同的代码 严格模
  • mutex 互斥

    文章目录 互斥 mutex 类 要求 公共方法 注意 lock unlock try lock 相关参考 互斥 互斥算法避免多个线程同时访问共享资源 这会避免数据竞争 并提供线程间的同步支持 mutex 类 mutex 类是能用于保护共享数
  • 【程序开发】手把手教你写一个Python爬虫

    经常听音乐的的人有一个苦恼 很多自己喜欢的歌曲 因为各种原因无法进行免费下载 很多人没办法 只能咬咬牙开个会员 都是自己辛苦挣的人民币啊 幸好 我们还有爬虫 通过爬虫 我们可以很轻易 很快速的获取互联网上的资源 不管是音乐视频 还是工作和商
  • PPPoE协议详解

    PPPoE协议详解 PPPoE协议的工作流程包含发现和会话两个阶段 发现阶段是无状态的 目的是获得PPPoE终结端 在局端的ADSL设备上 的以太网MAC地址 并建立一个惟一的PPPoESESSION ID 发现阶段结束后 就进入标准的PP
  • 非系统盘安装linux,Windows10 Linux子系统安装/迁移到非系统盘(示例代码)

    oboth DESKTOP BUFOEB1 mnt c Users luoz mnt d LxRunOffline exe 一 通过wsl命令迁移 备份Linux分发 操作步骤 wsl exe 命令用法 wsl h 命令行选项无效 h 版权
  • 子串和子序列问题-动态规划向

    1 子串子序列问题概述 有关于子序列和子串的问题是字符串或者数组经常会遇到的问题 一般我们经常使用多指针 滑动窗口 回溯 动态规划的方式去解决 而本篇重点关注能用动态规划解决或者说明显使用动态规划解决的子串问题和子序列问题 1 1 子串 子
  • <string>库和<cstring>库常用函数

    库和库常用函数 void memcpy void p1 const void p2 size t n void memset void p int v size t n char strcpy char p1 char p2 char st
  • canvas arcTo()用法详解

    arcTo x1 y1 x2 y2 radius arcTo 方法将利用当前端点 端点1 x1 y1 和端点2 x2 y2 这三个点所形成的夹角 然后绘制一段与夹角的两边相切并且半径为radius的圆上的弧线 弧线的起点就是当前端点所在边与
  • c语言经典代码实现0-100的和

    include
  • 【C++】-- STL之list详解

    目录 list类 1 list类对象构造 2 迭代器 3 empty 4 size 5 front 6 back 7 push front 8 pop front 9 push back 10 pop back 11 insert 12 e
  • 基类与派生类对象的关系 派生类的构造函数

    博主主页 一怀明月 专栏系列 线性代数 C初学者入门训练 题解C C的使用文章 初学 C 座右铭 不要等到什么都没有了 才下定决心去做 大家觉不错的话 就恳求大家点点关注 点点小爱心 指点指点 目录 基类与派生类对象的关系 派生类的构造函数
  • linux三剑客实战nginx日志

    nginx日志的格式 统计日志中出现404 500 502 400错误的次数 grep 404 500 nginx log wc l awk 9 404 500 nginx log wx l 表示包含 表示不包含 awk BEGIN num
  • java.lang.NoClassDefFoundError:could not initial class xxxxxxxxx

    转载 http www voidcn com blog wangjun5159 article p 6223131 html API 当出现这个异常时 我的第一反应是 它跟ClassNotFoundException有什么不同 为了找到确切
  • 爬虫入门_3:数据解析及案例实战

    聚焦爬取 爬取页面中指定的页面内容 编码流程 指定url 发起请求 获取响应数据 数据解析 持久化存储 数据解析分类 正则 re M 多行匹配 re S 单行匹配 bs4 xpath 数据解析原理概述 解析的局部的文本内容都会在标签之间或者