scrapy分页抓取网页(【干货】selenium下载中间件常用函数_response下载详解)

优采云 发布时间: 2022-03-19 15:19

  scrapy分页抓取网页(【干货】selenium下载中间件常用函数_response下载详解)

  1、需补充知识1.下载中间件常用函数process_response(self, request, spider):2.scrapy与selenium对接

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

  关于 selenium 的基本使用,请参见:

  3.常用设置的内置设置

  参考链接:

  2、案例研究

  分析:

  一共需要抓取三页。首先,抓取第一页上的所有城市名称和相应的链接。地址是:

  然后抓取每个城市和每个月的具体信息(即年月),地址:%E5%AE%89%E5%BA%B7,这里只是其中一个城市

  最后,抓取每个月的每一天的数据,示例地址:%E5%AE%89%E5%BA%B7&month=2015-01

  其中,首页为静态页面,可直接抓取上面的城市信息;第二页和第三页是动态页面,是selenium结合Phantomjs爬取的(也可以用谷歌浏览器)

  1. 创建一个项目

  scrapy startproject ChinaAir

  2.指定需要抓取的字段

  在 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()

  3.生成爬虫文件

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

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

  进入爬虫文件,开始编写爬虫部分的代码。

  4.写一个爬虫

  # -*- 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。当然,这里没有用到,所以不用担心。

  由于每次生成请求请求,都必须经过下载中间件。因此,在编写判断条件时,使用 selenium 只从第二页开始执行请求。

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

  

  得到第二页返回的响应后,继续爬虫文件,解析响应并提取第三页需要的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

  5.写管道文件

  抓取数据后,就可以开始编写保存数据的逻辑了。在这里,只有数据以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接收到pipeline抛出的item后,继续添加两个自读,捕获时间和数据源,添加完后,继续通过pipeline抛到下面的ChinaAirJsonPipelines文件中保存。

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

  6.运行爬虫,抓取数据

  scrapy crawl airChina

  3、完整代码

  也可以看看:

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线