数据解析

文章目录

数据解析分类

  • 正则
  • bs4
  • xpath(***)

数据解析原理

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

正则匹配

先学习正则表达式

import re

// \w  匹配数字、字母、下划线
print(re.findall('\w','abc123_*()-='))  # findall是从左到右匹配字符串
// ['a', 'b', 'c', '1', '2', '3', '_']

// \W 匹配非数字字母下划线
print(re.findall('\W','aAbc123_*()-='))
//['*', '(', ')', '-', '=']

// \s 匹配任意空白字符,等价于\t\n\r\f
print(re.findall('\s','aAb\tc\r123_*()-= '))
//['\t', '\r', ' ']

// \S 匹配任意非空字符
print(re.findall('\S','aAb\tc\r123_*()-= '))
//['a', 'A', 'b', 'c', '1', '2', '3', '_', '*', '(', ')', '-', '=']

// \d 匹配任意数字,等价于[0-9]
print(re.findall('\d','aAb\tc\r123_*()-= '))
//['1', '2', '3']

// \D 匹配任意非数字
print(re.findall('\D','aAb\tc\r123_*()-= '))
//['a', 'A', 'b', '\t', 'c', '\r', '_', '*', '(', ')', '-', '=', ' ']

// \A 只匹配字符串的开头
print(re.findall('\Ashyshy','shyshyis is 20'))
//['shyshy']

// \Z 只匹配字符串的结尾
print(re.findall('20\Z','shyshyis is 20'))
//['20']

// ^ 匹配字符串的开头
print(re.findall('^shyshy','shyshyis is 20'))
//['shyshy']

// $ 匹配字符串的结尾
print(re.findall('20$','shyshyis is 20'))
//['20']

// . 匹配除\n外的任意字符,指定re.DOTALL才能匹配换行符
print(re.findall('a.b','a b ab acb a\nb acccb'))
//['a b', 'acb']
print(re.findall('a.b','a b ab acb a\nb acccb',re.DOTALL))
//['a b', 'acb', 'a\nb']

// * 左侧字符重复0次或无数次
print(re.findall('ab*','ab abb abbbbbbb ab aa'))
// ['ab', 'abb', 'abbbbbbb', 'ab', 'a', 'a']

// + 左侧字符重复1次或无数次
print(re.findall('ab+','ab abb abbbbbbb ab aa bbbbbbbbb'))
// ['ab', 'abb', 'abbbbbbb', 'ab']

// ? 匹配左侧字符0次或1次
print(re.findall('ab?','ab abb abbbbbbb ab aa bbbbbbbbb'))
// ['ab', 'ab', 'ab', 'ab', 'a', 'a']

// {m,n} 匹配左侧字符m到n次,{n} 代表只出现n次
print(re.findall('ab{2,5}','ab abb abbbbbbb ab aa bbbbbbbbb'))
// ['abb', 'abbbbb']

// [] 匹配字符1次
print(re.findall('a[0-9]b','a1b a22b acb'))
// ['a1b']

// [^] 对匹配的内容取反
print(re.findall('a[^0-9]b','a1b a22b acb'))
// ['acb']

// () 是为了提取匹配字符串的,表达式中有几个()就有几个相应的匹配字符串

爬取糗事百科图片

首先是爬取单个图片

import requests

if __name__ == '__main__':
    url = 'https://pic.qiushibaike.com/system/pictures/12401/124017243/medium/CAN7KLR2PPDXJ932.jpg'
    headers = {
        'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36'
    }
    # content返回文件的二进制数据
    # text (字符串) content (二进制数据) json(对象)
    img_data = requests.get(url=url,headers=headers).content
    with open('./1.jpg','wb') as fp:
        fp.write(img_data)

注意url是图片的地址,和之前的爬取文本不同,这里因为爬取的是图片,所有获取数据时不说text,而是能返回文件二进制数据的content,在写入时,注意是wb而并非w,下面是他们的区别:

w 打开一个文件只用于写入。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。
wb 以二进制格式打开一个文件只用于写入。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。

