1、网站分析
该网站搞了很多反爬技术,非常麻烦,最好是能通过Ajax请求获得数据,再进行解密,这样速度最快,但是技术要求较高。我这里还是用selenium中间件实现数据获取。后续再研究js解密。PM2.5历史数据_空气质量指数历史数据_中国空气质量在线监测分析平台历史数据
这个页面是个静态的页面,直接就能获取到所有的链接,然后我们找一个城市来进行详情数据的获取,比如朝阳,这个地方可以使用切片。
url_list = response.xpath('//div[@class="bottom"]/ul/div[2]/li/a/@href').extract()
print(url_list)
#遍历列表
for url in url_list[45:46]:
city_url = response.urljoin(url)#发起对城市详情页面的请求
#print(city_url)
yield scrapy.Request(city_url,callback=self.parse_month)
详情页先是所有月度空气质量数据,通过详情页再获取到某月所有日的空气质量数据,我们要获得所有日的数据,需要再次发数据请求。
(1)获得所有月的空气质量数据页面的链接
这个网站做了限制,没有办法使用使用F12进行调试
这个具体的原因就不详细介绍了,网站使用了无限 debugger,通过函数txsdefwsw()实现,如何能绕过这个方法太复杂,网上有教程,有兴趣的可以找下。
我通过view-source:https://www.aqistudy.cn/historydata/monthdata.php?city=%E6%9C%9D%E9%98%B3,就是view-source:可以查看到网页的源码,不影响做XPath解析。往后多拉一下就能看到。
url_list = response.xpath('//ul[@class="unstyled1"]/li/a/@href').extract()
#print(url_list)
#遍历url列表中的部分
for url in url_list[50:51]:
month_url =response.urljoin(url)#发起详情页面请求
(2)获取某个月的详情数据,需要再次请求,这时遇到一个问题就是打开的页面加载是动态的,比较慢,通过view-source看不到具体的数据,这只能通过selenium中间件技术来获取页面的源码,page_source,这个页面的源码通过print是可以看到数据的。因此中间件就是将请求截取,通过再次包装,提交请求。if 'daydata' in url:凡是页面链接URL中含有daydata的就通过selenium发出请求获取数据。
2、代码详情
middlewares.py
# Define here the models for your spider middleware
#
# See documentation in:
# https://docs.scrapy.org/en/latest/topics/spider-middleware.html
from scrapy import signals
from itemadapter import is_item, ItemAdapter
import time
from selenium import webdriver
from scrapy.http import HtmlResponse
class SelenimuMiddleWare(object):
def process_request(self,request,spider):
url =request.url
print(url)
if 'daydata' in url:
options = webdriver.ChromeOptions()
options.add_experimental_option('useAutomationExtension', False)
options.add_experimental_option('excludeSwitches', ['enable-automation'])
#options.add_argument('--headless')#无头浏览器
#options.add_argument('--disable-gpu')
driver = webdriver.Chrome(options=options)
driver.execute_cdp_cmd(
"Page.addScriptToEvaluateOnNewDocument",
{"source":"""
Object.defineProperty(navigator,'webdriver',{
get: () => undefined
})
"""})
driver.get(url)
time.sleep(5)
data = driver.page_source
print(data)
driver.close()
res = HtmlResponse(url=url,body = data,encoding='utf-8',request = request)
return res
爬虫文件aqi
import scrapy
from AQI.items import AqiItem
import time
class AqiSpider(scrapy.Spider):
name = "aqi"
allowed_domains = ["aqistudy.cn"]
host = "https://www.aqiStudy.cn/historydata/"
start_urls = [host]
#解析起始url对应的响应
def parse(self,response):
#获取城市url列表
url_list = response.xpath('//div[@class="bottom"]/ul/div[2]/li/a/@href').extract()
print(url_list)
#遍历列表
for url in url_list[45:46]:
city_url = response.urljoin(url)#发起对城市详情页面的请求
#print(city_url)
yield scrapy.Request(city_url,callback=self.parse_month)
#解析详情页面请求对应的响应
def parse_month(self, response):
#获取每月详情url列表
#print(response.text)
url_list = response.xpath('//ul[@class="unstyled1"]/li/a/@href').extract()
#print(url_list)
#遍历url列表中的部分
for url in url_list[50:51]:
month_url =response.urljoin(url)#发起详情页面请求
print(month_url)
yield scrapy.Request(month_url,callback=self.parse_day)
def parse_day(self, response):
#print(response.url)
#获取所有的数据节点
node_list = response.xpath('//tr')
node_list.pop(0)
city = response.xpath('//div[@class="panel-heading"]/h3/text()').extract_first().split('2')[0]
#遍历数据节点列表
item = AqiItem()
for node in node_list:
#创建存储数据的item容器item = AqiItem()
#先填写一些固定参数
item['city'] = city
item['url'] = response.url
item['timestamp']= time.time()
item['date'] = node.xpath('./td[1]/text()').extract_first()
item['AQI'] = node.xpath('./td[2]/text()').extract_first()
item['level'] = node.xpath('./td[5]/span/text()').extract_first()
# item['pm2_5'] = node.xpath('./td[4]/text()').extract_first()
# item['pm10'] = node.xpath('./td[5]/text()').extract_first()
# item['pm2_5'] = node.xpath('./td[4]/text()').extract_first()
# item['pm10'] = node.xpath('./td[5]/text()').extract_first()
# item['so2'] = node.xpath('./td[6]/text()').extract_first()
# item['co'] = node.xpath('./td[7]/text()').extract_first()
# item['no2'] = node.xpath('./td[8]/text()').extract_first()
# item['o3'] = node.xpath('./td[9]/text()').extract_first()
yield item
3、问题
拿到的渲染后的网页源代码,其中包含了3个 table,每个 Table 都有数据,但只有1个table 的数据是真实的,其他2个都是 js 代码造的假数据。就算拿到了这个真实的 table,单元格 td 里面也有伪造的假数据,css 设置了隐藏(hidden 或者 display none),需要把这些假数据剔除掉。
真实页面显示的数据
通过driver.page_source获得的源码,保存到html文件,用浏览器打开。
数据也对不上,应该是通过加密了。页面最下面有2个密文。
这个需要破解后才能看到是什么,有兴趣的小伙伴可以百度下。如果能拿到加密解密的js函数,可以通过import execjs这个模块,在python里面执行JS函数,获得解密后的数据,那么应该就不会出现数据混乱的问题了。如果系统的学习后,再交流,如有不正确的地方,欢迎批评指正。