中间件的使用——爬取网易新闻
scrapy 五大核心组件
引擎(Scrapy)
用来处理整个系统的数据流量,触发事务(框架核心)
调度器(Scheduler)
用来接受引擎发送的请求,压入队列中,并在引擎再次请求的时候返回,可以想象成一个 URL (抓取网页的网址或者说是链接)的有限队列,由它来决定下一个要抓取的网址是什么,同时去除重复的网址。
下载器(Downloader)
用于下载网页内容,并将网页内容返回给爬虫(Spiders),下载器是建立在 twisted 这个高效的异步模型上的。
爬虫(Spider)
爬虫是主要干活的,用于从特定的网页中提取自己需要的信息,即所谓的实体(item),用户也可以从中提取出链接,让 引擎(Scrapy) 继续抓取下一个页面。
项目管道(Pipeline)
负责处理爬虫从网页中抽取的实体,主要的功能是持久化实体,验证实体的有效性,清除不需要的信息。当页面被爬虫解析后,将被发送到项目管道,并经过几个特定的次序处理数据。
中间件(Middlewares)的介绍
下载中间件(Downloader Middlewares)
位置:引擎和下载器之间
作用:批量拦截到整个工程中所有的请求和响应
拦截请求:
1. UA伪装
2. 代理IP
拦截响应:
1. 篡改响应数据,响应对象
需求
爬取网易新闻的新闻数据
思路分析
在开始写代码的时候,我们首先要到网易新闻网页中踩点,知己知彼,百战不殆。
网易首页稀疏平常,跟其他的网页差别不大,但我们点开随便一个模块,比如说 “国内”,在下拉网页的时候我们会发现这些网页都是动态加载的。
很显然,但目前为止,我们所学的 scrapy 框架解决不了这个问题。
对于动态加载的情况,我们首先想到的肯定是 selenium 爬取数据,那么在 scrapy 中我们要怎么和 selenium 结合呢?
没错,这就是中间件拦截响应的作用了。
至于中间件具体怎么使用,大家还是看代码吧。
代码实现
配置文件settings.py
管道和下载中间件的开启(注意是下载中间件:DOWNLOADER_MIDDLEWARES
)
主文件代码编写
import scrapy
from selenium import webdriver
from newsPro.items import NewsproItem
class NewsSpider(scrapy.Spider):
name = 'news'
# allowed_domains = ['www.xxx.com']
start_urls = ['https://news.163.com/']
all_urls = [] # 起判断作用,其中要发送请求的网址包含动态加载的内容,需要下载中间件拦截,并使用 selenium 替换响应对象
def __init__(self):
self.bro = webdriver.Chrome('C:\\Users\\ASUS\\Desktop\\CSDN\\selenium 模块\\chromedriver.exe')
def parse(self, response): # 获取国内、国际、军事新闻模块的网址
guonei_url = response.xpath('//li[@class="menu_guonei"]/a/@href').extract_first()
# print(guonei_url)
self.all_urls.append(guonei_url)
guoji_url = response.xpath('//li[@class="menu_guoji"]/a/@href').extract_first()
# print(guoji_url)
self.all_urls.append(guoji_url)
war_url = response.xpath('//li[@class="menu_war"]/a/@href').extract_first()
# print(war_url)
self.all_urls.append(war_url)
for url in self.all_urls:
yield scrapy.Request(url = url, callback = self.parse_news_url)
def parse_news_url(self, response): # 通过对国内、国际、军事新闻模块的网址发送请求(此时响应对象已经被下载中间件替换了)获取新闻的详情页的网址
new_urls = response.xpath('//div[@class="ndi_main"]/div/a/@href').extract()
for new_url in new_urls:
# print(new_url)
yield scrapy.Request(url = new_url, callback = self.parse_new)
def parse_new(self, response): # 获取新闻标题和内容
item = NewsproItem()
title = response.xpath('//div[@class="post_main"]/h1/text()').extract_first()
content = response.xpath('//div[@class="post_body"]//text()').extract()
content = "".join(content)
# print(title)
# print(content)
item["title"] = title
item["content"] = content
yield item
pass
实例化对象 items.py
# Define here the models for your scraped items
#
# See documentation in:
# https://docs.scrapy.org/en/latest/topics/items.html
import scrapy
class NewsproItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
title = scrapy.Field()
content = scrapy.Field()
pass
下载中间件 middlewares.py
下载中间件的类和函数方法很多,我们要注意选择是下载中间件和其中的 process_response
自定义函数
process_response
自定义函数的代码如下:
def process_response(self, request, response, spider):
bro = spider.bro
# spider 表示对应的爬虫对象
# 挑选出指定的响应对象进行篡改
# 通过 url 构造 request
if request.url in spider.all_urls:
bro.get(request.url)
page = bro.page_source
sleep(2)
# 针对定位到的 reponse 进行篡改
# 实例化一个新的响应对象(符合需求:包含动态加载的新闻数据),代替旧的响应对象
new_response = HtmlResponse(url = request.url, body = page, encoding = "utf-8", request = request)
return new_response
else:
return response
管道 pipelines.py
# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html
# useful for handling different item types with a single interface
from itemadapter import ItemAdapter
class NewsproPipeline:
fp = None
def open_spider(self, spider):
self.fp = open('网易新闻.txt', "a", encoding = "utf-8")
print("爬虫开始……")
def process_item(self, item, spider):
title = item["title"]
content = item["content"]
if title is not None:
self.fp.write(title + "\n" + content + "\n\n")
print(title, "爬取成功!")
return item
def close_spider(self, spider):
self.fp.close()
print("爬取结束!!!")