利用python爬虫(案例6+part15)--如何爬取科研数据

学习笔记



Ajax动态加载网站数据抓取

前几天小伙伴在写报告时,和我讨论了一下爬取某生态网站的统计数据问题,我看了一下,这个网站是动态加载的,想了一想,很多数据网站的数据都是动态加载的,那么脆写一个案例吧,方便大家进行数据收集和整理。

在爬取数据之前,我先讲几个关于动态加载网站的知识点,方便大家理解代码。


动态加载的类型

  • 部分页面刷新的动态加载

我们首先看一个动态加载的网站(这个网站也是我们之后要爬取的网站):


当我们进行翻页操作时,URL地址并没有变化,网站只进行了部分页面的刷新,只有表格内的数据有变化,页面的其他部分并无改变:


  • 滚动页面自动触发页面加载

我们再看一个大家都很熟悉的动态加载的网站,某东。当我们在某东上搜索【华章数学时】会出现如下页面:


我们审查元素,查看当前页面中的最后一个商品的HTML结构:



通过观察,我们发现每一个商品被包裹在一个li标记中,那么当前页面的最后一个商品,应该是被包裹在最后一个li标签中。现在我们滑动鼠标滑轮,边滑动页面边观察HTML页面结构:


哎呀!我们发现,当滑倒底部时,页面自动加载出了一大堆新的被li标签包裹的商品,此时网页的URL地址依然没有改变。


那么该如何抓取数据?


我们看到在动态加载的页面下,URL是如此无力,我们无法沿用静态匹配的方法去爬取动态加载页面里的数据,那么我们该如何爬取信息呢?

需要注意的是!只要是动态加载的网站,当我们与网站发生交互,那么网站就会返回给我们JSON格式的信息,这些信息中包含了我们要爬取的数据。此时,我们只要找到这些JSON文件的地址,就成功了一大步。


如何得到JSON文件的地址?


我们以安#省生态环境厅的数据(http://sthjt.ah.gov.cn/site/tpl/5391?cityCode=340100)为例,我们发现在这个网页中进行翻页操作,只有部分页面信息被刷新,网页URL地址不变,因此该网站应该是动态加载数据的。


打开安#省生态环境厅的网页后,右键打开审查元素–>点击Network–>点击All–>进行翻页操作,获取网站和我们进行交互的数据包–>点开preview,判断该数据包中是否有我们要的数据–>点开Headers,查看数据包的头部信息:


我们关注一下Headers中基本请求信息(General)里的Request URL , 这个URL地址,就是我们需要的JSON文件的URL地址,我们打开这个地址验证一下:


很好!就是你啦。但是…这些JSON数据的格式看起来很混乱,还有很多\转义符是怎么回事?虽然这个问题不影响我们之后的数据爬取,但是我先标记一下这个问题,以后再解决


我们再回到请求头Headers,查看一下查询参数


这些查询参数,就是JSON文件URL地址的查询参数,不信的话,我们将这些查询参数与JSON文件的URL地址对比一下:

#JSON文件的URL地址
http://sthjt.ah.gov.cn/site/label/8888?IsAjax=1&dataType=json&_=0.6245771911926585&isJson=true&cityCode=340100&type=1&num=2&isPage=true&pageIndex=3&pageSize=10&labelName=airQuality

#查询参数
IsAjax:1
dataType:json
_:0.6245771911926585
isJson:true
cityCode:340100
type:1
num:2
isPage:true
pageIndex:3
pageSize:10
labelName:airQuality

嗯!没错了!


那么我们要这些查询参数有啥用呢?就像我们在之前爬取静态网页的数据时,需要观察多个URL地址的查询参数的规律一样。如果我们要爬取动态页面里的数据,这些数据可能不止在一两个JSON文件中,而是可能在大量的JSON文件中。这时,就需要通过观察查询参数的规律,来批量得到我们需要的JSON文件的URL地址,从而获取JSON文件里的数据。


观察JSON文件URL地址的查询参数


我们进行多次翻页操作,观察查询参数规律。

第一页:

IsAjax:1
dataType:json
_:0.9632859283976705
isJson:true
cityCode:340100
type:1
num:2
isPage:true
pageIndex:1
pageSize:10
labelName:airQuality

第二页:

IsAjax:1
dataType:json
_:0.699042424499402
isJson:true
cityCode:340100
type:1
num:2
isPage:true
pageIndex:2
pageSize:10
labelName:airQuality

第三页:

IsAjax:1
dataType:json
_:0.6245771911926585
isJson:true
cityCode:340100
type:1
num:2
isPage:true
pageIndex:3
pageSize:10
labelName:airQuality

我们发现除了_pageIndex在翻页时有所改变,其他查询参数不变。


这个_查询参数的规律我有点捉摸不透啊,我们看看,在URL地址中把这个查询参数去掉,能不能访问JSON文件。

去掉_查询参数后的第3页的JSON文件的URL地址:

http://sthjt.ah.gov.cn/site/label/8888?IsAjax=1&dataType=json&isJson=true&cityCode=340100&type=1&num=2&isPage=true&pageIndex=3&pageSize=10&labelName=airQuality

访问结果:

可以!删除_查询参数后不妨碍我们得到该JSON文件;同时,我们对比网页中第3页的数据,一毛一样。


现在我们观察pageIndex查询参数,可以看到第一页pageIndex的参数值为1,第二页为2,第三页为3, 则我们推断第 k k pageIndex查询参数的值为 k k . 现在我把pageIndex的参数值改为4,并访问此URL地址:

对比网页中第4页的数据,一模一样。


JSON格式数据转换成python字典


现在我们理清了JSON文件的URL地址,就可以爬取网页,获取网页内容了。但是!需要注意的是,我们如何获取JSON文件里的数据呢?我们看到JSON的数据格式,有点像python里的字典和列表。其中要爬取的数据,被data键所对应的值(一个列表)包裹;每一条记录在列表中,被一个字典包裹:

{data: [
{
aqi: "54",
area: "合肥市",
cityCode: 340100,
co: "0.363",
level: "II级",
measure: "",
no2: "30",
o3: "115",
pm10: "57",
pm25: "16",
primaryPollutant: "颗粒物(PM10)",
quality: "良",
so2: "7",
timePoint: "2020-04-15 18:00:00",
unheathful: ""
},
{
aqi: "49",
area: "合肥市",
cityCode: 340100,
co: "0.334",
level: "I级",
measure: "",
no2: "22",
o3: "123",
pm10: "49",
pm25: "15",
primaryPollutant: "—",
quality: "优",
so2: "7",
timePoint: "2020-04-15 17:00:00",
unheathful: ""
}],
pageCount: 5,
pageIndex: 4,
pageSize: 10,
startNumber: 40,
total: 47
}

但仔细观察我们又发现, 这些数据格式和字典又有细微差别,不能直接用字典的索引方式去获取数据. 所以,我们就想将JSON格式的数据转换为python字典类型,方便获取数据。

但是要怎么进行转换呢?

requests模块是我们的好帮手,使用requests.get().json()方法可以直接返回python的数据类型的字符串

语法:

html_str = requests.get(url, headers=headers).json()

我不想要字符串,我想要字典!现在我在网上查到了2种把字符串转换为字典类型的方法,演示一下:

In [43]: str_01 = '{"a":1}'

In [44]: type(json.loads(str_01))
Out[44]: dict

In [45]: type(eval(str_01))
Out[45]: dict

如何获取科研数据

好了,现在回到我们该如何获取科研数据这个问题。这里,我就以我和小伙伴想要爬取的网站为例,就是我们刚才一直在讲解的安徽省生态环境厅的网站:


在这个案例中,我就获取网页中aqi(质量指数)、cityCode(城市代码)、pm25(PM2.5)这几个变量值.

PS:因为我太懒了,不想打那么多代码,大家可以举一反三,将其他变量值获取下来。

好,开始敲代码:

# -*- coding: utf-8 -*-

import requests
import time
import csv
import json

class DeeSpider:
    def __init__(self):
        self.url = 'http://sthjt.ah.gov.cn/site/label/8888?IsAjax=1&dataType=json&isJson=true&cityCode=340100&type=1&num=2&isPage=true&pageIndex={}&pageSize=10&labelName=airQuality'
        
        self.headers = {'Accept':'application/json, text/javascript, */*; q=0.01',
                        'Accept-Language':'zh-CN,zh;q=0.9',
                        'Connection':'keep-alive',
                        'Host':'sthjt.ah.gov.cn',
                        'User-Agent':'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36',
                                                  'X-Requested-With':'XMLHttpRequest'}
        
    def get_data(self, url):
        html = requests.get(url, headers=self.headers).json()
        #得到的是字符串
        print(type(html))
        html_dict = json.loads(html)["data"]
        #html_dict = html["data"]

        #for循环遍历每条记录的数据
        data_list = []
        
        for data in html_dict:
            aqi = data['aqi']
            cityCode = data['cityCode']
            pm25 = data['pm25']
            #print(cityCode)
            
            data_list.append([aqi, cityCode, pm25])
        self.write_data(data_list)
    
    def write_data(self, data_list):
        with open('./test/my_DeeData.csv', 'a', newline = '') as f:
            writer = csv.writer(f)
            #writer.writerow(['aqi', 'cityCode', 'pm25'])
            writer.writerows(data_list)
    
    def main(self):
        for item in range(1, 5):
            url = self.url.format(item)
            self.get_data(url)
            
            
                 
if __name__ == '__main__':
    start = time.time()
    spider = DeeSpider()
    spider.main()
    end = time.time()
    print('执行时间:%.2f' % (end-start))       

控制台输出:

<class 'str'>
<class 'str'>
<class 'str'>
<class 'str'>
执行时间:1.15

爬取下来的部分数据(共37条):

85,340100,63
93,340100,69
94,340100,70
92,340100,68
88,340100,65
85,340100,60
85,340100,49
82,340100,43
84,340100,44
87,340100,41
87,340100,35
83,340100,31

很好!数据都爬取下来了。


但是注意! 这个数据网站的页面虽然标明了有47条数据,并共有5页,但实际上只有37条数据,第5页我是咋地都点不开:


设置JSON的URL地址中查询参数pageIndex为5,得到的响应是系统繁忙:

。。。嗯


这个案例写完啦,欢迎补充,纠错,指导。
备注:虽然看上去,我们花了那么多心思,而爬取的数据并不多,但重要的是理解+举一反三。

发布了219 篇原创文章 · 获赞 102 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/m0_37422217/article/details/105586821
今日推荐