1、所需知识补充

1.下载中间件常用函数

  • process_request(self, request, spider):

    • 当每个request通过下载中间件是,该方法被调用
    • process_request()函数必须返回一下其中之一:一个None,一个Response对象,一个Request对象或raise IgnoreRequest。
      如果返回None,Scrapy将继续处理该request,执行其他的中间件中相应的方法,直达合适的下载器处理函数(download handler)被调用,该request被执行(其response被下载);
      如果返回的是Response对象,scrapy将不会调用任何其他的process_request()或process_exception()方法,或相应的下载函数,其将返回该response,已安装的中间件的process_response()方法则会在每个response返回时被调用;
      如果其返回Request对象,scrapy则停止调用process_request()方法并重新调度返回的request。当心返回的request被执行后,相应的中间件链将会更具下载的response被调用。
      如果其raise一个IgnoreRequest异常,则安装的下载中间件的process_exception()方法会被调用。如果没有任何一个方法处理该异常,则request的errback(Request.errback)方法会被调用,如果没有代码吹抛出的异常,则该异常被忽略且不记录(不同于其他异常那样)
    • 参数:
      request(Request对象)--处理的request
      spider(Spider对象)--该request对应的spider
  • process_response(self, request, spider):
    • 当下载器完成http请求,传递响应给引擎的时候调用
    • process_response()必须返回以下其中之一:返回一个Request对象或raise一个IgnorRequest异常
      如果其返回一个Response(可以与传入的response相同,也可以是全新的对象),该response会被在链中其他中间件的process_response()方法处理。
      如果其返回一个Request对象,则中间件链停止,返回的request会被重新调度下载,处理类似于process_request()返回request所做的那样。
      如果其抛出一个IgnorRequest异常,则调用request的errback(Request.errback)。如果没有代码处理抛出的异常,则该异常被忽略且不记录。
    • 参数:
      request(Request对象)--response所对应的request
      response(Response对象)--被处理的response对象
      spider(Spider对象)--response所对应的spider

2.scrapy对接selenium

scrapy通过设置setting.py文件里的DOWNLOADER_MIDDLEWARES添加自己编写的下载中间件,通常将运用到的selenium相关内容写在这个下载中间件中,具体后面会有代码说明。

selenium的基本使用参见:http://www.cnblogs.com/pythoner6833/p/9052300.html

3.常用settings的内置设置

  • BOT_NAME
    默认:“scrapybot”,使用startproject命令创建项目时,其被自动赋值
  • CONCURRENT_ITEMS
    默认为100,Item Process(即Item Pipeline)同时处理(每个response的)item时最大值
  • CONCURRENT_REQUEST
    默认为16,scrapy downloader并发请求(concurrent requests)的最大值
  • LOG_ENABLED
    默认为True,是否启用logging
  • DEFAULT_REQUEST_HEADERS
    默认如下:{'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
    'Accept-Language': 'en',}
    scrapy http request使用的默认header
  • LOG_ENCODING
    默认utt-8,logging中使用的编码
  • LOG_LEVEL
    默认“DEBUG”,log中最低级别,可选级别有:CRITICAL,ERROR,WARNING,DEBUG
  • USER_AGENT
    默认:“Scrapy/VERSION(....)”,爬取的默认User-Agent,除非被覆盖
  • COOKIES_ENABLED=False,禁用cookies
  • PEOXIE:代理设置
    例如:

    1. PROXIES = [
    2. {'ip_port': '111.11.228.75:80', 'password': ''},
    3. {'ip_port': '120.198.243.22:80', 'password': ''},
    4. {'ip_port': '111.8.60.9:8123', 'password': ''},
    5. {'ip_port': '101.71.27.120:80', 'password': ''},
    6. {'ip_port': '122.96.59.104:80', 'password': ''},
    7. {'ip_port': '122.224.249.122:8088', 'password':''},
    8. ]

参考链接:

2、案例分析

分析:

一共需要抓取三个页面,首先抓取第一个页面的所有城市名及对应的链接,地址:https://www.aqistudy.cn/historydata/

然后抓取具体的,每个城市,每个月份的信息(就是年月),地址:https://www.aqistudy.cn/historydata/monthdata.php?city=%E5%AE%89%E5%BA%B7,这里只是其中一个城市

