scrapy分页抓取网页(【干货】selenium下载中间件常用函数_response下载详解)
优采云 发布时间: 2022-03-19 15:19scrapy分页抓取网页(【干货】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、完整代码
也可以看看: