scrapy经过设置setting.py文件里的DOWNLOADER_MIDDLEWARES添加本身编写的下载中间件,一般将运用到的selenium相关内容写在这个下载中间件中,具体后面会有代码说明。php
selenium的基本使用参见:http://www.cnblogs.com/pythoner6833/p/9052300.htmlhtml
PROXIES = [ {'ip_port': '111.11.228.75:80', 'password': ''}, {'ip_port': '120.198.243.22:80', 'password': ''}, {'ip_port': '111.8.60.9:8123', 'password': ''}, {'ip_port': '101.71.27.120:80', 'password': ''}, {'ip_port': '122.96.59.104:80', 'password': ''}, {'ip_port': '122.224.249.122:8088', 'password':''}, ]
参考连接:node
分析:python
一共须要抓取三个页面,首先抓取第一个页面的全部城市名及对应的连接,地址:https://www.aqistudy.cn/historydata/git
而后抓取具体的,每一个城市,每月份的信息(就是年月),地址:https://www.aqistudy.cn/historydata/monthdata.php?city=%E5%AE%89%E5%BA%B7,这里只是其中一个城市github
最后抓取每月份中,每一天的数据,示例地址:https://www.aqistudy.cn/historydata/daydata.php?city=%E5%AE%89%E5%BA%B7&month=2015-01web
其中,第一个页面为静态页面,直接抓取上面的城市信息便可;第二个和第三页面时动态页面,采用selenium结合Phantomjs抓取(也能够用Google浏览器。)json
scrapy startproject ChinaAir
在items.py文件中定义须要抓取的字段,编写相关代码。浏览器
# -*- coding: utf-8 -*- # Define here the models for your scraped items # # See documentation in: # https://doc.scrapy.org/en/latest/topics/items.html import scrapy class ChinaairItem(scrapy.Item): # define the fields for your item here like: # name = scrapy.Field() """ 首先明确抓取目标,包括城市,日期,指标的值 """ # 城市 city = scrapy.Field() # 日期 date = scrapy.Field() # 空气质量指数 AQI = scrapy.Field() # 空气质量等级 level = scrapy.Field() # pm2.5的值 PM2_5 = scrapy.Field() # pm10 PM10 = scrapy.Field() # 二氧化硫 SO2 = scrapy.Field() # 一氧化碳 CO = scrapy.Field() # 二氧化氮 NO2 = scrapy.Field() # 臭氧浓度 O3_8h = scrapy.Field() # 数据源(数据来源) source = scrapy.Field() # 抓取时间 utc_time = scrapy.Field()
建立名为airChina的爬虫,并给定初始地址。cookie
scrapy genspider airChina https://www.aqistudy.cn/historydata/
来到爬虫文件,开始编写爬虫部分的代码。
# -*- coding: utf-8 -*- import scrapy from ChinaAir.items import ChinaairItem class AirchinaSpider(scrapy.Spider): name = 'airChina' allowed_domains = ['aqistudy.cn'] base_url = "https://www.aqistudy.cn/historydata/" # 抓取首页 start_urls = [base_url] def parse(self, response): # 拿到页面的全部城市名称连接 url_list = response.xpath('//div[@class="all"]/div[@class="bottom"]//a/@href').extract()# 拿到页面的全部城市名 city_list = response.xpath('//div[@class="all"]/div[@class="bottom"]//a/text()').extract()# 将城市名及其对应的连接,进行一一对应 for city, url in zip(city_list, url_list): # 拼接该城市的连接 link = self.base_url + url yield scrapy.Request(url=link, callback=self.parse_month, meta={"city": city})
def parse_month(self, response):
pass
在yield后,来到下载中间件文件,因为每个请求都要通过下载中间件,所以,从第一个页面中解析到的url,请求时,能够在下载中间件中进行必定的操做,如利用selenium进行请求。
来到middlerwares.py文件,删掉全部已写好的内容,从新编写咱们须要的内容。
# -*- coding: utf-8 -*- # Define here the models for your spider middleware # # See documentation in: # https://doc.scrapy.org/en/latest/topics/spider-middleware.html import random # 导入User-Agent列表 from ChinaAir.settings import USER_AGENT as ua_list # class UserAgentMiddlerware(object): # """ # 定义一个中间件,给每个请求随机选择USER_AGENT # 注意,不要忘了在setting文件中打开DOWNLOADER_MIDDLERWARE的注释 # """ # def process_request(self, request, spider): # # # 从ua_list中随机选择一个User-Agent # user_agent = random.choice(ua_list) # # 给请求添加头信息 # request.headers['User-Agent'] = user_agent # # 固然,也能够添加代理ip,方式以下,此处不用代理,仅说明代理使用方法 # # request.meta['proxy'] = "..." # print(request.headers['User-Agent']) import time import scrapy from selenium import webdriver class SeleniumMiddlerware(object): """ 利用selenium,获取动态页面数据 """ def process_request(self, request, spider): # 判断请求是否来自第二个页面,只在第二个页面调用浏览器 if not request.url == "https://www.aqistudy.cn/historydata/": # 实例化。selenium结合谷歌浏览器, self.driver = webdriver.PhantomJS() # 实在受不了每次测试都打开浏览器界面,因此换成无界面的了 # 请求 self.driver.get(request.url) time.sleep(2) # 获取请求后获得的源码 html = self.driver.page_source # 关闭浏览器 self.driver.quit() # 构造一个请求的结果,将谷歌浏览器访问获得的结果构形成response,并返回给引擎 response = scrapy.http.HtmlResponse(url=request.url, body=html, request=request, encoding='utf-8') return response
其中,注释部分为,下载中间件给每个请求分配一个随机的User-Agent及代理IP的方法,固然,此处用不上,所以,不用管。
因为每一次产生request请求,都要通过下载中间件,所以,写一个断定条件,只有是来自第二个页面的请求时,才采用selenium来执行。
代码的最后一行,下载中间件将selenium请求后的结果,再构形成一个response,返回给引擎,继续后续处理。注意,要在settings.py文件中将下载中间件的注释打开。
拿到第二页返回的response时,继续来到爬虫文件,对response进行解析和提取第三页中须要的url,代码以下:
class AirchinaSpider(scrapy.Spider): name = 'airChina' allowed_domains = ['aqistudy.cn'] base_url = "https://www.aqistudy.cn/historydata/" # 抓取首页 start_urls = [base_url] def parse(self, response): # 拿到页面的全部城市名称连接 url_list = response.xpath('//div[@class="all"]/div[@class="bottom"]//a/@href').extract()[:1] # 拿到页面的全部城市名 city_list = response.xpath('//div[@class="all"]/div[@class="bottom"]//a/text()').extract()[:1] # 将城市名及其对应的连接,进行一一对应 for city, url in zip(city_list, url_list): # 拼接该城市的连接 link = self.base_url + url yield scrapy.Request(url=link, callback=self.parse_month, meta={"city": city}) def parse_month(self, response): """ 拿到每一个城市的,每月份的数据 此页面为动态页面,这里利用selenium结合浏览器获取动态数据 所以在下载中间件中添加中间件代码 :param response: :return: """ # 获取城市每月份的连接 url_list = response.xpath('//tr/td/a/@href').extract()[:1] for url in url_list: url = self.base_url + url # 构造该url yield scrapy.Request(url=url, meta={'city': response.meta['city']}, callback=self.parse_day)
拿到第二页的数据后,解析出第三页请求的url后,回调,并提取出须要抓取的数据,就完成了爬虫部分的代码。所以,整个爬虫文件的代码以下:
# -*- coding: utf-8 -*- import scrapy from ChinaAir.items import ChinaairItem class AirchinaSpider(scrapy.Spider): name = 'airChina' allowed_domains = ['aqistudy.cn'] base_url = "https://www.aqistudy.cn/historydata/" # 抓取首页 start_urls = [base_url] def parse(self, response): # 拿到页面的全部城市名称连接 url_list = response.xpath('//div[@class="all"]/div[@class="bottom"]//a/@href').extract()[:1] # 拿到页面的全部城市名 city_list = response.xpath('//div[@class="all"]/div[@class="bottom"]//a/text()').extract()[:1] # 将城市名及其对应的连接,进行一一对应 for city, url in zip(city_list, url_list): # 拼接该城市的连接 link = self.base_url + url yield scrapy.Request(url=link, callback=self.parse_month, meta={"city": city}) def parse_month(self, response): """ 拿到每一个城市的,每月份的数据 此页面为动态页面,这里利用selenium结合浏览器获取动态数据 所以在下载中间件中添加中间件代码 :param response: :return: """ # 获取城市每月份的连接 url_list = response.xpath('//tr/td/a/@href').extract()[:1] for url in url_list: url = self.base_url + url # 构造该url yield scrapy.Request(url=url, meta={'city': response.meta['city']}, callback=self.parse_day) def parse_day(self, response): """ 获取每一天的数据 :param response: :return: """ node_list = response.xpath('//tr') node_list.pop(0) for node in node_list: # 解析目标数据 item = ChinaairItem() item['city'] = response.meta['city'] item['date'] = node.xpath('./td[1]/text()').extract_first() item['AQI'] = node.xpath('./td[2]/text()').extract_first() item['level'] = node.xpath('./td[3]/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_8h'] = node.xpath('./td[9]/text()').extract_first() yield item
抓取到数据后,就能够开始写保存数据的逻辑了,这里仅仅将数据写成json格式的数据。
# -*- coding: utf-8 -*- # Define your item pipelines here # # Don't forget to add your pipeline to the ITEM_PIPELINES setting # See: https://doc.scrapy.org/en/latest/topics/item-pipeline.html import json from datetime import datetime class ChinaAirPipeline(object): def process_item(self, item, spider): item["source"] = spider.name item['utc_time'] = str(datetime.utcnow()) return item class ChinaAirJsonPipeline(object): def open_spider(self, spider): self.file = open('air.json', 'w', encoding='utf-8') def process_item(self, item, spider): content = json.dumps(dict(item), ensure_ascii=False) + '\n' self.file.write(content) def close_spider(self, spider): self.file.close()
ChinaAirPipeline是在接收到管道丢过来的item后,继续添加两个自读,抓取时间和数据的来源,并在添加后,继续经过管道丢给下面的ChinaAirJsonPipelines文件,进行保存。
其中,不要忘了在settings.py文件中注册管道信息。
scrapy crawl airChina
参见:https://github.com/zInPython/ChinaAir/tree/master/ChinaAir