最后抓取每个月份中,每一天的数据,示例地址:https://www.aqistudy.cn/historydata/daydata.php?city=%E5%AE%89%E5%BA%B7&month=2015-01

其中,第一个页面为静态页面,直接抓取上面的城市信息即可;第二个和第三页面时动态页面,采用selenium结合Phantomjs抓取(也可以用Google浏览器。)

1. 创建一个项目

  1. scrapy startproject ChinaAir

2.明确需要抓取的字段

在items.py文件中定义需要抓取的字段,编写相关代码。

  1. # -*- coding: utf-8 -*-
  2.  
  3. # Define here the models for your scraped items
  4. #
  5. # See documentation in:
  6. # https://doc.scrapy.org/en/latest/topics/items.html
  7.  
  8. import scrapy
  9.  
  10. class ChinaairItem(scrapy.Item):
  11. # define the fields for your item here like:
  12. # name = scrapy.Field()
  13. """
  14. 首先明确抓取目标,包括城市,日期,指标的值
  15. """
  16. # 城市
  17. city = scrapy.Field()
  18. # 日期
  19. date = scrapy.Field()
  20. # 空气质量指数
  21. AQI = scrapy.Field()
  22. # 空气质量等级
  23. level = scrapy.Field()
  24. # pm2.5的值
  25. PM2_5 = scrapy.Field()
  26. # pm10
  27. PM10 = scrapy.Field()
  28. # 二氧化硫
  29. SO2 = scrapy.Field()
  30. # 一氧化碳
  31. CO = scrapy.Field()
  32. # 二氧化氮
  33. NO2 = scrapy.Field()
  34. # 臭氧浓度
  35. O3_8h = scrapy.Field()
  36.  
  37. # 数据源(数据来源)
  38. source = scrapy.Field()
  39. # 抓取时间
  40. utc_time = scrapy.Field()

3.生成爬虫文件

创建名为airChina的爬虫,并给定初始地址。

  1. scrapy genspider airChina https://www.aqistudy.cn/historydata/

来到爬虫文件,开始编写爬虫部分的代码。

4.编写爬虫

  1. # -*- coding: utf-8 -*-
  2. import scrapy
  3. from ChinaAir.items import ChinaairItem
  4.  
  5. class AirchinaSpider(scrapy.Spider):
  6. name = 'airChina'
  7. allowed_domains = ['aqistudy.cn']
  8. base_url = "https://www.aqistudy.cn/historydata/"
  9. # 抓取首页
  10. start_urls = [base_url]
  11.  
  12. def parse(self, response):
  13.  
  14. # 拿到页面的所有城市名称链接
  15. url_list = response.xpath('//div[@class="all"]/div[@class="bottom"]//a/@href').extract()# 拿到页面的所有城市名
  16. city_list = response.xpath('//div[@class="all"]/div[@class="bottom"]//a/text()').extract()# 将城市名及其对应的链接,进行一一对应
  17. for city, url in zip(city_list, url_list):
  18.  
  19. # 拼接该城市的链接
  20. link = self.base_url + url
  21. yield scrapy.Request(url=link, callback=self.parse_month, meta={"city": city})
  1.   def parse_month(self, response):
        pass

在yield后,来到下载中间件文件,由于每一个请求都要经过下载中间件,因此,从第一个页面中解析到的url,请求时,可以在下载中间件中进行一定的操作,如利用selenium进行请求。

