嘘~ 正在从服务器偷取页面 . . .

行百里者半九十——scrapy 框架(6)


中间件的使用——爬取网易新闻

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("爬取结束!!!")

文章作者: New Ass
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 New Ass !
  目录