接下来是爬取这一页的所有图片,首先,可以在源码中看到图片的地址,主要是被以下标签包裹

<div class="thumb">
    <a href="/article/124104208" target="_blank">
    <img src="//pic.qiushibaike.com/system/pictures/12410/124104208/medium/JIIR7Y6T2PDU8P0V.jpg" alt="糗事#124104208" class="illustration" width="100%" height="auto">
    </a>
    </div>

想要得到中间的地址,需要用正则匹配

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

这里面,.*?表示非贪婪匹配,满足条件的情况只匹配一次,()用于提取匹配字符串的。

匹配完了后,因为这个网址中没有协议头,需要拼接一个https:上去,这个可以用列表遍历来实现;因为图片数量比较多,可以用os模块去执行系统命令os.mkdir('./qiutupic'),创建一个目录,然后将图片保存到里面。

最后,还剩下图片名的问题,可以看到,html标签中是有图片名的,我们可以用字符串切割,来得到图片名,这里可以用split()函数来实现。

import requests
import re
import os

if __name__ == '__main__':
    if not os.path.exists('./qiutupic'):
        os.mkdir('./qiutupic')
    url = 'https://www.qiushibaike.com/imgrank/'
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36'
    }
    # 使用通用爬虫对url对应的一整张页面爬取
    page_text = requests.get(url=url, headers=headers).text
    # 使用聚焦爬虫对页面中所有糗图进行提取
    """
    <div class="thumb">
    <a href="/article/124104208" target="_blank">
    <img src="//pic.qiushibaike.com/system/pictures/12410/124104208/medium/JIIR7Y6T2PDU8P0V.jpg" alt="糗事#124104208" class="illustration" width="100%" height="auto">
    </a>
    </div>
    """
    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:
        # 拼接出一个完整的url
        src = 'https:' + src
        img_data = requests.get(url=src, headers=headers).content
        # 生成图片名称
        img_name = src.split('/')[-1] //将url以/切割开后,查找倒数第一个(-1)
        # 拼接图片路径
        imgpath = './qiutupic/' + img_name
        with open(imgpath,'wb') as fp:
            fp.write(img_data)
            print(img_name + 'success')

str.split(str="", num=string.count(str)).
str – 分隔符,默认为所有的空字符,包括空格、换行(\n)、制表符(\t)等。
num – 分割次数。默认为 -1, 即分隔所有。

最后,实现对每个分页的爬取,

import requests
import re
import os

if __name__ == '__main__':
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36'
    }
    if not os.path.exists('./wholeqiutupic'):
        os.mkdir('./wholeqiutupic')
    # 设置通用url模板
    url = 'https://www.qiushibaike.com/imgrank/page/%d'
    for pagenum in range(1,13):
        # 对应页码的url
        new_url = format(url%pagenum)

        # 使用通用爬虫对url对应的一整张页面爬取
        page_text = requests.get(url=new_url, headers=headers).text
        # 使用聚焦爬虫对页面中所有糗图进行提取
        """
        <div class="thumb">
        <a href="/article/124104208" target="_blank">
        <img src="//pic.qiushibaike.com/system/pictures/12410/124104208/medium/JIIR7Y6T2PDU8P0V.jpg" alt="糗事#124104208" class="illustration" width="100%" height="auto">
        </a>
        </div>
        """
        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:
            # 拼接出一个完整的url
            src = 'https:' + src
            img_data = requests.get(url=src, headers=headers).content
            # 生成图片名称
            img_name = src.split('/')[-1]
            # 拼接图片路径
            imgpath = './wholeqiutupic/' + img_name
            with open(imgpath,'wb') as fp:
                fp.write(img_data)
                print(img_name + 'success')

因为每次换页,不同的是url中最后的数字,所以可以采用for循环的方式,依次遍历,并格式化字符串,将页数填充到url中,这样就可以实现对不同页数的爬取。

上一篇:Python正则表达式的findall函数与分组


下一篇:【Python基础】Python正则表达式入门到入魔