来到middlerwares.py文件,删掉所有已写好的内容,重新编写我们需要的内容。

  1. # -*- coding: utf-8 -*-
  2.  
  3. # Define here the models for your spider middleware
  4. #
  5. # See documentation in:
  6. # https://doc.scrapy.org/en/latest/topics/spider-middleware.html
  7.  
  8. import random
  9. # 导入User-Agent列表
  10. from ChinaAir.settings import USER_AGENT as ua_list
  11.  
  12. # class UserAgentMiddlerware(object):
  13. # """
  14. # 定义一个中间件,给每一个请求随机选择USER_AGENT
  15. # 注意,不要忘了在setting文件中打开DOWNLOADER_MIDDLERWARE的注释
  16. # """
  17. # def process_request(self, request, spider):
  18. #
  19. # # 从ua_list中随机选择一个User-Agent
  20. # user_agent = random.choice(ua_list)
  21. # # 给请求添加头信息
  22. # request.headers['User-Agent'] = user_agent
  23. # # 当然,也可以添加代理ip,方式如下,此处不用代理,仅说明代理使用方法
  24. # # request.meta['proxy'] = "..."
  25. # print(request.headers['User-Agent'])
  26.  
  27. import time
  28. import scrapy
  29. from selenium import webdriver
  30.  
  31. class SeleniumMiddlerware(object):
  32. """
  33. 利用selenium,获取动态页面数据
  34. """
  35. def process_request(self, request, spider):
  36.  
  37. # 判断请求是否来自第二个页面,只在第二个页面调用浏览器
  38. if not request.url == "https://www.aqistudy.cn/historydata/":
  39. # 实例化。selenium结合谷歌浏览器,
  40. self.driver = webdriver.PhantomJS() # 实在受不了每次测试都打开浏览器界面,所以换成无界面的了
  41. # 请求
  42. self.driver.get(request.url)
  43. time.sleep(2)
  44.  
  45. # 获取请求后得到的源码
  46. html = self.driver.page_source
  47. # 关闭浏览器
  48. self.driver.quit()
  49.  
  50. # 构造一个请求的结果,将谷歌浏览器访问得到的结果构造成response,并返回给引擎
  51. response = scrapy.http.HtmlResponse(url=request.url, body=html, request=request, encoding='utf-8')
  52. return response

其中,注释部分为,下载中间件给每一个请求分配一个随机的User-Agent及代理IP的方法,当然,此处用不上,因此,不用管。

由于每一次产生request请求,都要经过下载中间件,因此,写一个判定条件,只有是来自第二个页面的请求时,才采用selenium来执行。

代码的最后一行,下载中间件将selenium请求后的结果,再构造成一个response,返回给引擎,继续后续处理。注意,要在settings.py文件中将下载中间件的注释打开。

拿到第二页返回的response时,继续来到爬虫文件,对response进行解析和提取第三页中需要的url,代码如下:

  1. class AirchinaSpider(scrapy.Spider):
  2. name = 'airChina'
  3. allowed_domains = ['aqistudy.cn']
  4. base_url = "https://www.aqistudy.cn/historydata/"
  5. # 抓取首页
  6. start_urls = [base_url]
  7.  
  8. def parse(self, response):
  9.  
  10. # 拿到页面的所有城市名称链接
  11. url_list = response.xpath('//div[@class="all"]/div[@class="bottom"]//a/@href').extract()[:1]
  12. # 拿到页面的所有城市名
  13. city_list = response.xpath('//div[@class="all"]/div[@class="bottom"]//a/text()').extract()[:1]
  14.  
  15. # 将城市名及其对应的链接,进行一一对应
  16. for city, url in zip(city_list, url_list):
  17.  
  18. # 拼接该城市的链接
  19. link = self.base_url + url
  20. yield scrapy.Request(url=link, callback=self.parse_month, meta={"city": city})
  21.  
  22. def parse_month(self, response):
  23. """
  24. 拿到每个城市的,每个月份的数据
  25. 此页面为动态页面,这里利用selenium结合浏览器获取动态数据
  26. 因此在下载中间件中添加中间件代码
  27. :param response:
  28. :return:
  29. """
  30. # 获取城市每个月份的链接
  31. url_list = response.xpath('//tr/td/a/@href').extract()[:1]
  32.  
  33. for url in url_list:
  34. url = self.base_url + url # 构造该url
  35. yield scrapy.Request(url=url, meta={'city': response.meta['city']}, callback=self.parse_day)

拿到第二页的数据后,解析出第三页请求的url后,回调,并提取出需要抓取的数据,就完成了爬虫部分的代码。因此,整个爬虫文件的代码如下:

  1. # -*- coding: utf-8 -*-
  2. import scrapy
  3. from ChinaAir.items import ChinaairItem
  4.  
  5. class AirchinaSpider(scrapy.Spider):
  6. name = 'airChina'
  7. allowed_domains = ['aqistudy.cn']
  8. base_url = "https://www.aqistudy.cn/historydata/"
  9. # 抓取首页
  10. start_urls = [base_url]
  11.  
  12. def parse(self, response):
  13.  
  14. # 拿到页面的所有城市名称链接
  15. url_list = response.xpath('//div[@class="all"]/div[@class="bottom"]//a/@href').extract()[:1]
  16. # 拿到页面的所有城市名
  17. city_list = response.xpath('//div[@class="all"]/div[@class="bottom"]//a/text()').extract()[:1]
  18.  
  19. # 将城市名及其对应的链接,进行一一对应
  20. for city, url in zip(city_list, url_list):
  21.  
  22. # 拼接该城市的链接
  23. link = self.base_url + url
  24. yield scrapy.Request(url=link, callback=self.parse_month, meta={"city": city})
  25.  
  26. def parse_month(self, response):
  27. """
  28. 拿到每个城市的,每个月份的数据
  29. 此页面为动态页面,这里利用selenium结合浏览器获取动态数据
  30. 因此在下载中间件中添加中间件代码
  31. :param response:
  32. :return:
  33. """
  34. # 获取城市每个月份的链接
  35. url_list = response.xpath('//tr/td/a/@href').extract()[:1]
  36.  
  37. for url in url_list:
  38. url = self.base_url + url # 构造该url
  39. yield scrapy.Request(url=url, meta={'city': response.meta['city']}, callback=self.parse_day)
  40.  
  41. def parse_day(self, response):
  42. """
  43. 获取每一天的数据
  44. :param response:
  45. :return:
  46. """
  47. node_list = response.xpath('//tr')
  48.  
  49. node_list.pop(0)
  50. for node in node_list:
  51. # 解析目标数据
  52. item = ChinaairItem()
  53. item['city'] = response.meta['city']
  54. item['date'] = node.xpath('./td[1]/text()').extract_first()
  55. item['AQI'] = node.xpath('./td[2]/text()').extract_first()
  56. item['level'] = node.xpath('./td[3]/text()').extract_first()
  57. item['PM2_5'] = node.xpath('./td[4]/text()').extract_first()
  58. item['PM10'] = node.xpath('./td[5]/text()').extract_first()
  59. item['SO2'] = node.xpath('./td[6]/text()').extract_first()
  60. item['CO'] = node.xpath('./td[7]/text()').extract_first()
  61. item['NO2'] = node.xpath('./td[8]/text()').extract_first()
  62. item['O3_8h'] = node.xpath('./td[9]/text()').extract_first()
  63. yield item

5.编写pipelines文件

抓取到数据后,就可以开始写保存数据的逻辑了,这里仅仅将数据写成json格式的数据。

  1. # -*- coding: utf-8 -*-
  2.  
  3. # Define your item pipelines here
  4. #
  5. # Don't forget to add your pipeline to the ITEM_PIPELINES setting
  6. # See: https://doc.scrapy.org/en/latest/topics/item-pipeline.html
  7. import json
  8. from datetime import datetime
  9.  
  10. class ChinaAirPipeline(object):
  11. def process_item(self, item, spider):
  12. item["source"] = spider.name
  13. item['utc_time'] = str(datetime.utcnow())
  14. return item
  15.  
  16. class ChinaAirJsonPipeline(object):
  17. def open_spider(self, spider):
  18. self.file = open('air.json', 'w', encoding='utf-8')
  19.  
  20. def process_item(self, item, spider):
  21. content = json.dumps(dict(item), ensure_ascii=False) + '\n'
  22. self.file.write(content)
  23.  
  24. def close_spider(self, spider):
  25. self.file.close()

ChinaAirPipeline是在接收到管道丢过来的item后,继续添加两个自读,抓取时间和数据的来源,并在添加后,继续通过管道丢给下面的ChinaAirJsonPipelines文件,进行保存。

其中,不要忘了在settings.py文件中注册管道信息。

6.运行爬虫,抓取数据

  1. scrapy crawl airChina

3、完整代码

参见:https://github.com/zInPython/ChinaAir/tree/master/ChinaAir

scrapy下载中间件结合selenium抓取全国空气质量检测数据的更多相关文章

  1. 使用google chrome抓取数据:抓取全国的高中的数据

    http://tomycat.github.io/blog/other/2014/05/28/use-google-chrome-capture-data.html

  2. scrapy结合selenium抓取武汉市环保局空气质量日报

    1.前言 目标网站:武汉市环境保护局(http://hbj.wuhan.gov.cn/viewAirDarlyForestWaterInfo.jspx).scrapy对接selenium模块抓取空气质 ...

  3. Python爬虫实战八之利用Selenium抓取淘宝匿名旺旺

    更新 其实本文的初衷是为了获取淘宝的非匿名旺旺,在淘宝详情页的最下方有相关评论,含有非匿名旺旺号,快一年了淘宝都没有修复这个. 可就在今天,淘宝把所有的账号设置成了匿名显示,SO,获取非匿名旺旺号已经 ...

  4. selenium抓取动态网页数据

    1.selenium抓取动态网页数据基础介绍 1.1 什么是AJAX AJAX(Asynchronouse JavaScript And XML:异步JavaScript和XML)通过在后台与服务器进 ...

  5. selenium抓取视频

    今天闲着没事,用selenium抓取视频保存到本地,只爬取了第一页,只要小于等于5分钟的视频... 为什么不用requests,没有为什么,就因为有些网站正则和xpath都提取不出来想要的东西,要么就 ...

  6. Scrapy下载中间件的优先级(神踏马值越小优先级越高)

    自从之前看的一篇讲Scrapy下载中间件的文章后,一直认为设置里下载中间件的优先级数值越小,越优先,最近要抓的网站反爬增强了,所以需要使用代理ip,但是由于使用的是免费代理以至于经常失效,需要对失效的 ...

  7. Hawk: 20分钟无编程抓取大众点评17万数据

    1. 主角出场:Hawk介绍 Hawk是沙漠之鹰开发的一款数据抓取和清洗工具,目前已经在Github开源.详细介绍可参考:http://www.cnblogs.com/buptzym/p/545419 ...

  8. SQL Server定时自动抓取耗时SQL并归档数据发邮件脚本分享

    SQL Server定时自动抓取耗时SQL并归档数据发邮件脚本分享 第一步建库和建表 USE [master] GO CREATE DATABASE [MonitorElapsedHighSQL] G ...

  9. SQL Server定时自动抓取耗时SQL并归档数据脚本分享

    原文:SQL Server定时自动抓取耗时SQL并归档数据脚本分享 SQL Server定时自动抓取耗时SQL并归档数据脚本分享 第一步建库 USE [master] GO CREATE DATABA ...

随机推荐

  1. 针对工程实践项目的用例建模Use Case Modeling

    一.什么是用例建模(Use Case Modeling) 1.用例(Use Case) (1)概念:用例是软件工程或系统工程中对系统如何反应外界请求的描述,是一种通过用户的使用场景来获取需求的技术. ...

  2. Life is short, I love Python~!

    python学习目录 一:计算机基础 计算机基础 二:python基础 python基础 三:函数 函数 内置函数匿名函数 迭代器生成器 四:文件处理&异常 文件处理 异常处理 五:模块 常用 ...

  3. 前端技术之:JavaScript测试工具

    Mocha 一个用于Node.js与浏览器端的简单.自由.有趣的JavaScript测试框架. https://mochajs.org/ https://github.com/mochajs/moch ...

  4. MIT线性代数:5.转置,置换,向量空间

  5. MVC5异步提交表单疑难杂症

    //此处必须添加,不然不能执行异步回调OnAddPortSuccess方法 <script src="~/scripts/jquery.unobtrusive-ajax.min.js& ...

  6. [2018-01-13] 安装Django的一些笔记

    安装django pip install Django = =1.10.2 下载源码,进入根目录执行 python setup.py install 确认是否已经安装成功 python -m djan ...

  7. [tesseract-ocr]OCR图像识别Ubuntu下环境包安装

    问题: ImportError: libSM.so.6: cannot open shared object file: No such file or directory 解决: sudo apt- ...

  8. 操作系统实现(一):从Bootloader到ELF内核(转载)

    原文链接: http://www.cppblog.com/airtrack/archive/2014/10/30/208729.html Bootloader 我们知道计算机启动是从BIOS开始,再由 ...

  9. 学习 Java 应该关注哪些网站?

    经常有一些读者问我:"二哥,学习 Java 应该关注哪些网站?",我之前的态度一直是上知乎.上搜索引擎搜一下不就知道了.但读者对我这个态度很不满意,他们说,"我在问你,又 ...

  10. Python OpenCV4趣味应用系列(四)---颜色物体实时检测

    今天,我们来实现一个视频实时检测颜色物体的小实例,视频中主要有三个颜色物体,我们只检测红色和绿色的球状物体,如下图所示: 第一步需要打开视频(或者摄像头): cap = cv2.VideoCaptur ...